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

# Submit payment request

> Submit a signed payment request to the Atum Payment Gateway. request_id is the idempotency key that makes a retry safe.

<Note>
  `request_id` is your idempotency key for this call. Reusing it resumes the same payment; a new one starts a new payment; reusing it with different terms is refused with `409`. See [Idempotency](/payment-protocols/idempotency).
</Note>


## OpenAPI

````yaml POST /v1/payments
openapi: 3.0.3
info:
  title: Atum Payment Gateway API
  description: >
    The Atum Payment Gateway API enables secure, cross-chain payments with
    built-in escrow protection 

    and settlement verification. This API facilitates decentralized payment
    routing across multiple blockchain 

    networks while ensuring transaction atomicity and settlement guarantees.


    ## Supported Blockchains

    - Ethereum and EVM-compatible chains (Arbitrum, Optimism, Base, etc.)

    - Solana

    - Tron


    ## Authentication


    No API key, token, or authorization header is required on any operation.

    A payment carries its own authorization: the `sender_auth` signed messages
    prove

    control of the funding account, and are verified on every submission.
  version: 1.0.0
servers:
  - url: https://payment-gw.production-mainnet.atum.xyz
    description: Mainnet
  - url: https://payment-gw.production-testnet.atum.xyz
    description: Testnet
security: []
tags:
  - name: Payments
    description: >-
      Submit a payment and follow it to settlement, by payment id, by your own
      request id, or step by step.
  - name: Authorizations
    description: Counterparty authorization requested while a payment is being accepted.
  - name: Quote Requests
    description: >-
      The two-phase quote flow - open a quote-request batch, poll it, then award
      one quote.
  - name: Chain Defaults
    description: >-
      The per-chain corridor addresses a client needs before it can build and
      sign a payment.
  - name: Health
    description: Liveness probe for the gateway.
paths:
  /v1/payments:
    post:
      tags:
        - Payments
      summary: Submit a payment request
      description: >
        Initiates a payment by submitting a cryptographically signed payment
        request.
         
         The payment gateway will:
         1. Validate the request and signatures
         2. Find optimal settlement routes across chains/assets
         3. Secure funds in escrow
         4. Coordinate with settlement agents for execution
         5. Return a real-time result if settlement completes inside the synchronous window,
            otherwise return a payment ID and status for polling

         **Idempotency.** `request_id` is the idempotency key, scoped to the source account
         and originator. Submitting the same `request_id` again returns the ORIGINAL payment
         instead of charging a second time, so a client whose request timed out or failed
         in transit can safely re-submit.

         Because a re-submission must be admissible on its own, some fields legitimately
         differ between attempts and are excluded from the comparison: `quote_deadline` and
         `fulfillment_deadline` (both must be in the future, so a later attempt has to move
         them), and the `sender_auth` signature they are signed into.

         What must NOT differ is the payment's economics — source account and asset,
         destination accounts and assets, `fulfillment_amount`, and `max_source_amount` (the
         payer's own risk cap; lowering it on a retry describes a different payment, not the
         same one with less headroom). Re-using one `request_id` for a genuinely different
         payment is a client bug that would otherwise be silently collapsed onto the first
         payment, so it is rejected with `409` and `IDEMPOTENCY_TERMS_MISMATCH`. Use a
         distinct `request_id` per payment.
      operationId: submitPayment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaymentRequest'
      responses:
        '200':
          description: Payment request submitted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentResponse'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Signature or sender authorization could not be verified
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Payment declined during counterparty authorization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >
            The `request_id` has already been used for a payment with different
            economics

            (`IDEMPOTENCY_TERMS_MISMATCH`). Re-submitting one `request_id` is
            how a retry is

            made safe, so it must identify one payment; use a distinct
            `request_id` per

            payment.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: Payment request too large to process
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            The request was not accepted: a dependency is temporarily
            unavailable, or this operation is paused (`SERVICE_PAUSED`). Nothing
            was submitted, so the request is safe to retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    PaymentRequest:
      description: >-
        Structured payment request with sender authorization and optional W3C
        Verifiable Credentials
      type: object
      required:
        - version
        - request_id
        - source
        - destination
        - fulfillment_amount
        - sender_auth
        - quote_deadline
        - fulfillment_deadline
      properties:
        version:
          type: string
          description: >-
            Semver-shaped declaration version (MAJOR.MINOR). The major component
            is locked to this schema's parent directory (v1 → major must be 1).
            Producers MUST emit the canonical value exported by the bindings'
            Version constant; the canonical bumps on each additive minor
            evolution.
          pattern: ^1\.[0-9]+$
          example: '1.0'
        request_id:
          type: string
          description: Unique identifier for this payment request (used for idempotency)
          example: req_123456789
        source:
          $ref: '#/components/schemas/OwnedAsset'
        destination:
          type: array
          description: >-
            Array of destination assets for the settlement agent to choose from
            (at least one required, at most 16).

            The ceiling bounds the fan-out one request can buy: every consumer
            iterates this list.
          minItems: 1
          maxItems: 16
          items:
            $ref: '#/components/schemas/OwnedAsset'
        fulfillment_amount:
          type: string
          description: >-
            The exact amount to be received at the destination (in the smallest
            unit, e.g., wei for ETH, cents for USD).
          example: '1000000'
        max_source_amount:
          type: string
          description: >-
            The maximum amount willing to be spent from the source (in the
            smallest unit).
          example: '1000000'
        quote_deadline:
          type: string
          format: date-time
          description: >-
            Required. The deadline for receiving price quotes from settlement
            providers. After this

            point no new quotes are accepted. The gateway injects no default and
            validates the value

            is in the future. The originator must supply it, and a request that
            omits it or supplies

            a past value is rejected with 400.
          example: '2024-12-25T10:30:00Z'
        fulfillment_deadline:
          type: string
          format: date-time
          description: >-
            Required. The final deadline for completing the payment. If not
            completed by this time the

            payment fails and is eligible for a refund. The gateway injects no
            default and validates

            the value is in the future. The originator must supply it, and a
            request that omits it or

            supplies a past value is rejected with 400.
          example: '2024-12-25T11:00:00Z'
        settler_auth_required:
          type: object
          description: >-
            Policy requirements for settler authentication. Keys are policy
            names, values indicate if the policy is required.
          additionalProperties:
            type: boolean
        sender_auth:
          $ref: '#/components/schemas/SenderAuth'
        originator_auth:
          $ref: '#/components/schemas/VerifiableCredentialAuth'
        quote_selector:
          type: string
          description: >-
            Wallet address or public key of a trusted service that will
            automatically select

            the best exchange rate for your payment. Leave empty to use the
            default selector.
          example: '0x742D35cC6634c0532925a3b844Bc9e7595F41360'
        fulfillment_verifier:
          type: object
          description: >-
            Third-party service that will independently verify your payment was
            delivered.

            Leave empty to use the default verifier.
          required:
            - account
            - endpoint
          properties:
            account:
              type: string
              description: The wallet address or public key of the verification service
              example: '0x892BB2e4F6b14a2B5b82Ba8d33E5925D42D4431F'
            endpoint:
              type: string
              format: uri
              description: >-
                Origin of the fulfillment verifier service, with no path — the
                caller

                appends the verifier's own API paths. The gateway's
                `/v1/defaults` response

                supplies the default fulfillment verifier for each chain.
              example: https://veri-fill.production-testnet.atum.xyz
        escrow_contract_address:
          type: string
          description: >-
            Escrow contract address, used to protect source funds until proof of
            fulfillment has been verified. Leave empty to use the default.
          example: '0x742D35cC6634c0532925a3b844Bc9e7595F41360'
        fulfillment_proxy:
          type: string
          description: >-
            Fulfillment Proxy contract address, used to emit proof of
            fulfillment on the destination chain. Leave empty to use the
            default.
          example: '0x892BB2e4F6b14a2B5b82Ba8d33E5925D42D4431F'
        settler_requirements:
          type: object
          description: >-
            Optional settler filtering requirements. If whitelist is present,
            only whitelisted settlers may win. If blacklist is present,
            blacklisted settlers are excluded. Both can be specified; blacklist
            takes precedence (a settler in both lists is rejected).
          properties:
            whitelist:
              type: array
              uniqueItems: true
              minItems: 1
              maxItems: 100
              description: >-
                List of settler DIDs permitted to fulfill this request. If
                present and non-empty, only these settlers may win. Use W3C DID
                format (e.g., did:pkh:eip155:1:0xABC...).
              items:
                type: string
                pattern: ^did:[a-z][a-z0-9]*:[a-zA-Z0-9._:%-]+$
              example:
                - did:pkh:eip155:1:0x742D35cC6634c0532925a3b844Bc9e7595F41360
            blacklist:
              type: array
              uniqueItems: true
              minItems: 1
              maxItems: 100
              description: >-
                List of settler DIDs excluded from fulfilling this request. If
                present and non-empty, these settlers are rejected. Use W3C DID
                format (e.g., did:pkh:eip155:1:0xABC...).
              items:
                type: string
                pattern: ^did:[a-z][a-z0-9]*:[a-zA-Z0-9._:%-]+$
              example:
                - did:pkh:eip155:1:0x892bb2E4F6b14a2B5b82bA8D33e5925d42d4432f
    PaymentResponse:
      type: object
      description: >
        Response from submitting a payment request. If settlement completes
        inside the

        gateway's synchronous window, the response includes a
        fulfillment_confirmation with

        full settlement details. Otherwise you receive a payment_id plus the
        payment's

        current `status`, and can poll /v1/payments/{paymentId}/status for
        updates.


        Read `status` before deciding what to do next: a `pending` or
        `finalizing` payment is

        in flight and must NOT be re-submitted under a new request_id (that
        would be a second

        payment), whereas a `failed` one is terminal — re-submitting it under
        the SAME

        request_id resolves to the same terminal payment, so a genuine
        re-attempt needs a

        new request_id.


        **A synchronously-completed payment carries proof of QUOTE as well as of
        delivery.** When

        settlement lands inside the window you get `status: completed`, a full

        `fulfillment_confirmation`, and — since a quote necessarily was awarded
        to get there —

        `quote_id` and `fulfillment_quote`. The fast path therefore needs no
        follow-up call to

        verify: recompute `quote_hash` over the returned quote and compare it
        against the on-chain

        commitment from this response alone.


        `status` is present on every response. `completed` is always accompanied
        by

        fulfillment_confirmation; `failed` is accompanied by `error` where a
        reason has been

        recorded.
      required:
        - payment_id
        - status
        - idempotent_replay
      properties:
        payment_id:
          type: string
          description: >
            Unique ID for tracking this payment. Save this to check status
            later.
          example: '0x761989729fefb4b0cb8b3e6f787301a3878c91e8fba35ed362a079292879f34f'
        status:
          $ref: '#/components/schemas/PaymentStatus'
        transactions:
          $ref: '#/components/schemas/PaymentTransactions'
        idempotent_replay:
          type: boolean
          description: >
            Whether this response describes a payment that already existed,
            rather than one

            created by this request. `true` means the request_id had been used
            before and you

            are being handed the ORIGINAL payment — no second payment was
            created and nothing

            additional was charged.


            **Re-attempting a payment.** Re-submit the same request and you get
            the same payment

            back. You may re-send the original bytes unchanged: the deadlines
            will have expired by

            then — `quote_deadline` bounds the auction and is short, while this
            endpoint may hold a

            submission for up to 30 seconds — and an expired deadline on a
            re-submission is not an

            error, because the request is resolved onto the payment that already
            exists rather than

            used to create one.


            Re-signing with fresh deadlines is equally fine and returns the same
            payment. What you

            must NOT change is the request_id, and what you must not change
            under it is the

            payment's economics — source, destination, fulfillment_amount,
            max_source_amount — which

            are rejected with 409 IDEMPOTENCY_TERMS_MISMATCH. Deadlines and the
            signature are

            exempt precisely so that a re-attempt is possible.


            A genuinely new payment needs a NEW request_id. In particular a
            `failed` payment is

            terminal: re-submitting its request_id keeps returning that dead
            payment, so recovering

            means starting a new one under a new key.


            Present on every response, `false` for a newly created payment. A
            response without

            the field is from a gateway predating it, not a newly created
            payment.


            This is the field to read when reconciling a suspected double
            charge: two

            submissions where the second reports `true` are one payment, whereas
            two both

            reporting `false` are two. It says nothing about how far the payment
            has got —

            read `status` for that.
          example: false
        quote_id:
          type: string
          description: >
            The winning quote's ID. Present when `status` is `completed` — a
            payment cannot settle

            without an award — and absent on a `pending` response, where no
            quote has been selected

            yet. Same value and meaning as on GET
            /v1/payments/{paymentId}/status.
          example: 0x9a3b...e8d4
        fulfillment_quote:
          type: object
          description: >
            The settler-signed FulfillmentQuote for the winning quote, returned
            verbatim as an

            opaque signed document — the PREIMAGE of the `quote_hash` committed
            on-chain. Same

            value, semantics and canonicalization caveats as on

            GET /v1/payments/{paymentId}/status; see that field for how to
            verify it.


            Present when `status` is `completed`. It may be absent even then, if
            the stored quote

            is unreadable: the award is reported through `quote_id` regardless,
            so a present

            `quote_id` with no `fulfillment_quote` means "verification preimage
            unavailable" and

            never "no quote was awarded". A consumer that requires the preimage
            should fall back to

            the status endpoint rather than treat the payment as unverified.
        fulfillment_confirmation:
          $ref: '#/components/schemas/FulfillmentConfirmation'
        error:
          $ref: '#/components/schemas/PaymentFailure'
    ErrorResponse:
      type: object
      description: >-
        Safe, outward-facing error. Derived from an internal error through the
        shared error catalog. The public code identifies the category and the
        message is a curated, safe description.


        A panic is caught by shared middleware that responds `{"error":
        "Internal server error occurred"}` instead, so a 500 is not guaranteed
        to carry this shape.
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: Public, machine-readable error category in SCREAMING_SNAKE_CASE.
          pattern: ^[A-Z][A-Z0-9_]*$
          example: INVALID_SOURCE_ASSET
        message:
          type: string
          description: Curated, safe, human-readable message.
          maxLength: 500
          example: The source asset identifier is not a valid CAIP-19 asset.
        request_id:
          type: string
          description: >-
            Correlation id to quote to support to locate the internal error
            record.
          example: req_123456789
        payment_id:
          type: string
          description: >-
            The payment this error was raised against. Absent when the error is
            not about a specific payment.
          example: '0xc7108e200d11580e7e75185991084b42a535af588ba8267cd0d1e7df089e5c9c'
        docs_url:
          type: string
          format: uri
          description: Link to the documentation page for this public code.
        domain:
          type: string
          description: Originating service domain.
          example: PAYMENT_GATEWAY
        retry_classification:
          type: string
          description: >-
            What the caller should do next. Absent means the condition has not
            been classified, which is not the same as safe to retry.

            Re-sending the same `request_id` is an idempotent replay and always
            safe. This value is about submitting a new one, which starts a new
            operation. See https://docs.atum.xyz/errors for the value set.
          example: RECONCILE_THEN_DECIDE
        request_id_reusable:
          type: boolean
          description: >-
            Whether the caller may re-send this same request_id. True means no
            payment was created for it, so the id is still free. False means a
            payment exists for it, so the id is spent and re-sending only
            replays this outcome. Absent means undetermined, which a caller
            should read as false. Separate from retry_classification, which
            answers what the caller must change rather than whether the id
            survived.
          example: false
    OwnedAsset:
      type: object
      description: >-
        Represents a cryptocurrency or token in a specific wallet. This tells
        the system what asset you're sending/receiving and which wallet to use.
      required:
        - asset_identifier
        - account
      properties:
        asset_identifier:
          type: string
          description: >-
            A unique identifier for the asset using CAIP-19 format.

            CAIP-19 is a standard way to identify blockchain assets:
            {chain_id}/{asset_namespace}:{asset_reference}

            Examples:

            - Ethereum USDC:
            "eip155:1/erc20:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"

            - Arbitrum USDC:
            "eip155:42161/erc20:0xaf88d065e77c8cC2239327C5EDb3A432268e5831"

            - Solana USDC:
            "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"

            - Tron USDT: "tron:mainnet/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
          pattern: >-
            ^[a-z0-9]+:[a-zA-Z0-9_-]+/[a-z0-9-]{3,8}:[-.%a-zA-Z0-9]{1,128}(/[-.%a-zA-Z0-9]{1,78})?$
          example: eip155:1/erc20:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
        account:
          type: string
          description: >-
            The wallet address that owns this asset.

            Format depends on the blockchain:

            - Ethereum/EVM: 0x-prefixed hex address (42 characters)

            - Solana: Base58 encoded address

            - Tron: T-prefixed Base58 address


            A destination account is checked at ingress and must be payable on
            the chain named by

            its `asset_identifier`: it parses for that chain, carries a valid
            EIP-55 checksum when

            mixed-case, has no surrounding whitespace, and is not the zero
            address. A receiver that

            fails any of these is refused with `INVALID_DESTINATION_ACCOUNT`,
            before any funds move.
          minLength: 1
          maxLength: 128
          example: '0x742D35cC6634c0532925a3b844Bc9e7595F41360'
    SenderAuth:
      type: object
      description: >-
        Authorization from the asset owner to execute the payment. The exact
        format depends on the source blockchain's authorization mechanism.
      required:
        - message_scheme
        - signed_messages
      properties:
        verifiable_credential:
          $ref: '#/components/schemas/VerifiableCredentialAuth'
          deprecated: true
          description: >-
            DEPRECATED: inner-document credential carrier. Credentials MUST ride
            ONLY in the holder-signed PaymentRequestEnvelope (PRE); the inner
            PaymentRequest carries no credential and its Data Integrity
            signature EXCLUDES this field (stripped before canonicalization on
            both producer and verifier). Populated only for one back-compat
            rollout cycle, then removed.
        message_scheme:
          $ref: '#/components/schemas/SenderAuthMessageScheme'
        signed_messages:
          type: array
          description: Array of signed messages (at least one required)
          minItems: 1
          items:
            $ref: '#/components/schemas/SignedMessage'
    VerifiableCredentialAuth:
      type: object
      description: >-
        Optional identity verification using cryptographic credentials.


        DEPRECATED. Credentials are moving to a holder-signed W3C
        VerifiablePresentation that

        travels alongside a request rather than inside it. That envelope is not
        part of this

        HTTP contract, so there is nothing here to move them to yet: treat this
        as a legacy

        carrier, and do not build a new integration on it.
      required:
        - verifiable_credential
        - verification_method_id
        - signature
      properties:
        verifiable_credential:
          $ref: '#/components/schemas/VerifiableCredential'
        verification_method_id:
          type: string
          description: Reference to verification method from the DID Document
        signature:
          type: string
          description: Cryptographic signature matching the verification method type
    PaymentStatus:
      type: string
      description: >
        Where your payment is in its lifecycle. One payment reads the same
        however you ask

        about it.


        - `pending`: accepted and in progress. Nothing has reached the recipient
        yet.

        - `finalizing`: the transaction paying your recipient is on chain and is
        accruing
          the confirmations its chain requires. NOT FINAL, see below.
        - `completed`: delivered, with the required confirmations behind it.

        - `failed`: did not complete. Any funds held in escrow are returned, and
        `error`
          describes what went wrong.

        `completed` and `failed` are final and will not change afterwards.
        `pending` and

        `finalizing` will.


        **Do not treat `finalizing` as delivered.** It says the payment has been
        made on

        chain but is not yet irreversible, much as a card authorization is not
        yet a

        settlement. It normally becomes `completed`, and it can still become
        `failed` if

        the transaction never reaches the confirmation depth its chain requires.
        Acting on

        `finalizing` — releasing goods, crediting an account — means accepting
        that risk

        deliberately, at whatever threshold suits you rather than the one we
        wait for.


        `finalizing` is about paying the recipient. While your own funds are
        still being

        committed on the source chain the payment is `pending`, because nothing
        has reached

        the recipient yet.
      enum:
        - pending
        - finalizing
        - completed
        - failed
      example: pending
    PaymentTransactions:
      type: object
      description: >
        The transactions a payment has produced, one entry per stage, added as
        each becomes

        known. A stage absent here has produced no transaction yet.


        These are the two movements that decide whether your payment happened:
        your funds

        being committed, and your recipient being paid. Present on every
        outcome, so a

        payment that did not complete can still be traced on chain.

        `fulfillment_confirmation` is not a substitute: it is written only once
        a payment

        completes, and describes the finished delivery rather than progress
        towards it.


        Where a stage is attempted more than once, the entry describes the
        attempt that

        counts — the one that succeeded, or the last one if none did.
      properties:
        deposit:
          allOf:
            - $ref: '#/components/schemas/PaymentTransaction'
          description: Committing your funds on the source chain.
        fulfillment:
          allOf:
            - $ref: '#/components/schemas/PaymentTransaction'
          description: Paying your recipient on the destination chain.
    FulfillmentConfirmation:
      type: object
      description: >
        Confirmation details after a payment has been successfully delivered.

        Contains proof of delivery and transaction details for both source and
        destination chains.
      required:
        - payment_id
        - request_id
        - source_chain_id
        - destination_chain_id
        - source_tx_hash
        - destination_tx_hash
      properties:
        payment_id:
          type: string
          description: |
            Unique identifier for this payment transaction.
            Use this to track the payment across all systems.
          example: '0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f'
        request_id:
          type: string
          description: >
            Your original request ID that was provided when creating the
            payment.

            Helps match confirmations to your internal systems.
          example: req_123456789
        fulfillment_timestamp:
          type: string
          format: date-time
          description: Timestamp when the fulfillment was completed
        source_chain_id:
          type: string
          description: >
            The blockchain where funds were locked in escrow, using CAIP-2
            format.

            CAIP-2 is a standard way to identify blockchains:
            {namespace}:{reference}

            Examples:

            - "eip155:1" for Ethereum mainnet

            - "eip155:42161" for Arbitrum

            - "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" for Solana

            - "tron:mainnet" for Tron
          example: eip155:1
        destination_chain_id:
          type: string
          description: >
            The blockchain where the payment was delivered, using CAIP-2 format.

            CAIP-2 is a standard way to identify blockchains:
            {namespace}:{reference}

            Examples:

            - "eip155:1" for Ethereum mainnet

            - "eip155:42161" for Arbitrum

            - "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" for Solana

            - "tron:mainnet" for Tron
          example: eip155:42161
        source_tx_hash:
          type: string
          description: Transaction hash of the escrow reservation on the source chain
          example: '0xa1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456'
        destination_tx_hash:
          type: string
          description: Transaction hash of the fulfillment on the destination chain
          example: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
        source_block_number:
          type: integer
          description: Block number where the source transaction was confirmed
        destination_block_number:
          type: integer
          description: Block number where the destination transaction was confirmed
    PaymentFailure:
      type: object
      description: >
        Why a payment failed. Present only when `status` is `failed`, and
        identical

        however you ask about the payment.


        `code` is the stable, machine-readable identity of the failure — branch
        on it

        rather than on `message`, which is written for a person and may be
        reworded.

        `docs_url` points at the page describing that code.
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: |
            Public error category in SCREAMING_SNAKE_CASE. Common codes:
            - `SETTLEMENT_FAILED`: the settlement could not be completed
            - `NO_QUOTES_RECEIVED`: no quotes arrived within the auction window
            - `INTERNAL_ERROR`: an unexpected error occurred
          example: SETTLEMENT_FAILED
        message:
          type: string
          description: >
            Human-readable description of the failure, for a person reading it.
            Do not

            branch on this text.
          example: No quotes received within the auction window
        domain:
          type: string
          description: |
            Which part of the system reported the failure.
          enum:
            - PAYMENT_GATEWAY
            - SETTLER_GATEWAY
            - SETTLER_AGENT
            - WALLET_ORCHESTRATION
            - VERIFICATION_SERVICE
            - BLOCKCHAIN
            - EXTERNAL_SERVICE
          example: PAYMENT_GATEWAY
        docs_url:
          type: string
          format: uri
          description: Link to the documentation page for this public code.
        retry_classification:
          type: string
          description: >-
            What the caller should do next. Absent means the condition has not
            been classified, which is not the same as safe to retry.

            Re-sending the same `request_id` is an idempotent replay and always
            safe. This value is about submitting a new one, which starts a new
            payment. See https://docs.atum.xyz/errors for the value set.
          example: RECONCILE_THEN_DECIDE
        request_id_reusable:
          type: boolean
          description: >-
            Whether the caller may re-send the `request_id` this payment was
            created under. On this shape it is almost always false, because a
            payment exists by definition, but it is carried rather than assumed
            so a caller reads the same field here as on a synchronous error.
            Absent means undetermined, which a caller should read as false.
          example: false
        blockchain_context:
          $ref: '#/components/schemas/PaymentBlockchainContext'
    SenderAuthMessageScheme:
      type: string
      description: |-
        The signature method used for authorizing this payment:
        - `EVM_PERMIT2`: Gasless token approvals for Ethereum/EVM chains
        - `EVM_PERMIT2_ESCROW`: Gasless approvals with escrow support
        - `SOLANA`: Standard Solana transaction signatures
        - `TRON`: Tron transaction signatures
      enum:
        - EVM_PERMIT2
        - EVM_PERMIT2_ESCROW
        - SOLANA
        - TRON
    SignedMessage:
      type: object
      description: >-
        A message that has been cryptographically signed to prove authorization.
        The signature proves you control the wallet that's sending funds.
      required:
        - message
        - signature
      properties:
        message:
          type: string
          description: |-
            The data that was signed. Format depends on the blockchain:
            - EVM: Hex-encoded message hash (with 0x prefix)
            - Solana: Hex-encoded message hash (with 0x prefix)
            - Tron: Hex-encoded message hash (possibly without 0x prefix)
          example: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
        message_prehash:
          type: string
          description: >-
            Optional: If 'message' contains a hash, this field contains the
            original data before it was hashed. Useful for verification and
            debugging.
          example: >-
            {"permit":{"token":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","amount":"1000000"}}
        signature:
          type: string
          description: >-
            The cryptographic signature proving you authorized this message.
            Hex-encoded with a 0x prefix on every supported chain. The signer's
            own public key is base58 on Solana, but the signature itself is not.
          example: >-
            0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab1c
        payload:
          type: object
          description: >-
            Optional chain-specific payload containing additional signing
            context (e.g., delegate_signer for Solana). Preserved through the
            pipeline for downstream consumers.
          additionalProperties: true
          example:
            delegate_signer: 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin
    VerifiableCredential:
      type: object
      description: >-
        W3C Verifiable Credential structure conforming to the W3C Verifiable
        Credentials Data Model v1.1
      required:
        - '@context'
        - id
        - type
        - issuer
        - issuanceDate
        - credentialSubject
        - proof
      properties:
        '@context':
          type: array
          description: >-
            JSON-LD context array, must include
            'https://www.w3.org/2018/credentials/v1'
          items:
            type: string
          example:
            - https://www.w3.org/2018/credentials/v1
        id:
          type: string
          description: Unique identifier for this credential
        type:
          type: array
          description: Credential types, must include 'VerifiableCredential'
          items:
            type: string
          example:
            - VerifiableCredential
            - KYCVerifiedCredential
        issuer:
          oneOf:
            - type: string
            - type: object
          description: DID or issuer object of the credential issuer
        issuanceDate:
          type: string
          format: date-time
          description: When the credential was issued
        expirationDate:
          type: string
          format: date-time
          description: When the credential expires
        credentialSubject:
          type: object
          description: Claims about the credential subject (structure varies by issuer)
          additionalProperties: true
        credentialStatus:
          type: object
          description: >-
            Information for credential revocation checking. Not consulted by the
            verifier today.
          additionalProperties: true
        proof:
          type: object
          description: Cryptographic proof of credential integrity
          additionalProperties: true
    PaymentTransaction:
      type: object
      description: >
        One on-chain transaction belonging to a payment: which chain, which
        transaction, and

        which block it landed in.


        Whether the payment has actually happened is answered by `status`, not
        here. Read this

        to look the transaction up on chain and to verify it yourself.
      required:
        - transaction_hash
        - chain_id
      properties:
        transaction_hash:
          type: string
          description: >
            The transaction's hash on its chain. Hex hashes carry the `0x`
            prefix, including on

            Tron, whose own tooling omits it.
          pattern: ^(0x[a-fA-F0-9]{64}|[1-9A-HJ-NP-Za-km-z]{32,88})$
          example: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
        chain_id:
          type: string
          description: CAIP-2 identifier of the chain the transaction is on.
          pattern: ^[a-z0-9]+:[a-zA-Z0-9_-]+$
          example: eip155:42161
        block_number:
          type: integer
          description: >
            The block the transaction landed in. Absent until it has been mined.


            Provisional while `status` is `finalizing`: on a chain that settles
            against another,

            a reorganization there can move the transaction to a different
            block. Settled once

            `status` is `completed` — keep it as a final record only from then.
          example: 61234567
    PaymentBlockchainContext:
      type: object
      description: >-
        Where on chain a failed payment failed. Present only when the failure
        happened

        on chain — an escrow revert, or a fulfillment that could not be
        delivered — so a

        payment that failed before any transaction was sent carries no context
        here.
      properties:
        chain_id:
          type: string
          description: CAIP-2 chain identifier
          pattern: ^[a-z0-9]+:[a-zA-Z0-9_-]+$
        transaction_hash:
          type: string
          description: Failed transaction hash
          pattern: ^(0x[a-fA-F0-9]{64}|[1-9A-HJ-NP-Za-km-z]{32,88})$
        block_number:
          type: integer
          description: Block number where error occurred
        contract_address:
          type: string
          description: Contract that caused the error
          pattern: ^(0x[a-fA-F0-9]{40}|[1-9A-HJ-NP-Za-km-z]{32,44})$
        revert_reason:
          type: string
          description: Smart contract revert reason
        gas_used:
          type: string
          description: Gas consumed before failure
        estimated_gas_needed:
          type: string
          description: Estimated gas needed for success

````