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

# List Payment Intents

> GET /payment-intents



## OpenAPI

````yaml GET /payment-intents
openapi: 3.1.0
info:
  title: ZoPay Connect API
  description: >-
    B2B partner API for crypto custody, deposits, and payouts. Authenticate
    every request with a Bearer token (``sk_live_…`` for production,
    ``sk_test_…`` for sandbox).
  version: v1
servers:
  - url: https://api.zopay.cash/connect/v1
    description: Production
  - url: https://dev.api.zopay.cash/connect/v1
    description: Development
security:
  - BearerAuth: []
tags:
  - name: capabilities
    description: >-
      Static product catalog: which assets, networks, and fiat valuations this
      tenant can use. Poll on app startup to drive every UI surface (network
      pickers, fee warnings, fiat selection).
  - name: addresses
    description: >-
      Per-user deposit addresses. Mint with ``external_ref`` to attribute to one
      of your users; omit for a tenant-treasury (pooled) address.
  - name: payment-intents
    description: >-
      Single-use payment requests. Mint an intent for a specific amount +
      currency set; we expand it to per-network deposit addresses. Pass
      ``external_ref`` to attribute the intent to one of your users for
      downstream webhook reconciliation.
  - name: balances
    description: >-
      Per-user or tenant-wide balance snapshot with multi-fiat valuation (USD,
      PEN, EUR, MXN, ARS, COP, DOP).
  - name: transactions
    description: >-
      Unified ledger covering deposits, payouts, internal transfers, and (after
      Phase 4) conversions and ramps.
  - name: payouts
    description: >-
      Outbound money movement: get a signed quote, then execute it. Quote IDs
      are single-use and expire in 60s.
  - name: conversions
    description: '[Coming soon] Crypto-to-crypto conversions.'
  - name: ramps
    description: '[Coming soon] Fiat <-> crypto hosted-checkout sessions.'
  - name: webhooks
    description: '[Coming soon] Subscribe to event streams + replay past events.'
  - name: health
    description: >-
      Auth + connectivity probe. Use to validate your API key works before
      depending on a priced endpoint.
  - name: test-helpers
    description: >-
      Sandbox-only simulators. Let you exercise webhook handlers and downstream
      state transitions without real testnet activity. Routes return 404 to
      ``sk_live_…`` keys.
paths:
  /payment-intents:
    get:
      tags:
        - payment-intents
      summary: List Payment Intents
      operationId: list_payment_intents_payment_intents_get
      parameters:
        - name: external_ref
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            description: >-
              Filter to one partner-supplied attribution key. Omit to list every
              intent across this tenant.
            title: External Ref
          description: >-
            Filter to one partner-supplied attribution key. Omit to list every
            intent across this tenant.
        - name: cursor
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            description: >-
              Opaque continuation token from a previous response's ``cursor``
              field. Omit to fetch the first page.
            title: Cursor
          description: >-
            Opaque continuation token from a previous response's ``cursor``
            field. Omit to fetch the first page.
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            maximum: 200
            minimum: 1
            description: Max rows to return (clamped to 200).
            default: 50
            title: Limit
          description: Max rows to return (clamped to 200).
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectPaymentIntentsListResponse'
        '400':
          description: >-
            Invalid request. The body or query parameters failed validation.
            ``error.code`` names the specific failure (e.g. ``invalid_request``,
            ``catalog_invalid``, ``min_usdt_amount_requires_solana``);
            ``error.param`` names the offending field when applicable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectErrorEnvelope'
        '401':
          description: >-
            Authentication failed. ``error.code`` distinguishes
            ``authentication_required`` (missing / malformed / unknown key) from
            ``expired_credential`` (key matched but past its ``expires_at``).
            Rotate the key in the admin panel for the latter.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectErrorEnvelope'
        '403':
          description: >-
            The key is valid but lacks permission for this endpoint.
            ``error.code`` is ``forbidden_scope`` when the key is missing a
            scope; ``account_pending_approval`` when the org is not yet active
            for Connect.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectErrorEnvelope'
        '404':
          description: >-
            Resource not found in this tenant. Same envelope shape is returned
            for genuinely-missing IDs and for cross-tenant accesses -- existence
            under other tenants is not leakable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectErrorEnvelope'
        '409':
          description: >-
            Conflict. Most commonly ``idempotency_conflict`` -- the same
            ``Idempotency-Key`` was reused with a different body. Generate a
            fresh key or replay the original body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectErrorEnvelope'
        '422':
          description: >-
            Validation failed. Runtime translates FastAPI / Pydantic validation
            errors into the same Connect envelope you see on 400s; this status
            is emitted when the request shape is structurally wrong (missing
            required field, wrong type).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectErrorEnvelope'
        '429':
          description: >-
            Rate limited. Back off and retry. ``error.code`` is
            ``rate_limited``; per-key throughput is documented via
            ``rate_limit_rps`` on the API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectErrorEnvelope'
        '500':
          description: >-
            Internal server error. ``error.code`` is ``internal_error``. The
            request can usually be safely retried with the same
            ``Idempotency-Key``.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectErrorEnvelope'
        '501':
          description: >-
            Endpoint is declared in the router but its implementation has not
            shipped yet (``error.code = not_yet_available``). Pinned in the
            schema so SDK generators emit the endpoint with a typed error rather
            than skipping it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectErrorEnvelope'
      security:
        - BearerAuth: []
components:
  schemas:
    ConnectPaymentIntentsListResponse:
      properties:
        data:
          items:
            $ref: '#/components/schemas/ConnectPaymentIntent'
          type: array
          title: Data
        has_more:
          type: boolean
          title: Has More
          description: >-
            True iff at least one more row exists past this page. Computed by
            fetching ``limit + 1`` server-side.
        cursor:
          anyOf:
            - type: string
            - type: 'null'
          title: Cursor
          description: >-
            Opaque continuation token. Pass back as ``?cursor=...`` to fetch the
            next page; None on the last page.
      type: object
      required:
        - has_more
      title: ConnectPaymentIntentsListResponse
      description: |-
        Paginated list response. Same shape as
        ConnectAddressesListResponse -- partners get a consistent
        pagination contract across all Connect list endpoints.
    ConnectErrorEnvelope:
      properties:
        error:
          $ref: '#/components/schemas/ConnectErrorBody'
          description: >-
            Error details. Always present on a non-2xx response from
            /connect/v1.
      type: object
      required:
        - error
      title: ConnectErrorEnvelope
      description: |-
        Top-level wrapper for every /connect/v1 failure response.

        Wire shape::

            {"error": {"type": ..., "code": ..., "message": ..., ...}}

        The wrapper is intentional: it keeps error bodies syntactically
        distinct from success bodies, so polymorphic partner code paths
        that read the same JSON for both states can branch on the
        presence of the ``error`` key.
    ConnectPaymentIntent:
      properties:
        id:
          type: string
          title: Id
          description: Payment intent UUID. Pass to GET /payment-intents/:id.
        object:
          type: string
          title: Object
          description: Always ``payment_intent``. Stripe convention.
          default: payment_intent
        amount:
          anyOf:
            - type: number
            - type: 'null'
          title: Amount
          description: Requested amount in the intent's primary asset.
        currency:
          type: string
          title: Currency
          description: Primary asset code, uppercase (e.g. USDT).
        status:
          anyOf:
            - type: string
            - type: 'null'
          title: Status
          description: >-
            Intent status. One of: pending, partial, completed, expired
            (snake_case, Stripe-compatible).
        ref_code:
          anyOf:
            - type: string
            - type: 'null'
          title: Ref Code
          description: >-
            5-character public-page identifier. Embedded in hosted checkout URLs
            (zopay.cash/pay/<ref_code>).
        external_ref:
          anyOf:
            - type: string
            - type: 'null'
          title: External Ref
          description: >-
            Partner-supplied attribution key as passed to POST /payment-intents.
            Echoed verbatim. Use for your own per-user reconciliation.
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: Partner-supplied description, echoed verbatim.
        metadata:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          title: Metadata
          description: >-
            Partner-supplied metadata, echoed verbatim. NULL when no metadata
            was sent at creation.
        addresses:
          items:
            $ref: '#/components/schemas/ConnectPaymentIntentAddress'
          type: array
          title: Addresses
          description: >-
            Per (asset, network) deposit destinations. One row per currency /
            network pair available on this intent.
        created_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Created At
          description: When the intent was minted.
        expires_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Expires At
          description: When the intent stops accepting deposits.
        completed_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Completed At
          description: >-
            When the intent transitioned to ``status='completed'``. NULL while
            pending. Idempotency anchor for the completion transition -- a
            retried call sees the same value, never a fresh one.
        deposit:
          anyOf:
            - $ref: '#/components/schemas/ConnectPaymentIntentDeposit'
            - type: 'null'
          description: >-
            Chain-side details of the deposit that fulfilled this intent. NULL
            while pending; populated on completion. Field-for-field parity with
            the ``payment_intent.completed`` webhook event payload so pull and
            push reconciliation see one shape. On a partial-then-complete intent
            this object reflects the LATEST deposit, not the cumulative total;
            use ``amount_received`` for the cumulative figure.
        amount_received:
          type: number
          title: Amount Received
          description: >-
            Cumulative amount credited against this intent across all completed
            deposits. Always present; defaults to 0.0 when nothing has landed
            yet. On a partial payment this is the partial total; once the intent
            transitions to ``completed`` it equals (or slightly exceeds, by up
            to the 0.01 tolerance) ``amount``.
          default: 0
        amount_remaining:
          type: number
          title: Amount Remaining
          description: >-
            Raw remaining amount: ``max(0, amount - amount_received)``. No
            tolerance applied -- partners that want to render a 'pay X more' UI
            should check ``partial_payment`` first and read this value as the
            literal shortfall. A near-complete intent within the 0.01 tolerance
            shows a small positive value here but ``partial_payment=false``.
          default: 0
        partial_payment:
          type: boolean
          title: Partial Payment
          description: >-
            True iff the intent has received at least one deposit but is not yet
            completed: ``amount_received > 0 AND status == 'pending'``. Use this
            as the canonical 'is incomplete' boolean for partial-payment UI.
            Tolerance is handled transitively through ``status``: the credit
            flow only leaves the intent in ``pending`` when the cumulative
            received amount is below the (amount - 0.01) threshold, so a
            near-complete intent within tolerance has already transitioned to
            ``completed`` and surfaces as ``partial_payment=false`` here.
          default: false
      type: object
      required:
        - id
        - currency
      title: ConnectPaymentIntent
      description: Stripe-shaped payment intent envelope.
    ConnectErrorBody:
      properties:
        type:
          type: string
          title: Type
          description: >-
            Stripe-style error category. One of: ``invalid_request_error``,
            ``authentication_error``, ``not_found_error``, ``rate_limit_error``,
            ``idempotency_error``, ``api_error``. Stable across minor versions;
            branch on this for top-level error routing.
          examples:
            - invalid_request_error
        code:
          type: string
          title: Code
          description: >-
            Machine-stable identifier for the specific failure. More specific
            than ``type``. Examples: ``invalid_request``,
            ``authentication_required``, ``expired_credential``,
            ``forbidden_scope``, ``account_pending_approval``,
            ``resource_not_found``, ``idempotency_conflict``, ``rate_limited``,
            ``internal_error``, ``not_yet_available``. Match on ``code`` in your
            client's error handler -- never parse ``message``.
          examples:
            - authentication_required
        message:
          type: string
          title: Message
          description: >-
            Human-readable explanation. Safe to surface in your own UI but may
            be reworded between releases -- never branch on the string.
          examples:
            - >-
              Missing or malformed Authorization header. Send: Authorization:
              Bearer sk_live_... or sk_test_...
        doc_url:
          type: string
          title: Doc Url
          description: >-
            Link to docs explaining this ``code``. Constructed as
            ``https://docs.zopay.cash/errors/<code>``. Surface this in support
            tickets so we can investigate without a round-trip.
          examples:
            - https://docs.zopay.cash/errors/authentication_required
        param:
          anyOf:
            - type: string
            - type: 'null'
          title: Param
          description: >-
            Names the offending input field when the error is
            input-shape-related (mostly ``invalid_request_error`` from 400 and
            422). Absent on auth, scope, rate-limit, and internal errors.
          examples:
            - currencies
      type: object
      required:
        - type
        - code
        - message
        - doc_url
      title: ConnectErrorBody
      description: |-
        The ``error`` object inside every /connect/v1 failure response.

        Mirrors ``ConnectError.to_envelope()`` in
        ``app/api/connect/errors.py``. Field set is locked -- adding a
        new field is a partner-visible contract change and requires a
        coordinated docs + schema update.
    ConnectPaymentIntentAddress:
      properties:
        asset:
          type: string
          title: Asset
          description: Asset code, uppercase (USDT, XAUT).
        network:
          anyOf:
            - type: string
            - type: 'null'
          title: Network
          description: >-
            Network code, lowercase (solana, ethereum). NULL on the rare case
            where the underlying address row references a network not yet in the
            catalog.
        address:
          type: string
          title: Address
          description: >-
            On-chain wallet address verbatim. Pass to your payer as the deposit
            destination.
      type: object
      required:
        - asset
        - address
      title: ConnectPaymentIntentAddress
      description: A single deposit-destination row inside a payment intent.
    ConnectPaymentIntentDeposit:
      properties:
        amount_received:
          anyOf:
            - type: number
            - type: 'null'
          title: Amount Received
          description: >-
            Amount actually credited, in ``currency`` units. May differ from the
            intent's ``amount`` on partial payments (today: equal; future: lower
            on a partial).
        currency:
          anyOf:
            - type: string
            - type: 'null'
          title: Currency
          description: >-
            Asset code of the credited deposit, uppercase. Mirrors the intent's
            primary ``currency`` today; the field exists per-deposit because
            future wrong-currency deposits will populate it independently.
        network:
          anyOf:
            - type: string
            - type: 'null'
          title: Network
          description: >-
            Network code, lowercase. Mirrors the address row's network on a
            normal deposit.
        address_receiver:
          anyOf:
            - type: string
            - type: 'null'
          title: Address Receiver
          description: >-
            On-chain destination address that received the funds (the intent's
            primary address on a normal flow).
        address_sender:
          anyOf:
            - type: string
            - type: 'null'
          title: Address Sender
          description: >-
            On-chain sender address. ``zSAND_simulator_sender`` verbatim for
            sandbox-simulated deposits -- partners filtering test-mode senders
            out of their dashboards can grep this literal.
        tx_hash:
          anyOf:
            - type: string
            - type: 'null'
          title: Tx Hash
          description: >-
            Chain transaction hash that credited the deposit. Hex string. For
            sandbox-simulated deposits this is a deterministic SHA-256 over the
            intent id so partner tests can pin on the value.
      type: object
      title: ConnectPaymentIntentDeposit
      description: |-
        Chain-side details of the deposit that fulfilled this intent.

        Populated on ``GET /payment-intents/{id}`` and on the
        ``simulate-deposit`` response once a deposit has been observed
        on-chain (live) or simulated (sandbox). NULL while the intent
        is still ``pending``.

        Field-for-field parity with the ``payment_intent.completed``
        webhook event payload so partners building both pull and push
        reconciliation see one shape. Stripe's analogue is
        ``PaymentIntent.charges.data[0]`` -- we nest a single object
        because today every intent fulfills via one credit; the shape
        can grow to a list later without breaking the single-object
        contract (additive).
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Connect API key (sk_live_… or sk_test_…)
      description: >-
        Paste your Connect API key (sk_live_… for production, sk_test_… for
        sandbox) without the ``Bearer `` prefix. Mint and rotate keys from the
        admin panel.

````