This is an enterprise-only feature. Please contact us to enable.
payment flow with disableSwaps, the flow issues a deposit address on the settlement chain, the payer transfers the token to it, and the funds reach your destination. No swap provider quotes the payment and nothing is converted: the payer sends USDC on Base, and you receive USDC on Base.
Use this when your payers already hold the token you want to receive. To accept any token or chain, see the API guide.
Prerequisites
- A Dynamic environment with Flow payments enabled.
- An environment API token with the
flow.writepermission, kept on your server and read from an environment variable or a secrets manager. - A destination address on the settlement chain.
https://app.dynamic.xyz/api/v0.
Step 1: Create the flow
Create the flow from your backend withmode: "payment" and disableSwaps: true. The amount, currency, settlement, and destination are fixed here and cannot change later.
flow. Store flow.id: your payer page needs it for the next steps, and your backend uses it and the memo to reconcile the payment later.
To set how long the payment stays open, pass expiresIn in seconds. The default is 86400 seconds (24 hours).
Step 2: Attach a deposit address source
On your payer page, attach adeposit_address source to the flow. This request needs no credential, and its response carries the flow session token that the remaining requests use.
- JavaScript
- cURL
fromChainNameandfromChainIdmust match a chain in yoursettlementConfig.settlements.refundAddressis required. It is where the deposit provider returns funds it does not accept. Dynamic does not initiate refunds, and a refund can fail.fromAddressis not accepted for adeposit_addresssource, because the payer does not connect a wallet.
sessionToken immediately. It is returned once, with Cache-Control: no-store, and expires at sessionExpiresAt.
Step 3: Get the deposit address
Request a quote with the session token. For a deposit address source, this request issues the deposit address and returns it on the flow.- JavaScript
- cURL
fromTokenAddress for a token such as USDC; without it, the quote uses the chain’s native token, which does not match a USDC settlement. The flow moves to executionState: "quoted" and includes:
depositAddress: the address the payer sends funds to.quote.fromAmount: how much of the token the payer must send, as an integer string in the token’s smallest unit. Divide it by10 ** quote.fromTokenInfo.decimalsto display it.
quote.expiresAt shows the flow’s expiry rather than that window. If the payer has not sent funds after 60 seconds, request the quote again to refresh the amount. A new quote for the same chain and token usually keeps the same address.
Step 4: Show the payer the address
RenderdepositAddress and the amount with the token symbol and chain, so the payer knows exactly what to send and where. A QR code of the address works well for mobile wallets.
A deposit address belongs to one flow. Create a new flow for each payment.
Step 5: Detect the payment
Flow payments watches the deposit address and advances the flow on its own. Execution moves tosource_detected when the transfer is seen and to source_confirmed once it is confirmed. Your backend can learn about it from webhooks, which are recommended, or by polling.
Webhooks
Subscribe your backend to the flow events:
See Webhooks for the payload shapes and Webhook setup for endpoint configuration and signature verification.
Polling
For a prototype or a low-volume integration, poll the read endpoint until the flow reaches a final state. It needs no credential, and it returns the flow itself.Step 6: Confirm the payment
TreatsettlementState: "completed" as paid. Because this is a payment flow, completed means the payment was accepted in full: a deposit smaller than the quote does not settle, and the deposit provider attempts to return it to the refund address. Execution reaching source_confirmed only means the payer’s transfer arrived; settlement can still be in progress.
When settlement completes, reconcile the payment on your server:
- Match
flowIdand the flow’smemo, for example theorderIdfrom Step 1, to your order. Webhooks do not carrymemo, so read it from the flow with your API token, as shown in Read a flow from your server. - Record
settlementTxHashfrom theflow.settlement.updatedevent, or from the server read, as the on-chain receipt. The payer’s view of the flow does not include it. - Record the flow’s
txHashas the payer’s deposit transaction, when the deposit provider reported one. settlement.completedAtis when the funds reached your destination.
cancelled, expired, or failed, this payment attempt is over. Create a new flow for another attempt.
Alternative: send a payment link
If you do not want to build the payer page, create a payment link instead of calling the create endpoint directly:baseUrl, the page the link opens, whose origin must be a registered CORS origin for the environment. The response returns a paymentUrl to send to the payer. See Payment links.
Common pitfalls
- Do not send
Authorization: Bearerto/sdk/endpoints. Only the/server/endpoints accept the API token. The payer endpoints useX-Dynamic-Flow-Session-Tokenor no credential. - The quote fails with
422. The payer’sfromChainName,fromChainId, or token does not match a configured settlement, so the payment would need a swap whiledisableSwapsis set. - A request returns
409. The flow moved past that step. Read the flow and continue from its currentexecutionStateinstead of repeating the request. API errors lists the codes. - The payer sends the wrong token or chain. The transfer does not match the quote, and the deposit provider decides what happens to it. It may return the funds to the refund address, but Dynamic does not initiate or guarantee a refund. Show the payer the exact token, chain, and amount to avoid this. See Failures and refunds.
- The payer sends funds after the flow ends. Funds sent to the address of a cancelled or expired flow can still reach your destination without the flow recording them. See Expiry and cancellation.