Fireblocks Flow is an enterprise feature. Contact us to enable it for your environment.
@dynamic-labs-sdk/client, which work with any framework, and show the matching React hooks from @dynamic-labs-sdk/react-hooks next to them.
Complete Get started first. Your server creates the flow and passes its ID to this page, for example as a flowId URL parameter, so the payer can reload the page and resume.
Load and route by phase
Load the flow before you show anything, and choose the screen from its current state instead of from what the page did last. A flow has three independent states:executionState tracks the payer’s payment, settlementState tracks delivery to your destination, and riskState tracks screening. Payment lifecycle and states defines each one.
Collapse the states into a phase, checking the conditions in this order:
shouldPollFlow tells you when to read it again. It returns false once the flow has ended, and while the flow waits for the payer in initiated or source_attached, because the functions the page calls return the updated flow.
useGetFlow already polls, so do not pass your own refetchInterval. It keeps the newest flow it has seen, keeps showing it when a later read fails, and does not retry an error such as FlowNotFoundError that cannot succeed. The mutation hooks in this guide write the flow they return into the same cache, so every component that calls useGetFlow for the flow re-renders with it. To run code when a state changes, use useSubscribeToFlowStateChange from the React hooks catalog, which polls the same way.
Choose a payment method
In thepay phase with executionState initiated, let the payer choose a payment source: a wallet, an exchange, or a deposit address. Payment sources describes each one and which are screened.
uiConfig.enabledPaymentMethods lists the sources your server chose to show when it created the flow. It is for display only, and the API does not enforce it: a payer can still attach another source with direct requests, so do not rely on it for a compliance rule. The rest of uiConfig is also display data for your UI. Show merchantName and merchantLogoUrl if your server set them, and when termsOfServicesUrl is set, ask the payer to accept the terms before they confirm.
The payer can change their mind until the payment is sent. Attaching another source replaces the current one, so a back button on any pay screen returns to this choice.
Wallet
A wallet payment takes four steps: connect a wallet, attach it, quote a token, and approve the payment in the wallet.Connect and attach the wallet
Connect the payer’s wallet with your wallet connection UI. Build a wallet picker and Connect and verify a wallet cover it, and Compliance describes checks your app can run at this point, such as verifying wallet ownership. Then attach the wallet. Pass the wallet’s address, its chain family, and the network it is on.fromChainId is required for wallet sources, and the request fails without it.
Quote a token
List the tokens the payer holds on the wallet’s network withgetTokenBalances, and let them pick one. Filtering spam tokens from that list is one of the compliance checks your app can run. When the payer picks a token, quote it:
FlowQuoteUnavailableError.quoteFailures, which says why each of your settlement tokens could not be quoted, for example an amount below the route’s minimum.
Review the quote
Show the payer what they send, the fees, and how long the quote lasts. Quote amounts are integer strings in the token’s smallest unit, so format them with the token’s decimals fromfromTokenInfo and toTokenInfo:
quote.expiresAt, and when it runs out, replace the confirm button with one that gets a new quote. A new quote for the same token reuses the quote’s chain and token:
deposit mode, also pass the payer’s amount, as described in Deposit mode amount entry.
Send the payment
When the payer confirms, callsubmitFlowTransaction. It prepares the transaction, switches the wallet to the right network, asks the payer to approve a token allowance when an ERC-20 payment needs one, asks them to sign and send the transaction, and reports it to the flow. onStepChange reports approval and transaction, so you can show which prompt the wallet is waiting for.
Treat any successful return as sent. When the wallet accepted the transaction but did not return its hash in time, submitFlowTransaction returns the flow as it is, still in signing, without reporting a hash. The transaction may still be on its way, so treat that return as sent too. Either way, move to the processing screen and do not offer the confirm button again.
Handle errors by class, and never by message text:
UserRejectedError, by submitting again, and after the quote_expired code, by getting a new quote and submitting it. After any other error, call getFlow and show the screen for the state it returns. A flow still in quoted was not sent, and the payer can confirm again. A flow in signing or later may have been sent, and the page shows it as processing. A second approval could send a second payment.
Deposit address
With a deposit address, the payer sends funds from any wallet or exchange to an address generated for the payment. Nothing is connected or signed in your UI.Attach the source and get the address
Ask the payer for the chain family, network, and token they will send, and for a refund address on that chain family. Deposit addresses support EVM chains, Solana, Bitcoin, and TRON. Then attach the source and quote it. The quote returns the deposit address.fromTokenAddress for the token the payer will send: without it, the quote uses the chain’s native token.
Show the address
Show the deposit details only when they belong to the payer’s current selection, so a stale address never stays on screen while a new quote loads:Refresh the quote
The deposit address stays open until the flow ends, but the deposit provider guarantees the quoted amount for only 60 seconds. The quote’sexpiresAt shows the flow’s expiry, not that 60-second window, so time the refresh from quote.createdAt. If the payer has not sent funds within 60 seconds, get a new quote for the same chain and token:
quoted, because a refresh is no longer allowed once the deposit is detected. A new quote for the same chain and token usually keeps the same address. If the address changes, show the new one. Funds sent later to the address can still arrive, but the amount delivered can differ from the original quote. In deposit mode, pass the payer’s amount on every refresh.
Wait for the deposit
The flow is polled while it isquoted, so the load section picks up the deposit. When the deposit is seen, the flow moves to source_detected and then to source_confirmed. From source_detected, the payer can no longer change the source, so hide the back button and show that the payment was received and is being confirmed.
If the payer switches to another chain or token, attach and quote again, which gives a new address. Funds already sent to an earlier address for the same flow can still complete the payment, so tell the payer which address is current and that funds already sent may still count.
Exchange
With an exchange source, the payer pays from an exchange account through a purchase link, and the exchange confirms the payment. This section uses Coinbase as the example. Each exchange must be enabled for your environment in the Dynamic Console, and an exchange is available for a flow only when the flow settles in a token and on a network that exchange supports. For Coinbase, that is USDC.List the exchanges
Ask the flow which exchanges it supports, and show each one bylabel and iconUrl:
Attach the exchange and open the purchase link
When the payer picks an exchange, attach it with the exchange’skey. The returned flow carries the purchase link in exchangeSource.metadata.url.
window.open inside the click handler of an “Open” button, as the React example does. Browsers can block a window that opens after an await, so do not open it at the end of the attach call. The FlowExchangeSourceError codes tell you why an exchange cannot be used, and Handle Flow errors lists them.
Wait for the exchange
After the payer opens the link, show a waiting screen and keep reading the flow. A flow insource_attached with an exchange source is polled, so the load section picks up the change. The exchange confirms the payment, and the flow moves to source_confirmed and then to settlement completed.
Do not call broadcastFlow for an exchange payment. The exchange reports the payment, and the page only reads the flow.
To let the payer open the exchange again, attach the exchange again for a new link. A purchase already completed through an earlier link can still reach your destination, so warn the payer before they start a second purchase.
Processing
In theprocessing phase, the payment is on its way and the payer has nothing left to do. Keep reading the flow and show progress. Hide the back and cancel buttons.
An exchange payment waits for the exchange in
source_attached, which is still in the pay phase, so the waiting screen from the exchange section covers that step. Do not show success at source_confirmed. It means the payer’s funds arrived, but delivery to your destination can still fail. The payment is done only when settlementState is completed.
Outcomes
Show the final screen for the phase, and decide fulfillment on your server from webhooks or a server read of the flow, not from this page.
A failed, cancelled, or expired flow is final. The same flow cannot be paid again, so a new attempt needs a new flow from your server. Failures and refunds lists the failure codes and what to show for each.
On success, show the amount that was actually delivered with
getFlowDeliveredAmount. It returns undefined when settlement did not report an amount, so fall back to the quoted amount, and to the flow’s requested amount when there is no quote:
deposit mode, a short deposit is accepted and settles for what arrived, so show and credit the delivered amount. In payment mode, a short payment is not accepted, so a completed flow was paid in full. Payment lifecycle and states explains each amount.
If your server set uiConfig.returnUrl, you can send the payer back to your app from the final screen.
Deposit mode amount entry
Indeposit mode, the flow’s amount is a suggestion, and the payer can choose how much to deposit. Show an amount field only when flow.mode is deposit; other modes reject an amount with FlowQuoteAmountError code amount_not_editable.
The payer can enter the amount in USD, or in the token they pay with as SOURCE_TOKEN, where "0.5" means 0.5 of that token. Pass amount and amountCurrency on every quote, including refreshes. A quote without them prices the flow’s current amount in USD, so a token amount would not stay fixed. Pass both to submitFlowTransaction too, because it gets a new quote with them after the payer declines in the wallet.
flow.amount. When the source is a wallet, changing the amount screens the wallet again, so the quote can fail with FlowSourceBlockedError even though the attach succeeded. Show amounts with flow.currencyTokenInfo when the flow is denominated in a token, and with the quote’s fromTokenInfo and toTokenInfo otherwise.
Cancel
The payer can cancel a flow insource_attached, quoted, or signing with cancelFlow. Cancelling needs the session token that attaching a source stored, so a flow in initiated cannot be cancelled from the page; your server can cancel it instead, as described in the Flow payments API guide. Show the cancel button only in those three states, and hide it as soon as the payer approves a wallet payment.
Expiry timer
A flow expires atflow.expiresAt if the payment has not been sent by then. The flow is not read again while it waits for the payer in initiated or source_attached, so the page does not see the expiry until the next action fails. Run a timer on expiresAt and read the flow when it fires:
broadcasted or later does not expire, and the page keeps showing progress. An action on an expired flow fails, with FlowStateConflictError for attaching a source and with FlowSessionExpiredError for the actions that need the session token, because an expired flow’s token stops working. In both cases, read the flow and show the expired screen. Expiry and cancellation describes flow and quote expiry.
Reload and another device
On every load, read the flow and route by phase, as the load section does. Do not repeat the last action just because the page reloaded. When the payer attaches a source, the SDK stores the flow’s session token on the device, and every later action sends it. Reading the flow needs only the flow ID.- Same browser or app. The token is still stored, so the payer resumes from the flow’s state. A wallet payment in
quotedcan resume at the review screen when the same wallet is connected, matchingfromAddressandfromChainName. A deposit-address payment can restore the selection fromfromChainIdandfromToken. An exchange payment insource_attachedshows the waiting screen, and opening the exchange again needs a new link. - Another device, or cleared storage. The page can read the flow but has no token, so the next action fails with
FlowSessionTokenMissingError. A token that the server no longer accepts fails withFlowSessionExpiredErrorinstead, and the SDK removes it.
signing on another device cannot be attached again without the original token, because the payer may already have approved the payment on the first device. Show it as processing. Payment lifecycle and states explains why recovery works this way.
State-to-screen table
Settlement
completed shows success, and settlement failed shows the failed screen, whatever the execution state. Until the payment is sent, the payer can also go back and attach another source, or cancel.
Related
- Handle Flow errors: every error class, its code, and how to recover.
- Payment lifecycle and states: what each state means and what moves the flow.
- Expiry and cancellation: flow and quote expiry, and who can cancel.
- Flow payments API guide: the HTTP requests behind these functions.