> ## Documentation Index
> Fetch the complete documentation index at: https://help.privy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Get a flow

> Retrieve a single flow by its identifier, including its full
configuration: re-entry behavior, flow filters, effective exit
behavior, a server-generated behavior summary, and the node tree —
each step's configuration (`data`) plus the branch graph, with split
conditions and the nodes on each branch.

`exit_behavior` and `reentry` state the engine's effective semantics
directly: exits end only journeys already in progress (the enrolling
event never triggers them), and `reentry.mode` says explicitly
whether and when a contact can go through again. Trust these fields
and `behavior_summary` over inferences from the node structure.

**Required scope:** `flows_read`




## OpenAPI

````yaml openapi/privy-api.yaml GET /flows/{id}
openapi: 3.1.0
info:
  title: Privy API
  version: '1.0'
  description: >
    The Privy API lets you programmatically manage your contact list and read
    what you have set up in Privy.

    Create, update, unsubscribe, and remove contacts; report orders;

    and read your campaigns, flows, segments, orders, store products, coupon
    pools,

    performance reports, and account setup.
servers:
  - url: https://api.privy.com/v1
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Account
    description: Retrieve information about the authenticated Privy account.
  - name: Contacts
    description: Manage your contact list.
  - name: Imports
    description: >-
      Read your contact import history — CSV uploads, connected-provider syncs,
      and SMS consent imports.
  - name: Campaigns
    description: >-
      Read your message campaigns — email and SMS newsletters, plus the messages
      your flows send.
  - name: Displays
    description: >-
      Read your on-site displays (popups, flyouts, banners, embedded forms,
      landing pages).
  - name: Coupons
    description: Read your coupon pools, their code inventory, and individual codes.
  - name: Reports
    description: >-
      Read performance reporting — signups, views, conversion rates, and
      attributed revenue — for your business, displays, and flows.
  - name: Flows
    description: Read your automation flows and their configuration.
  - name: Segments
    description: Read your customer segments and their membership.
  - name: Orders
    description: Report orders placed in your store and read your order history.
  - name: Products
    description: Read the product catalog and collections of your connected store.
  - name: Events
    description: Ingest custom events to trigger Flows and other automations.
paths:
  /flows/{id}:
    get:
      tags:
        - Flows
      summary: Get a flow
      description: |
        Retrieve a single flow by its identifier, including its full
        configuration: re-entry behavior, flow filters, effective exit
        behavior, a server-generated behavior summary, and the node tree —
        each step's configuration (`data`) plus the branch graph, with split
        conditions and the nodes on each branch.

        `exit_behavior` and `reentry` state the engine's effective semantics
        directly: exits end only journeys already in progress (the enrolling
        event never triggers them), and `reentry.mode` says explicitly
        whether and when a contact can go through again. Trust these fields
        and `behavior_summary` over inferences from the node structure.

        **Required scope:** `flows_read`
      operationId: getFlow
      parameters:
        - $ref: '#/components/parameters/AccountIdParam'
        - name: id
          in: path
          required: true
          description: >-
            The flow's unique identifier (returned as `id` in flow responses).
            Ids are opaque public strings — a numeric id from a Privy dashboard
            URL is not a valid API id and will 404; list flows and match by
            title instead.
          schema:
            type: string
          example: Vq7L3mN9xR
      responses:
        '200':
          description: The flow.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/FlowDetail'
              example:
                data:
                  id: Vq7L3mN9xR
                  title: Abandoned Cart Recovery
                  status: active
                  trigger_type: cart_abandoned
                  trigger_label: Cart abandoned
                  reentry:
                    mode: after_delay
                    frequency: 30
                    unit: days
                  flow_filters:
                    operation: AND
                    conditions:
                      - property: channel
                        operator: equals
                        value: email
                      - property: segment
                        operator: contains
                        value:
                          - Nk2P5rW8yU
                  exit_behavior:
                    exit_events:
                      - order_placed
                      - order_delivered
                    applies_to: active_journeys_only
                    enrollment_event_excluded: true
                    summary: >-
                      After enrollment, a subsequent order placed or order
                      delivered event ends the journey early. The event that
                      enrolled the contact never triggers this exit.
                  behavior_summary: >-
                    A contact enrolls when the "Cart abandoned" event occurs.
                    The flow has 2 message steps. A subsequent order placed or
                    order delivered event ends an active journey early; the
                    event that enrolled the contact never triggers this exit. A
                    contact can re-enter 30 days after their previous journey
                    ends; no setting ever runs two journeys at once.
                  nodes:
                    - uuid: 6f1e8d0a-4b2c-4c1d-9a3e-2f5b7c8d9e0f
                      type: delay
                      name: Wait 1 hour
                      parent_uuid: null
                      next_uuid: 8b3c0f2d-6d4e-4e3f-9c5a-4b7d9e0f1a2b
                      data:
                        delay_unit: hours
                        delay_unit_count: 1
                    - uuid: 8b3c0f2d-6d4e-4e3f-9c5a-4b7d9e0f1a2b
                      type: split
                      name: High-value cart?
                      parent_uuid: 6f1e8d0a-4b2c-4c1d-9a3e-2f5b7c8d9e0f
                      yes_next_uuid: 7a2f9e1b-5c3d-4d2e-8b4f-3a6c8d9e0f1a
                      no_next_uuid: 9c4d1a3e-7e5f-4f4a-8d6b-5c8e0f1a2b3c
                      data:
                        conditions:
                          operation: AND
                          conditions:
                            - property: abandoned_cart_value
                              operator: greater_than
                              value: 100
                    - uuid: 7a2f9e1b-5c3d-4d2e-8b4f-3a6c8d9e0f1a
                      type: email
                      name: VIP cart reminder
                      parent_uuid: 8b3c0f2d-6d4e-4e3f-9c5a-4b7d9e0f1a2b
                      next_uuid: null
                      data:
                        campaign_id: Xk4M7pQ2wT
                        subject: You left something behind!
                    - uuid: 9c4d1a3e-7e5f-4f4a-8d6b-5c8e0f1a2b3c
                      type: sms
                      name: Cart reminder text
                      parent_uuid: 8b3c0f2d-6d4e-4e3f-9c5a-4b7d9e0f1a2b
                      next_uuid: null
                      data:
                        campaign_id: Zt6N1rV4yU
                        message_body: Your cart is waiting — checkout now and save 10%.
                  sync_coupons: false
                  last_transition_at: '2026-06-01T12:00:00Z'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/InsufficientScope'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
        - bearerAuth: []
components:
  parameters:
    AccountIdParam:
      name: Privy-Account-Id
      in: header
      required: false
      description: >
        Target a subaccount instead of the main connected account.

        Pass a subaccount `id` from get_account's `subaccounts` list. Only
        direct subaccounts

        of the connected account are reachable — omit this for the main account
        itself.
      schema:
        type: string
      example: S1U2B3A4C5C6T7
  schemas:
    FlowDetail:
      allOf:
        - $ref: '#/components/schemas/Flow'
        - type: object
          properties:
            nodes:
              type: array
              description: >-
                The flow's steps from its current version, in tree-traversal
                order, with the branch graph and each step's configuration.
              items:
                $ref: '#/components/schemas/FlowDetailNode'
            reentry:
              type: object
              description: |
                The effective re-entry behavior — whether a contact who has
                been through the flow can enter it again. `immediate`: a new
                qualifying trigger re-enrolls the contact as soon as their
                previous journey has ended. `never`: the contact enters at
                most once. `after_delay`: the contact may re-enter once
                `frequency` `unit` have elapsed since their previous journey
                ended — measured from when it ended, however it ended
                (reaching the last node, being removed by exit criteria, or
                the flow being deactivated mid-journey). In every mode a
                contact with a journey in progress is never re-enrolled — no
                setting runs two journeys at once.
              properties:
                mode:
                  type: string
                  enum:
                    - immediate
                    - never
                    - after_delay
                  example: after_delay
                frequency:
                  type: integer
                  description: Present only when `mode` is `after_delay`.
                  example: 30
                unit:
                  type: string
                  enum:
                    - minutes
                    - hours
                    - days
                  description: Present only when `mode` is `after_delay`.
                  example: days
            flow_filters:
              anyOf:
                - $ref: '#/components/schemas/ConditionTree'
                - type: 'null'
              description: >
                Conditions a contact must meet to enter the flow, evaluated once

                when the triggering event is processed and never re-checked

                afterwards. Contact properties (segment membership, tags, order

                count, lifetime spend, country, custom fields, integration
                fields)

                are matched against the contact's live record; event properties
                (the

                triggering cart or order's value, subscription channel, source,
                and

                history) are matched against the triggering event's payload.
                `null`

                when the flow has no filters.
            exit_behavior:
              type:
                - object
                - 'null'
              description: |
                The effective early-exit behavior; `null` when nothing ends
                journeys early. When any `exit_events` event occurs after a
                contact enrolled, their in-progress journey ends. This applies
                only to journeys already in progress: the event that enrolled
                the contact never triggers the exit, and exits can never block
                entry. On a single event the exit of a previous journey runs
                before enrollment is evaluated, so a trigger and an exit
                firing on the same event type (e.g. an order-placed trigger
                with an order-placed exit) is a working setup — the standard
                winback / purchase follow-up pattern, not a misconfiguration.
                Whether that same event also re-enrolls the contact is
                `reentry.mode`. The stored exit condition's comparison
                operator and value are not evaluated by the engine and are
                deliberately not returned.
              properties:
                exit_events:
                  type: array
                  description: |
                    Events that end an in-progress journey. An order-count
                    exit condition listens to both `order_placed` and
                    `order_delivered`, so those two always appear together.
                  items:
                    type: string
                    enum:
                      - order_placed
                      - order_delivered
                      - product_added_to_cart
                      - checkout_started
                  example:
                    - order_placed
                    - order_delivered
                applies_to:
                  type: string
                  enum:
                    - active_journeys_only
                  description: >-
                    Exits only end journeys already in progress — they never
                    block entry.
                enrollment_event_excluded:
                  type: boolean
                  enum:
                    - true
                  description: The event that enrolled the contact never triggers the exit.
                summary:
                  type: string
                  description: Server-generated restatement of this exit behavior.
                  example: >-
                    After enrollment, a subsequent order placed or order
                    delivered event ends the journey early. The event that
                    enrolled the contact never triggers this exit.
            behavior_summary:
              type: string
              description: |
                Server-generated plain-language description of the flow's
                effective runtime behavior: enrollment, message timing, early
                exits, and re-entry. Deterministic — derived from the
                configuration by the server. Prefer it over reconstructing
                event ordering from the raw fields.

                When `status` is not `active` the summary opens by saying so:
                a flow that is not active enrolls no contacts and sends no
                messages, and the sentences that follow describe what it is
                configured to do once activated, not what it is doing now.
              example: >-
                A contact enrolls when the "Cart abandoned" event occurs. The
                flow has 2 message steps. A subsequent order placed or order
                delivered event ends an active journey early; the event that
                enrolled the contact never triggers this exit. A contact can
                re-enter 30 days after their previous journey ends; no setting
                ever runs two journeys at once.
    Flow:
      type: object
      properties:
        id:
          type: string
          description: >-
            Stable, unique identifier for the flow. Use this value in
            `/v1/flows/{id}`.
          example: Vq7L3mN9xR
        title:
          type:
            - string
            - 'null'
          example: Abandoned Cart Recovery
        status:
          type: string
          enum:
            - draft
            - active
            - inactive
          example: active
        trigger_type:
          type:
            - string
            - 'null'
          description: >
            Normalized name of the event that starts the flow — e.g.

            `order_placed`, `order_delivered`, `cart_abandoned`,

            `checkout_abandoned`, `browse_abandoned`, `back_in_stock`,

            `date_triggered`, `customer_subscribed_to_marketing`,

            `customer_tag_added`, `customer_tag_removed`, `flow_completed`.

            `order_placed` is the one name for the per-platform order events

            (Shopify, BigCommerce, Wix, and the public API) and
            `order_delivered`

            renames the order-received event; every other trigger passes through
            as

            stored, so integration and custom API event names (`api_event:...`)

            appear here verbatim.


            Platform availability: `browse_abandoned` and `back_in_stock`
            currently

            fire for Shopify stores only — the underlying product-view and
            inventory

            signals aren't collected for other platforms, so a flow using one of

            these triggers on a non-Shopify business will never enroll a
            contact.

            `checkout_abandoned` and `cart_abandoned` are available on both
            Shopify

            and BigCommerce. Shopify checkout starts come from the Web Pixel, so
            the

            pixel must hold the scopes that track them; BigCommerce checkout
            starts

            are captured when the Privy widget loads on the standard Optimized

            One-Page Checkout, so custom or headless BigCommerce checkouts
            produce no

            event and never enroll a contact.
          example: cart_abandoned
        trigger_label:
          type:
            - string
            - 'null'
          description: Human-readable form of `trigger_type`.
          example: Cart abandoned
        nodes:
          type: array
          description: The flow's steps, from its current version.
          items:
            $ref: '#/components/schemas/FlowNode'
        sync_coupons:
          type: boolean
          description: >
            Whether the flow uses one shared coupon: when true, a single coupon
            is

            set at the flow level and applied to every email and SMS node in
            every

            version of the flow, including nodes added later. When false,
            coupons

            are attached to individual nodes by hand and nothing is propagated.
          example: false
        last_transition_at:
          type:
            - string
            - 'null'
          format: date-time
          description: When the flow last changed status.
          example: '2026-06-01T12:00:00Z'
    FlowDetailNode:
      allOf:
        - $ref: '#/components/schemas/FlowNode'
        - type: object
          description: >-
            Node shape returned by `GET /v1/flows/{id}` — adds the branch graph
            and the node's configuration. Nodes are listed in tree-traversal
            order (depth-first from the node whose `parent_uuid` is `null`, yes
            branch before no branch), so after a split the whole yes branch
            reads before the no branch.
          properties:
            parent_uuid:
              type:
                - string
                - 'null'
              description: '`uuid` of the node''s parent; `null` on the flow''s first node.'
              example: null
            next_uuid:
              type:
                - string
                - 'null'
              description: >-
                `uuid` of the next node. Present on every node type except
                `split` and `ab_test`; `null` at the end of a branch.
              example: 7a2f9e1b-5c3d-4d2e-8b4f-3a6c8d9e0f1a
            yes_next_uuid:
              type:
                - string
                - 'null'
              description: >-
                `split` nodes only — `uuid` of the first node on the branch
                taken when the conditions match.
              example: 7a2f9e1b-5c3d-4d2e-8b4f-3a6c8d9e0f1a
            no_next_uuid:
              type:
                - string
                - 'null'
              description: >-
                `split` nodes only — `uuid` of the first node on the branch
                taken when the conditions do not match.
              example: 9c4d1a3e-7e5f-4f4a-8d6b-5c8e0f1a2b3c
            a_next_uuid:
              type:
                - string
                - 'null'
              description: '`ab_test` nodes only — `uuid` of the first node on path A.'
              example: 7a2f9e1b-5c3d-4d2e-8b4f-3a6c8d9e0f1a
            b_next_uuid:
              type:
                - string
                - 'null'
              description: >-
                `ab_test` nodes only — `uuid` of the first node on path B. When
                it is another `ab_test` node, that node splits the B traffic
                again.
              example: 9c4d1a3e-7e5f-4f4a-8d6b-5c8e0f1a2b3c
            data:
              type: object
              description: >-
                The node's configuration; fields depend on `type`. `email` —
                `campaign_id` (usable with `GET /v1/campaigns/{id}`), `subject`,
                `coupon` (the attached pool as `{id, title, kind, platform}`,
                `null` when none — a `{{ coupon_code }}` tag only renders when
                attached). `sms` — `campaign_id`, `message_body`, `coupon` (as
                above). `delay` — `delay_unit` (`minutes`/`hours`/`days`),
                `delay_unit_count`. `split` — `conditions` (the condition group
                the split evaluates). `ab_test` — `a_percent` (share of contacts
                sent down path A; path B gets the rest), `winner` (`a` or `b`
                once a winner is picked, otherwise `null`). `tag` —
                `action_type` (`add`/`remove`), `tags` (tag names).
                `update_contact` — `action_type`.
              properties:
                conditions:
                  allOf:
                    - $ref: '#/components/schemas/ConditionTree'
                  description: >-
                    Present on `split` nodes only — the condition group the
                    split evaluates. Ids embedded in its values follow the id
                    and prefix rules described on `ConditionTree`; this is where
                    the `tag_`, `stag_`, and `dfl_` prefixed ids surface.
              example:
                delay_unit: hours
                delay_unit_count: 1
    ConditionTree:
      type: object
      description: >-
        A group of conditions. `operation` joins them (`AND`/`OR`); each
        condition tests a property (e.g. segment membership, order count, cart
        value) with an operator and value. The same shape carries a flow's entry
        filters (`flow_filters`) and a split node's `conditions` — each is
        evaluated at a different point, described on those fields. A flow's exit
        criteria are not published in this shape; see `exit_behavior`.

        Entity ids embedded in condition values are public ids. Unprefixed ids
        round-trip through their GET endpoints: `segment` values via
        `/v1/segments/{id}`, `source_flow_id` and `skip_customer_in_flows`
        values via `/v1/flows/{id}`, and a signup `source`'s values via
        `/v1/displays/{id}`.

        Ids for objects with no lookup endpoint carry a type prefix, so a
        prefixed id is never a valid argument to any `/v1` path: Privy tags read
        `tag_<id>`, Shopify tags read `stag_<id>` (both in `privy_tag`,
        `shopify_tag`, and the `*_tag_added`/`*_tag_removed` trigger filters),
        and a `date_triggered` value's `date_field_label_id` reads `dfl_<id>`.
        The same prefix appears inside the `date_field_label$<id>` property
        name. Do not strip a prefix to retry a lookup — the remainder decodes as
        another resource's id and will return the wrong record.

        A `segment` value may also be a smart-segment reference of the form
        `smart_segments/<name>` — an opaque built-in segment name, not an id.
        The prefix is there to mark it unresolvable, so the same segment reads
        differently on a campaign's audience, where it appears as the bare
        `<name>` with `"prebuilt": true`; the name after the prefix is what
        matches the two. Sentinel values (`"*"` for any display, `"any"`) are
        not ids. Never treat other condition values as ids — product and
        collection values are the connected store's own ids.

        A condition's `type` describes the value as Privy stores it, not as it
        is emitted here: an id stored as an integer is published as its public
        id, so a row reading `"type": "integer"` still carries a string. Read
        ids as strings regardless of the `type` beside them.
      properties:
        operation:
          type: string
          description: >-
            How this group's conditions combine — `AND` requires every condition
            to match, `OR` any one of them.
          enum:
            - AND
            - OR
          example: AND
        conditions:
          type: array
          description: >-
            The group's members. An entry is either a single test, as described
            below, or a nested group carrying its own `operation` and
            `conditions` — trees can be arbitrarily deep, so recurse rather than
            assuming one level.
          items:
            type: object
            properties:
              property:
                type: string
                description: >-
                  The attribute being tested. Some property names embed a
                  discriminator — `custom_fields$<identifier>` names a
                  merchant-defined field and `date_field_label$<id>` a date
                  field.
                example: segment
              operator:
                type: string
                description: >-
                  How `value` is compared against the property (e.g. `equals`,
                  `contains`, `greater_than`).
                example: contains
              type:
                type: string
                description: >-
                  How Privy stores the value (e.g. `integer`, `string`, `array`)
                  — not how it is emitted here. An id stored as an integer is
                  published as its public id, so a row reading `integer` still
                  carries a string. Read ids as strings regardless of this
                  field.
                example: array
              value:
                description: >-
                  The comparison value — a scalar or an array, depending on the
                  property and operator.
                example:
                  - Nk2P5rW8yU
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - invalid_client
                - invalid_scope
                - unauthorized
                - insufficient_scope
                - paid_plan_required
                - api_access_required
                - not_found
                - no_connected_store
                - conflict
                - contacts_not_ready
                - validation_failed
                - rate_limited
                - store_unavailable
              example: validation_failed
            message:
              type: string
              example: One or more fields are invalid
            details:
              type: array
              items:
                type: object
                properties:
                  field:
                    type: string
                    example: email
                  message:
                    type: string
                    example: is required
    FlowNode:
      type: object
      description: >-
        One step in the flow. `email` and `sms` send a message — the only node
        types that carry per-node report stats; `delay` waits; `split` branches
        on conditions; `ab_test` sends each contact down path A or B by a
        percentage split; `tag` and `update_contact` change the contact.
      properties:
        uuid:
          type: string
          example: 6f1e8d0a-4b2c-4c1d-9a3e-2f5b7c8d9e0f
        type:
          type: string
          enum:
            - email
            - sms
            - delay
            - split
            - ab_test
            - tag
            - update_contact
          example: email
        name:
          type: string
          example: Welcome email
  responses:
    Unauthorized:
      description: Missing or invalid bearer token.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: unauthorized
              message: Bearer token is missing or invalid
    InsufficientScope:
      description: Token lacks the required scope for this endpoint.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: insufficient_scope
              message: Token does not have the required scope
    NotFound:
      description: >-
        The requested resource does not exist or does not belong to your
        account.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: not_found
              message: Not found
    RateLimited:
      description: Rate limit exceeded. Retry after the specified time.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
          example: 42
        X-RateLimit-Limit-Minute:
          description: Maximum requests allowed per minute.
          schema:
            type: integer
          example: 60
        X-RateLimit-Remaining-Minute:
          description: Requests remaining in the current minute window.
          schema:
            type: integer
          example: 0
        X-RateLimit-Reset-Minute:
          description: Unix timestamp when the minute window resets.
          schema:
            type: integer
          example: 1711929600
        X-RateLimit-Limit-Day:
          description: Maximum requests allowed per day.
          schema:
            type: integer
          example: 10000
        X-RateLimit-Remaining-Day:
          description: Requests remaining in the current day window.
          schema:
            type: integer
          example: 9500
        X-RateLimit-Reset-Day:
          description: Unix timestamp when the day window resets.
          schema:
            type: integer
          example: 1712016000
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: rate_limited
              message: Rate limit exceeded
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API token or OAuth access token
      description: >
        Send either an API token or an OAuth access token as

        `Authorization: Bearer <token>`. See the Authentication page for
        details.

````