Documentation Index
Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
Use this file to discover all available pages before exploring further.
What We’re Building
A server-side payment flow using Dynamic’s Checkout API. The Checkout API lets you accept a crypto payment from any supported chain and have it settled in a specific token on a specific chain. By the end of this guide you will be able to:- Create a checkout configuration and reuse its
checkoutIdwhen settlement and destinations stay the same - Walk through the full payment flow: create transaction, attach source, get quote, sign, broadcast
- Poll or receive webhooks for settlement completion
- Handle errors and edge cases
fetch.
Prerequisites
- A Dynamic environment with the checkout feature enabled
- An environment API token (
dyn_...) from Developer > API Tokens in the dashboard - A connected wallet that can sign transactions on the chains you support. If your app doesn’t already handle wallet connection, use the Dynamic JavaScript SDK to connect a wallet and sign
- Node.js 18+ (or any runtime with
fetch)
Base URL
Overview
The checkout flow is a state machine with eight sequential steps. The same eight steps are used in both payment and deposit modes — only the checkout config (Step 1) and how the amount is interpreted (Step 2) change between them. This guide walks through both flows end-to-end:409.
Jump to the flow that matches your use case:
- Payment flow — receiver fixes the amount
- Deposit flow — sender chooses the amount
Authentication
| Context | Header |
|---|---|
| Checkout management (create/update/delete) | Authorization: Bearer dyn_... |
| Transaction creation | None required (JWT optional) |
| Transaction mutations (source, quote, prepare, broadcast, cancel) | x-dynamic-checkout-session-token: dct_... |
| Transaction reads (polling) | None required |
Choose Your Flow
This guide covers two checkout modes side by side. The eight steps are the same in both flows — what changes is the checkout config you create in Step 1 and how the amount is interpreted in Step 2. Pick the flow that matches your use case:- Payment flow —
mode: "payment". The receiver fixes the amount on each transaction (e.g., a $25 invoice). Use for invoices, e-commerce checkouts, or paid services. - Deposit flow —
mode: "deposit". The sender chooses how much to send (e.g., a $100 top-up). Use for funding flows, on-ramps, or open-ended deposits.
Shared concepts
These apply to both flows:| Concept | Description |
|---|---|
settlementConfig.strategy | How the best quote is selected: "cheapest", "fastest", or "preferred_order" |
settlementConfig.settlements | Token/chain pairs you want to receive. Each needs a matching destination |
destinationConfig.destinations | Wallet addresses where funds land. chainName must match a settlement entry |
enableOrchestration | Optional. Default true. When false, skips cross-chain settlement orchestration |
| Strategy | Behavior |
|---|---|
"cheapest" | Selects the route with the lowest total cost (gas + fees) |
"fastest" | Selects the route with the fewest steps |
"preferred_order" | Returns the first available quote in the order settlements are listed |
GET, PATCH, and DELETE on /environments/{environmentId}/checkouts/{checkoutId}.
Payment Flow
A complete walkthrough formode: "payment" checkouts, where the receiver fixes the amount the sender must pay.
Step 1 (Payment): Create a Payment Checkout
A checkout is a reusable payment configuration: it defines what token(s) you want to receive and the destination wallet(s). Reuse the samecheckoutId for every transaction when that configuration does not change.
id. Save the id — you’ll use it as checkoutId in Step 2.
Step 2 (Payment): Create a Transaction
Start a new payment with the amount the receiver wants to collect. This returns a session token that authenticates all subsequent calls.| Field | Description |
|---|---|
amount | The amount the receiver collects, as a string (e.g., "25.00") |
currency | Fiat currency code the amount is denominated in (e.g., "USD") |
memo | Optional. Arbitrary JSON metadata for your own correlation |
expiresIn | Optional. TTL in seconds. Default: 3600 (1 hour) |
destinationAddresses | Optional. Override the checkout’s destination for this transaction |
Step 3 (Payment): Attach Source
Declare which wallet and chain the payer is paying from. After this, risk screening runs asynchronously.| Field | Description |
|---|---|
sourceType | "wallet" or "exchange" |
fromAddress | Source wallet address. Required for wallet type |
fromChainId | Chain ID (e.g., "1" = Ethereum, "8453" = Base). Required for wallet type |
fromChainName | Chain family: "EVM", "SOL", "BTC", "SUI". Required for wallet type |
executionState: "source_attached".
Error (403): Blocked by sanctions screening — cancel and retry with a different source.
Step 4 (Payment): Get a Quote
Specify which token the sender is paying with. The API finds the best route to your checkout’s settlement token(s) for the receiver’s requested amount.| Field | Description |
|---|---|
fromTokenAddress | Token contract address. Use 0x0000000000000000000000000000000000000000 for EVM native tokens |
slippage | Optional. Slippage tolerance as a decimal (e.g., 0.005 for 0.5%) |
toAmount matches the receiver’s requested amount and fromAmount is what the sender must pay (including swap costs and fees).
Quotes expire in 60 seconds. If it expires before you call
/prepare, request a new quote. The quoteVersion increments with each new quote.Step 5 (Payment): Prepare Signing
Locks in the quote and returns the signing payload. You can optionally request on-chain balance assertions.| Field | Default | Description |
|---|---|---|
assertBalanceForGasCost | false | Verify the wallet has enough for gas. Returns 422 if insufficient |
assertBalanceForTransferAmount | false | Verify the wallet has enough for the transfer. Returns 422 if insufficient |
executionState: "signing" and quote.signingPayload:
| Chain | Fields | Notes |
|---|---|---|
| EVM | evmTransaction (to, data, value, gasLimit) | Standard EVM transaction |
| EVM (ERC-20) | evmApproval (tokenAddress, spenderAddress, amount) | Send approval tx first if present |
| SOL, SUI | serializedTransaction | Base64-encoded serialized transaction |
| BTC | psbt | Base64-encoded unsigned PSBT |
422— Quote expired: go back to Step 4422— Risk not cleared: pollGET /transactions/{id}untilriskStateis"cleared", then retry422— Insufficient balance: response includesrequiredandavailableamounts
Step 6 (Payment): Sign and Broadcast On-Chain
Usequote.signingPayload to sign and submit the transaction with whatever wallet your sender has connected. If evmApproval is present, send the approval transaction first, then the main one.
Don’t have wallet connection in your app? Use the Dynamic JavaScript SDK to handle wallet connection, signing, and broadcasting end-to-end — including the full checkout flow via
checkout-flow. The JS SDK works in any browser or Node environment and removes the need to manage signing yourself.walletClient) — for example, the one returned by useWalletClient() in wagmi or by Dynamic’s primary wallet connector.
sign.ts
Step 7 (Payment): Notify Backend of Broadcast
This endpoint does not broadcast the transaction on-chain. That already happened in Step 6 — when the wallet signed and submitted the transaction, the network received it. This call is your client notifying Dynamic’s backend that the broadcast happened and handing over the resulting
txHash, so the backend can start watching the chain for confirmation and orchestrating settlement.executionState: "broadcasted".
Step 8 (Payment): Wait for Settlement
After broadcast, the backend handles cross-chain settlement automatically. Monitor progress via polling or webhooks.Option A: Polling
| Condition | Meaning |
|---|---|
settlementState === "completed" | Funds delivered to the receiver’s destination |
settlementState === "failed" | Settlement failed |
executionState === "failed" | Execution failed |
executionState === "cancelled" | Transaction cancelled |
executionState === "expired" | Session timed out |
none → routing → bridging → swapping → settling → completed. Same-chain, same-token payments jump directly to completed.
Option B: Webhooks (Recommended)
Set up a webhook to receive events as the transaction progresses:| Event | Action |
|---|---|
settlement.state.completed | Payment is done — fulfill the order |
settlement.state.failed | Settlement failed — inspect data.failure |
execution.state.failed | Execution failed — inspect data.failure |
webhook-handler.ts
Deposit Flow
A complete walkthrough formode: "deposit" checkouts, where the sender chooses how much to send. The eight steps mirror the payment flow — what changes is the checkout mode and how the amount is interpreted.
Step 1 (Deposit): Create a Deposit Checkout
A checkout is a reusable deposit configuration: it defines what token(s) you’ll receive and the destination wallet(s). Reuse the samecheckoutId for every deposit when that configuration does not change.
"mode": "deposit". Settlement and destination configs work the same way.
Response (201): Returns the checkout object with an id. Save the id — you’ll use it as checkoutId in Step 2.
Step 2 (Deposit): Create a Transaction
Start a new deposit. In deposit mode the sender chooses how much they want to send — for example, an end user topping up a $100 balance. This returns a session token that authenticates all subsequent calls.| Field | Description |
|---|---|
amount | The amount the sender wants to deposit, as a string (e.g., "100.00") |
currency | Currency code the amount is denominated in (e.g., "USD") |
memo | Optional. Arbitrary JSON metadata for your own correlation (e.g., user ID) |
expiresIn | Optional. TTL in seconds. Default: 3600 (1 hour) |
destinationAddresses | Optional. Override the checkout’s destination for this transaction |
Step 3 (Deposit): Attach Source
Declare which wallet and chain the depositor is funding from. After this, risk screening runs asynchronously.| Field | Description |
|---|---|
sourceType | "wallet" or "exchange" |
fromAddress | Depositor’s wallet address. Required for wallet type |
fromChainId | Chain ID (e.g., "1" = Ethereum, "8453" = Base). Required for wallet type |
fromChainName | Chain family: "EVM", "SOL", "BTC", "SUI". Required for wallet type |
executionState: "source_attached".
Error (403): Blocked by sanctions screening — cancel and retry with a different source.
Step 4 (Deposit): Get a Quote
Specify which token the depositor is funding with. The API finds the best route from that token to your checkout’s settlement token(s) for the deposit amount.| Field | Description |
|---|---|
fromTokenAddress | Token contract address the depositor is sending. Use 0x0000000000000000000000000000000000000000 for EVM native tokens |
slippage | Optional. Slippage tolerance as a decimal (e.g., 0.005 for 0.5%) |
fromAmount is what the depositor’s wallet will be charged in the source token; toAmount is what lands at the destination after swap and fees.
Quotes expire in 60 seconds. If it expires before you call
/prepare, request a new quote. The quoteVersion increments with each new quote.Step 5 (Deposit): Prepare Signing
Locks in the quote and returns the signing payload. You can optionally request on-chain balance assertions.| Field | Default | Description |
|---|---|---|
assertBalanceForGasCost | false | Verify the wallet has enough for gas. Returns 422 if insufficient |
assertBalanceForTransferAmount | false | Verify the wallet has enough for the transfer. Returns 422 if insufficient |
executionState: "signing" and quote.signingPayload:
| Chain | Fields | Notes |
|---|---|---|
| EVM | evmTransaction (to, data, value, gasLimit) | Standard EVM transaction |
| EVM (ERC-20) | evmApproval (tokenAddress, spenderAddress, amount) | Send approval tx first if present |
| SOL, SUI | serializedTransaction | Base64-encoded serialized transaction |
| BTC | psbt | Base64-encoded unsigned PSBT |
422— Quote expired: go back to Step 4422— Risk not cleared: pollGET /transactions/{id}untilriskStateis"cleared", then retry422— Insufficient balance: response includesrequiredandavailableamounts
Step 6 (Deposit): Sign and Broadcast On-Chain
Usequote.signingPayload to sign and submit the transaction with whatever wallet the depositor has connected. If evmApproval is present, send the approval transaction first, then the main one.
Don’t have wallet connection in your app? Use the Dynamic JavaScript SDK to handle wallet connection, signing, and broadcasting end-to-end — including the full checkout flow via
checkout-flow. The JS SDK works in any browser or Node environment and removes the need to manage signing yourself.walletClient) — for example, the one returned by useWalletClient() in wagmi or by Dynamic’s primary wallet connector.
sign.ts
Step 7 (Deposit): Notify Backend of Broadcast
This endpoint does not broadcast the transaction on-chain. That already happened in Step 6 — when the wallet signed and submitted the transaction, the network received it. This call is your client notifying Dynamic’s backend that the broadcast happened and handing over the resulting
txHash, so the backend can start watching the chain for confirmation and orchestrating settlement.executionState: "broadcasted".
Step 8 (Deposit): Wait for Settlement
After broadcast, the backend handles cross-chain settlement automatically. Monitor progress via polling or webhooks.Option A: Polling
| Condition | Meaning |
|---|---|
settlementState === "completed" | Deposit delivered to the destination |
settlementState === "failed" | Settlement failed |
executionState === "failed" | Execution failed |
executionState === "cancelled" | Transaction cancelled |
executionState === "expired" | Session timed out |
none → routing → bridging → swapping → settling → completed. Same-chain, same-token deposits jump directly to completed.
Option B: Webhooks (Recommended)
Set up a webhook to receive events as the transaction progresses:| Event | Action |
|---|---|
settlement.state.completed | Deposit is done — credit the user’s account |
settlement.state.failed | Settlement failed — inspect data.failure |
execution.state.failed | Execution failed — inspect data.failure |
webhook-handler.ts
Cancelling a Transaction
Cancel any time before broadcast (states:initiated, source_attached, quoted, signing):
executionState: "cancelled". Once cancelled, create a new transaction to retry.
Complete Example
A self-contained TypeScript script that creates a checkout, executes a payment, and polls for settlement. It assumes you already have a connected wallet client — for example one returned by Dynamic’s JS SDK, wagmi’suseWalletClient(), or any viem WalletClient. If your app doesn’t have wallet connection yet, use the Dynamic JavaScript SDK to provide it.
checkout-example.ts
Supported Chains and Native Tokens
The Checkout API supports the following chains. Use these values forchainName, chainId, and token addresses in your settlement config and source attachment.
Chain Reference
| Chain | chainName | chainId | Networks |
|---|---|---|---|
| EVM | "EVM" | Standard EVM chain ID (e.g., "1" for Ethereum, "137" for Polygon, "8453" for Base, "42161" for Arbitrum, "10" for Optimism) | All major EVM networks |
| Solana | "SOL" | "101" | Solana mainnet |
| Bitcoin | "BTC" | "1" | Bitcoin mainnet |
| Sui | "SUI" | "501" | Sui mainnet |
| Name | ID |
|---|---|
| Base Sepolia Testnet | "84532" |
| Arbitrum Sepolia Testnet | "421614" |
| Arc Testnet | "5042002" |
| OP Sepolia Testnet | "11155420" |
Native Token Addresses
For native tokens (ETH, SOL, BTC, SUI), use any of the accepted addresses below in token address fields:| Chain | Accepted native token addresses |
|---|---|
| EVM | 0x0000000000000000000000000000000000000000 (zero address) or 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee |
| Solana | 11111111111111111111111111111111 (System Program) or So11111111111111111111111111111111111111112 (Wrapped SOL) |
| Bitcoin | 11111111111111111111111111111111 or bitcoin |
| Sui | 0x2::sui::SUI or 0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI |
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 for USDC on Base).
Error Reference
| Status | Cause | Recovery |
|---|---|---|
400 | Invalid request body | Check field formats |
401 | Missing or invalid session token | Verify x-dynamic-checkout-session-token header |
403 | Risk screening blocked | Cancel and retry with a different source |
404 | Resource not found | Verify checkout/transaction IDs |
409 | State conflict or duplicate tx hash | Check executionState and call the correct next step |
422 | Quote expired | Re-quote (Step 4) and retry prepare |
422 | Insufficient balance | Source wallet doesn’t have enough funds |
422 | Risk not cleared | Poll until riskState is "cleared", then retry |
Tips
- Balance assertions: Enable both
assertBalanceForGasCostandassertBalanceForTransferAmountin prepare to catch insufficient balance before signing. - Quote evaluation: Check
quote.fees.totalFeeUsdandquote.estimatedTimeSecprogrammatically before proceeding. Cancel and retry with a different token/chain if fees are too high. - Quote expiry: Quotes last 60 seconds. Sign promptly. You can re-quote multiple times —
quoteVersionincrements with each new quote. - Idempotency: Use the
memofield to store your own idempotency keys (e.g.,{ "orderId": "order_abc123" }). - Error recovery: If your process crashes mid-flow, call
GET /transactions/{id}to check the currentexecutionStateand resume from the correct step. - Session token lifetime: Matches the transaction’s
expiresIn(default 1 hour).
Quick Reference
| Step | Method | Endpoint | Auth |
|---|---|---|---|
| Create checkout | POST | /environments/{envId}/checkouts | API token |
| Create transaction | POST | /sdk/{envId}/checkouts/{checkoutId}/transactions | None |
| Attach source | POST | /sdk/{envId}/transactions/{txId}/source | Session token |
| Get quote | POST | /sdk/{envId}/transactions/{txId}/quote | Session token |
| Prepare signing | POST | /sdk/{envId}/transactions/{txId}/prepare | Session token |
| Record broadcast | POST | /sdk/{envId}/transactions/{txId}/broadcast | Session token |
| Cancel | POST | /sdk/{envId}/transactions/{txId}/cancel | Session token |
| Poll status | GET | /sdk/{envId}/transactions/{txId} | None |
Related
- Token swaps via API — Standalone token swaps without checkout orchestration
- Checkout Flow — JavaScript SDK checkout reference
createCheckoutTransaction— SDK transaction creationsubmitCheckoutTransaction— SDK submit (prepare + sign + broadcast)