> ## 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.

# Create a quote-request

> Open a quote-request batch and collect price quotes for a corridor, without committing to a payment.



## OpenAPI

````yaml POST /v1/payments/quote-requests
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/quote-requests:
    post:
      tags:
        - Quote Requests
      summary: Create a quote-request batch
      description: >
        Opens a quote-request batch: broadcasts the corridor to settlement
        agents and collects

        their signed quotes. This is the first phase of the two-phase quote →
        award flow — the

        originator later picks one quote and commits in a separate POST
        /v1/payments/awards call.

        No spend authorization is supplied here; `sender_auth` is deferred to
        award time.


        Wait modes (the `wait` query parameter):

        - `wait=false` (default): return 202 immediately with status
        `collecting`; poll
          GET /v1/payments/quote-requests/{quoteRequestId} until status is `ready`.
        - `wait=true`: hold the connection for up to
          min(quote_deadline, quote_sync_wait_seconds) and return 200 with the collected quotes
          (status `ready`). If the sync cap fires before the quote deadline, return 200 with
          status `collecting` and poll the status endpoint.

        Collection continues server-side until the quote deadline regardless of
        the HTTP

        connection, so quotes remain retrievable via the status endpoint in
        either mode.


        **Idempotency.** `request_id` is the idempotency key, scoped to the
        source account

        and originator — the same key, and the same contract, as POST
        /v1/payments.

        Submitting the same `request_id` again returns the ORIGINAL batch
        instead of opening

        a second one, so a client whose request timed out or failed in transit
        can safely

        re-submit and recover its `quote_request_id`.


        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 `proof` that covers them. An expired `quote_deadline` on a

        re-submission is NOT an error — the request is resolved onto the batch
        that already

        exists rather than used to open one. There is no `sender_auth` to
        re-sign here: this

        endpoint rejects it, because the spend authorization belongs to the
        award call.


        The collection window belongs to the BATCH, not to the request. Asking
        again with a

        later `quote_deadline` does not extend a window already open;
        `quote_deadline` in the

        response is the batch's own, and `status` says whether collection is
        still running.


        What must NOT differ is the corridor's economics — source account and
        asset,

        destination accounts and assets, `fulfillment_amount`, and
        `max_source_amount`.

        Re-using one `request_id` for a genuinely different corridor is a client
        bug that

        would otherwise be silently collapsed onto the first batch, returning
        quotes for a

        corridor that was never requested. It is rejected with `409` and

        `IDEMPOTENCY_TERMS_MISMATCH`. Use a distinct `request_id` per
        quote-request.
      operationId: createQuoteRequest
      parameters:
        - name: wait
          in: query
          required: false
          description: >-
            Return a handle immediately (`false`, default) or hold the
            connection for collected quotes (`true`).
          schema:
            type: boolean
            default: false
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateQuoteRequest'
      responses:
        '200':
          description: >
            Quote-request resolved synchronously. `status` is `ready` when
            quotes were collected,

            or `collecting` when the sync wait cap fired before the quote
            deadline.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QuoteRequestResponse'
        '202':
          description: Quote-request accepted; collection continues asynchronously.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QuoteRequestAccepted'
        '400':
          description: >
            Invalid request. `QUOTE_DEADLINE_NOT_FUTURE` is the one worth
            planning for: a NEW

            quote-request whose `quote_deadline` has already passed is refused,
            because there is

            no window left to collect quotes in. It does not apply to a
            re-submission — an

            already-used `request_id` resolves onto the batch that exists and
            its expired

            deadline is ignored, which is what makes a retry recoverable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >
            The `request_id` has already been used for a quote-request with
            different

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

            retry is made safe, so it must identify one batch; use a distinct
            `request_id`

            per quote-request.
          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:
    CreateQuoteRequest:
      type: object
      description: >
        Request body for opening a quote-request batch. Structurally the
        existing PaymentRequest

        with `sender_auth` omitted (the spend authorization is deferred to the
        award call); see

        schemas/declarations/PaymentRequest/v1/schema.json.
      required:
        - version
        - request_id
        - source
        - destination
        - fulfillment_amount
        - quote_deadline
        - fulfillment_deadline
      properties:
        version:
          type: string
          description: >
            Semver-shaped declaration version (MAJOR.MINOR), locked to major 1
            for v1. Producers emit

            the canonical value exported by the bindings' Version constant.
          pattern: ^1\.[0-9]+$
          example: '1.0'
        request_id:
          type: string
          description: >
            Client-supplied idempotency key for this quote-request. The same
            request_id from the

            same originator returns the same quote_request_id. Independent of
            the award-call request_id.
          example: qr-2026-04-29-001
        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 (smallest unit,
            e.g. wei or cents).
          example: '100000000'
        max_source_amount:
          type: string
          description: >
            Optional cap on what the originator is willing to spend from source
            (smallest unit).

            Quotes with source_amount above this are filtered. No cap applied if
            omitted.
          example: '101000000'
        quote_deadline:
          type: string
          format: date-time
          description: >
            Required. End of the collection window. After this point no new
            quotes are accepted.

            The gateway injects no default and never mutates the
            originator-supplied body, so the

            value is carried through verbatim to the award call. It must be
            supplied and must be in

            the future. Must be strictly earlier than fulfillment_deadline.
          example: '2026-04-29T12:00:03Z'
        fulfillment_deadline:
          type: string
          format: date-time
          description: >
            Required. Final deadline for completing the payment. Past it the
            payment fails and

            eligible for refund. The gateway injects no default and never
            mutates the

            originator-supplied body, so the value is carried through verbatim
            to the award call. It

            must be supplied and must be in the future. Must be strictly later
            than quote_deadline.
          example: '2026-04-29T12:30:00Z'
        settler_auth_required:
          type: object
          description: >-
            Policy requirements for settler authentication. Keys are policy
            names, values indicate if required.
          additionalProperties:
            type: boolean
        originator_auth:
          $ref: '#/components/schemas/VerifiableCredentialAuth'
        quote_selector:
          type: string
          description: >
            Wallet address or public key of a trusted service that auto-selects
            the best rate.

            Leave empty to use the default selector.
          example: '0x742D35cC6634c0532925a3b844Bc9e7595F41360'
        fulfillment_verifier:
          type: object
          description: >-
            Optional third-party service that independently verifies delivery.
            Leave empty for 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: Optional escrow contract address. Leave empty to use the default.
          example: '0x742D35cC6634c0532925a3b844Bc9e7595F41360'
        fulfillment_proxy:
          type: string
          description: >-
            Optional fulfillment-proxy contract address on the destination
            chain. Leave empty to use the default.
          example: '0x892BB2e4F6b14a2B5b82Ba8d33E5925D42D4431F'
        settler_requirements:
          type: object
          description: >
            Optional settler filtering. 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 is rejected).
          properties:
            whitelist:
              type: array
              description: >-
                Settler DIDs permitted to fulfill this request (W3C DID format).
                If present, only these may win.
              minItems: 1
              maxItems: 100
              uniqueItems: true
              items:
                type: string
                pattern: ^did:[a-z][a-z0-9]*:[a-zA-Z0-9._:%-]+$
              example:
                - did:pkh:eip155:1:0x742D35cC6634c0532925a3b844Bc9e7595F41360
            blacklist:
              type: array
              description: >-
                Settler DIDs excluded from fulfilling this request (W3C DID
                format).
              minItems: 1
              maxItems: 100
              uniqueItems: true
              items:
                type: string
                pattern: ^did:[a-z][a-z0-9]*:[a-zA-Z0-9._:%-]+$
              example:
                - did:pkh:eip155:1:0x892bb2E4F6b14a2B5b82bA8D33e5925d42d4432f
        '@context':
          type: array
          description: >
            Optional JSON-LD context URLs — present when the originator wraps
            the request in a W3C

            Data Integrity envelope so a downstream verifier can recover the
            signer identity.
          items:
            type: string
        proof:
          $ref: '#/components/schemas/DataIntegrityProof'
        verifiableCredential:
          type: array
          description: >-
            Optional document-level W3C Verifiable Credentials (e.g. KnownToAtum
            about the request producer).
          items:
            $ref: '#/components/schemas/VerifiableCredential'
    QuoteRequestResponse:
      type: object
      description: >
        Synchronous response to POST /v1/payments/quote-requests. Self-contained
        — the corridor

        fields are echoed at the response root so a drop-recovery client can
        build the award call

        without retaining the original request body.


        The corridor echoed here describes the batch you actually receive. On an
        idempotent

        replay that is the ORIGINAL batch's corridor and window, which may
        differ from what you

        just sent in the ways a retry is allowed to differ (the deadlines, and
        address casing).
      required:
        - quote_request_id
        - status
        - idempotent_replay
      properties:
        quote_request_id:
          type: string
          description: >-
            Gateway-generated resource ID for the batch, used in subsequent
            status reads and awards.
          example: qr_a1b2c3d4-5678-90ef-1234-567890abcdef
        status:
          type: string
          enum:
            - collecting
            - ready
          description: >
            Collection-phase outcome of the create call:

            - `ready` — collection complete; `quotes` populated. If no settler
            bid, this is still
              `ready` with an empty `quotes` array (a successful "no quotes available" result —
              distinct from `expired`, which only applies to collected quotes that later lapse).
            - `collecting` — the sync wait cap fired before the quote deadline;
            poll the status endpoint for quotes.

            The `awarded` and `expired` lifecycle states are observed only via
            the status endpoint, not here.
          example: ready
        idempotent_replay:
          type: boolean
          description: >
            Whether this response describes a batch that already existed, rather
            than one opened

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

            handed the ORIGINAL batch — no second batch was opened and no second
            broadcast went

            out to settlement agents.


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

            batch back. You may re-send the original bytes unchanged:
            `quote_deadline` bounds the

            collection window and is short, so it will usually have expired by
            the time you

            retry, and an expired deadline on a re-submission is not an error —
            the request is

            resolved onto the batch that already exists rather than used to open
            one.

            Re-signing with fresh deadlines is equally fine and returns the same
            batch.


            The window is NOT extended either way: it belongs to the batch, and
            `quote_deadline`

            in this response is the batch's own. Read `status` to see whether
            collection is

            still running.


            What you must not change under one `request_id` is the corridor's
            economics —

            accounts, assets, or amounts — which are rejected with 409

            IDEMPOTENCY_TERMS_MISMATCH rather than resolved onto the first
            batch. A genuinely

            different corridor needs a new `request_id`.


            Present on every response, `false` for a newly opened batch. A
            response without the

            field is from a gateway predating it, not a newly opened batch.
        source:
          $ref: '#/components/schemas/OwnedAsset'
        destination:
          type: array
          items:
            $ref: '#/components/schemas/OwnedAsset'
        fulfillment_amount:
          type: string
          example: '100000000'
        max_source_amount:
          type: string
          example: '101000000'
        fulfillment_deadline:
          type: string
          format: date-time
          example: '2026-04-29T12:30:00Z'
        quote_deadline:
          type: string
          format: date-time
          example: '2026-04-29T12:00:03Z'
        quotes:
          type: array
          description: >-
            Collected quotes. Present when status is `ready` — an empty array if
            no settler bid.
          items:
            $ref: '#/components/schemas/Quote'
        requested_count:
          type: integer
          description: >
            How many agents the request was broadcast to, so silence is
            distinguishable from a

            decline without a second call. Same meaning and same absence rule as
            on the status

            endpoint: absent means unknown, never zero.
          example: 8
        decline_summary:
          type: array
          description: >
            Why the request drew few or no quotes, carried here so that a
            `ready` response with an

            empty `quotes` array explains itself rather than requiring a
            follow-up read.


            Same counting rules as the status endpoint: every declining agent
            counted exactly once

            under its most recent reason, empty when nobody declined, absent
            when the detail could not

            be read. Provisional while `status` is `collecting`, because agents
            are still responding.
          items:
            $ref: '#/components/schemas/DeclineSummaryEntry'
    QuoteRequestAccepted:
      type: object
      description: >
        Asynchronous (202) response to POST /v1/payments/quote-requests. Returns
        the batch handle

        plus the echoed corridor; quotes are retrieved via the status endpoint.


        The corridor echoed here describes the batch you actually receive. On an
        idempotent

        replay that is the ORIGINAL batch's corridor and window, which may
        differ from what you

        just sent in the ways a retry is allowed to differ (the deadlines, and
        address casing).
      required:
        - quote_request_id
        - status
        - idempotent_replay
      properties:
        quote_request_id:
          type: string
          example: qr_a1b2c3d4-5678-90ef-1234-567890abcdef
        status:
          type: string
          enum:
            - collecting
          description: >
            Always `collecting` — a constant on this response, not a report of
            the batch's state.

            On an idempotent replay the batch's window may already have closed;
            read

            GET /v1/payments/quote-requests/{quoteRequestId} for its actual
            state.
          example: collecting
        idempotent_replay:
          type: boolean
          description: >
            Whether this response describes a batch that already existed, rather
            than one opened

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

            handed the ORIGINAL batch — no second batch was opened and no second
            broadcast went

            out to settlement agents.


            The idempotency contract is the same as on the synchronous (200)
            response: a

            re-submission may carry expired deadlines and is resolved onto the
            existing batch

            rather than rejected, the collection window is the batch's own and
            is not extended,

            and differing economics are rejected with 409
            IDEMPOTENCY_TERMS_MISMATCH rather than

            collapsed onto the first batch.


            Present on every response, `false` for a newly opened batch. A
            response without the

            field is from a gateway predating it, not a newly opened batch.
        source:
          $ref: '#/components/schemas/OwnedAsset'
        destination:
          type: array
          items:
            $ref: '#/components/schemas/OwnedAsset'
        fulfillment_amount:
          type: string
          example: '100000000'
        max_source_amount:
          type: string
          example: '101000000'
        fulfillment_deadline:
          type: string
          format: date-time
          example: '2026-04-29T12:30:00Z'
        quote_deadline:
          type: string
          format: date-time
          example: '2026-04-29T12:00:03Z'
    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'
    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
    DataIntegrityProof:
      type: object
      description: >-
        W3C Data Integrity proof conforming to the Data Integrity 1.0
        specification. Which of jws or proofValue is required depends on the
        cryptosuite named in type, and that pairing is checked where the proof
        is verified rather than by this schema, so a document can satisfy the
        schema and still be refused at verification.
      required:
        - type
        - created
        - verificationMethod
        - proofPurpose
      properties:
        type:
          type: string
          description: >-
            Proof type identifier. ECDSA-secp256k1 (recoverable, EVM-compatible)
            for did:pkh:eip155:* signers; Ed25519 for did:pkh:solana:* signers.
          example: EcdsaSecp256k1RecoverySignature2020
        created:
          type: string
          format: date-time
          description: >-
            ISO 8601 timestamp when the proof was created. Canonicalize the
            value exactly as it appears in the raw JSON: parsing it into a
            date-time and re-serializing can change the spelling (Z versus
            +00:00), and a different spelling canonicalizes to different bytes,
            so the signature no longer verifies.
          example: '2026-04-29T12:00:00Z'
        verificationMethod:
          type: string
          description: >-
            DID URL identifying the verification method, e.g.,
            'did:pkh:eip155:1:0x...#blockchainAccountId' for ECDSA or
            'did:pkh:solana:<chain>:<addr>#SolanaMethod2021' for Ed25519.
          example: did:pkh:eip155:42161:0xabc...#blockchainAccountId
        proofPurpose:
          type: string
          description: Purpose of the proof (e.g., assertionMethod, authentication).
          example: assertionMethod
        jws:
          type: string
          description: >-
            Detached JWS signature in <base64url(header)>..<base64url(sig)>
            form, header {alg:ES256K-R, b64:false, crit:[b64]}. Required at
            runtime for EcdsaSecp256k1RecoverySignature2020.
        proofValue:
          type: string
          description: >-
            Multibase-encoded raw signature bytes. Required at runtime for
            Ed25519Signature2020; mutually exclusive with jws in practice.
    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
    Quote:
      type: object
      description: >-
        One collected quote — the content-hash identifier plus the verbatim
        settler-signed FulfillmentQuote.
      required:
        - quote_id
        - fulfillment_quote
      properties:
        quote_id:
          type: string
          description: >
            Canonical content-hash of the signed quote bytes; identifies the
            quote server-side at

            award time. Provided as a convenience — the gateway re-derives it
            from

            bid_award.fulfillment_quote_json.
          example: 0x4e7f...c2a1
        fulfillment_quote:
          type: object
          description: >
            The settler-signed FulfillmentQuote (structurally the canonical
            FulfillmentQuote/v1

            declaration at
            https://schemas.atum.xyz/declarations/FulfillmentQuote/v1), returned

            verbatim as an opaque signed document. A client recomputes
            quote_hash over the returned

            bytes and verifies the settler signature, then re-embeds them into

            bid_award.fulfillment_quote_json at award. quote_hash is
            canonicalization-based

            (URDNA2015), so it is stable across JSON key-order/whitespace
            normalization. The

            returned bytes are hash-equivalent to, but not necessarily
            byte-identical with, what the

            settler emitted (quotes are stored as JSONB, which normalizes on
            write). The gateway

            still never re-serializes the quote through a typed struct (that
            renormalizes timestamp

            values and WOULD change quote_hash), so the Go binding exposes it as
            a raw JSON passthrough.
    DeclineSummaryEntry:
      type: object
      required:
        - reason
        - count
      properties:
        reason:
          $ref: '#/components/schemas/DeclineReason'
        count:
          type: integer
          description: >
            Agents whose most recent decline reason was this one. Counts agents,
            never rows, and an

            agent appears under one reason only.
          example: 4
        retry_classification:
          type: string
          description: >
            What would have to change for a settler giving this reason to submit
            a price quote,

            drawn from the same closed set as the `retry_classification` on an
            error response so

            a caller reads one vocabulary.


            It describes the decline only. The authoritative instruction for the
            payment is the

            `retry_classification` on its own error response: a payment can fail
            for a reason no

            settler named, and it can succeed while some settlers declined.


            Optional, and a pattern-constrained string for the same reason
            `DeclineReason` is.
          pattern: ^[A-Z][A-Z0-9_]*$
          example: RETRY_AFTER_DELAY
        docs_url:
          type: string
          format: uri
          description: >
            Link to the documentation for this reason. Absent rather than empty
            when no page is

            published, so a reader never follows a link to the site root.
          example: https://docs.atum.xyz/decline-reasons#insufficient-liquidity
    DeclineReason:
      type: string
      description: >
        A settler's reason for not bidding, projected from its internal reason
        code through

        the shared decline catalog (`apis/decline/v1/catalog.json`), which is
        the single

        source of truth for the reason set. Several internal codes may map to
        one public

        reason.


        Deliberately a pattern-constrained string rather than a closed enum. The
        set grows

        whenever a settler gains a cause worth naming, and a consumer whose
        generated client

        closes the set rejects the whole response on a value it has not seen.
        Treat an

        unrecognised value as `OTHER`.


        `OTHER` covers a reason whose detail is settler-authored free text, and
        any reason

        this gateway does not recognise. Both are counted so the summary stays
        exhaustive,

        without relaying text we neither author nor validate.
      pattern: ^[A-Z][A-Z0-9_]*$
      example: INSUFFICIENT_LIQUIDITY

````