Skip to main content
POST
Submit a payment request
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.

Headers

X-Atum-Producer
string

Optional. Names the client library that built the request body, such as sdk-ts, sdk-go, x402 or mppx. Used for compatibility diagnostics only; it never changes how the payment is processed.

X-Atum-Deposit-Request-Hash-V2
string

Optional. The depositRequestHash version 2 the client computed over the body it posts, as 0x and 64 lowercase hex digits. The gateway checks it against its own reading of the body for compatibility diagnostics only; it is never trusted and never changes how the payment is processed. A client sends X-Atum-Producer without it when its reading refuses the body, which a version 2 deposit could not pay.

Query Parameters

auto_accept
boolean
default:false

Award the first valid quote at or under max_source_amount instead of waiting for quote_deadline; faster, but gives up competitive pricing. Requires max_source_amount.

Body

application/json

Structured payment request with sender authorization and optional W3C Verifiable Credentials

version
string
required

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
string
required

Unique identifier for this payment request (used for idempotency)

Example:

"req_123456789"

source
object
required

Represents a cryptocurrency or token in a specific wallet. This tells the system what asset you're sending/receiving and which wallet to use.

destination
object[]
required

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.

Required array length: 1 - 16 elements
fulfillment_amount
string
required

The exact amount to be received at the destination (in the smallest unit, e.g., wei for ETH, cents for USD).

Example:

"1000000"

quote_deadline
string<date-time>
required

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.

Keep this window short. A settler holds its quoted price for a limited period of its own choosing, and the gateway selects only once the window has closed, so a deadline set beyond that hold leaves no quote still awardable and the request is declined with QUOTE_WINDOW_TOO_LONG. That hold is set by each settler and is not part of this contract, so treat 60 seconds as a practical upper bound and prefer a shorter window where your integration allows. A submit with auto_accept can be awarded before the window closes, but settlers still decline a window longer than their hold, so keep it short either way.

Example:

"2024-12-25T10:30:00Z"

fulfillment_deadline
string<date-time>
required

Required. The time by which the payout to the recipient must be mined on the destination chain. If no payout is mined by then, your escrowed funds are refunded. The payout does not have to be final by then: completed follows once the destination chain judges it final. Leave room for your deposit to confirm on the source chain first, which on some chains takes minutes. 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"

sender_auth
object
required

Authorization from the asset owner to execute the payment. The exact format depends on the source blockchain's authorization mechanism.

fulfillment_verifier
object
required

The verification service that confirms the payment was delivered before the escrow releases the source funds, and the account it signs with. Required on every chain: the gateway's /v1/defaults response supplies it for each chain.

escrow_contract_address
string
required

Escrow contract on the source chain that holds the source funds until proof of fulfillment has been verified. Required on every chain. Take the value for the source chain from the payment gateway's GET /v1/defaults (escrow_contract).

Example:

"0x742D35cC6634c0532925a3b844Bc9e7595F41360"

max_source_amount
string

The maximum amount willing to be spent from the source (in the smallest unit).

Example:

"1000000"

executor

Who performs each leg of the payment. Omitted on the wire means settler for every leg. The split object keeps deposit, reserve, and fulfill as separate keys.

Available options:
settler,
originator
deposit_nonce
string

The nonce the payer passed to the escrow's directDeposit, as a decimal uint256. Only on an originator-executed deposit (executor.deposit is originator), where it is required: with the source account, chain and escrow it names the deposit the payer funded. Each nonce funds one deposit per account, forever.

Required string length: 1 - 78
Pattern: ^[0-9]+$
settler_auth_required
object

Policy requirements for settler authentication. Keys are policy names, values indicate if the policy is required.

originator_auth
object

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.

quote_selector
string

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_proxy
string

Fulfillment Proxy contract address, used to emit proof of fulfillment on the destination chain. Leave empty to use the default.

Example:

"0x892BB2e4F6b14a2B5b82Ba8d33E5925D42D4431F"

settler_requirements
object

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

Response

Payment request submitted successfully

Response from submitting a payment request. If settlement completes inside the gateway's synchronous window, the response is completed and transactions carries both the deposit and the payout. The window also ends when the payment reaches finalizing, so the response normally carries the payout transaction (transactions.fulfillment) within seconds. 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, the deposit and payout in transactions, 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 transactions.deposit and transactions.fulfillment: either one missing from a completed payment is a gateway fault, to report with the payment_id. failed is accompanied by error where a reason has been recorded.

payment_id
string
required

Unique ID for tracking this payment. Save this to check status later.

Example:

"0x761989729fefb4b0cb8b3e6f787301a3878c91e8fba35ed362a079292879f34f"

status
enum<string>
required

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, but its chain has not yet judged it final. NOT FINAL, see below.
  • completed: delivered and final on the destination chain.
  • failed: did not complete. Any funds held in escrow are returned, and error describes what went wrong.
  • refunding: the settler will not deliver this payment and your funds are being returned from escrow. NOT FINAL: it becomes refund_available or refunded, and it can still become completed or failed.
  • refund_available: the settler did not deliver and the refund is authorized for you to submit. GET /v1/payments/{paymentId}/status returns refund_signature, which you submit to the source escrow to return your funds. The other responses report this status without the signature. NOT FINAL: the funds are still in escrow, so it becomes refunded once the refund lands, or completed if the settler delivers late and is paid first. Do not pay again while it is refund_available.
  • refunded: your funds were returned from the source escrow. transactions.refund is the refund transaction, when it is known.

completed, failed and refunded are final and will not change afterwards. pending, finalizing, refunding and refund_available 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, after seconds on some chains and minutes on others. If a chain reorganization drops the transaction before it is final, your funds are refunded instead. 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.

Available options:
pending,
finalizing,
completed,
failed,
refunding,
refund_available,
refunded
Example:

"pending"

idempotent_replay
boolean
required

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

transactions
object

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 movements that decide whether your payment happened: your funds being committed, your recipient being paid, and your funds being returned when the payment is refunded. Present on every outcome, so a payment that did not complete can still be traced on chain.

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.

quote_id
string

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
object

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_authorization
object

The settler-signed FulfillmentAuthorization for this payment, returned verbatim as an opaque signed document (structurally the canonical FulfillmentAuthorization/v1 declaration at https://schemas.atum.xyz/declarations/FulfillmentAuthorization/v1; see also #/components/schemas/FulfillmentAuthorization). The inner declaration requires version, scheme, authorization (the signed EIP-3009 transfer, in authorization.message_prehash), fulfillment_quote_json, and evidence (the on-chain authorizeFulfillment transaction). On the wire the settler typically wraps that declaration in JSON-LD with @context, type, and proof — those envelope fields ride alongside the declaration and MUST be preserved byte-for-byte.

Present when the settler authorized an originator fulfill. Until the originator fulfills, transactions.fulfillment is absent.

The service returns these bytes verbatim and does not re-serialize through a typed representation (which would normalize timestamps and reorder JSON-LD envelope fields).

error
object

Why a payment failed, or why its settler gave up. As error it is present only when status is failed, as refund_reason only on the refund statuses, and it is 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.

refund_reason
object

Why the settler did not deliver, as it reported when it gave up. Present only when status is refunding, refund_available or refunded, and absent when the refund started without a reason from the settler. status decides what to do next: its retry_classification and request_id_reusable describe the settler's error, not this payment, and never mean paying again while it is refunding or refund_available.

requested_count
integer

How many settler agents this payment's auction was broadcast to. Accompanies decline_summary and is present under the same condition. Same meaning and same absence rule as on GET /v1/payments/{paymentId}/status: absent means unknown, never zero.

Example:

8

decline_summary
object[]

Why the payment drew few or no quotes, carried on the submission response so that a failed payment explains itself here rather than requiring a follow-up read. A payment that ran out its quote deadline reports NO_QUOTES_RECEIVED on error; this is what the settlers actually said.

Present when status is failed, alongside error. A submission rejected before its auction opened is answered as an error response rather than this type and carries no breakdown, and GET /v1/payments/{paymentId}/status reports the breakdown in every state.

Same counting rules as GET /v1/payments/{paymentId}/status: every declining agent is counted exactly once under its most recent reason, empty means no agent declined, absent means the breakdown could not be read. The free-form decline detail is never relayed.