> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Fireblocks Flow JavaScript guide

> End-to-end guide for completing a Fireblocks Flow payment with the JavaScript SDK.

<Note>
  This is an enterprise-only feature. Please [contact us](https://www.dynamic.xyz/book-a-call) to enable.
</Note>

Flow lets you send a crypto payment from any supported chain and have it settled in a specific token on a specific chain.

This guide walks through the complete flow, from creating a flow to polling for settlement completion.

## Prerequisites

Before starting a flow on the client, create one from your backend. The flow creation endpoint is server-side only, authenticated by an API token with `flow.write` scope — this is where amount, currency, settlement, and destination are fixed.

```bash theme={"system"}
# Create a flow from your backend (returns a flowId)
curl --request POST \
  --url https://app.dynamic.xyz/api/v0/server/<YOUR_ENVIRONMENT_ID>/flow/payment \
  --header 'Authorization: Bearer <YOUR_API_TOKEN>' \
  --header 'Content-Type: application/json' \
  --data '{
    "amount": "25.00",
    "currency": "USD",
    "settlementConfig": {
      "strategy": "cheapest",
      "settlements": [
        {
          "chainName": "EVM",
          "tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
          "chainId": "1",
          "symbol": "USDC",
          "tokenDecimals": 18
        }
      ]
    },
    "destinationConfig": {
      "destinations": [
        {
          "chainName": "EVM",
          "type": "address",
          "identifier": "0xYourSettlementAddress"
        }
      ]
    },
    "memo": {
      "description": "Payment for order #1234"
    }
  }'
```

The response includes `flow.id` — pass this as the `flowId` to your frontend when calling the SDK functions below.

<Note>
  **Optional — collect your own fee.** Add a `feeConfig` to the create body to take a percentage of each swap to your own EVM wallet(s). Recipient addresses must be EVM (`0x…`). See [Fee collection and claiming](/overview/fireblocks-flow-api#fee-collection-and-claiming) for the config shape, how to check balances, and how to claim accrued fees.
</Note>

## Supported Chains

Flow supports the following chains:

* BTC
* EVM
* SOL
* SUI
* TRON

<Note>
  TRON cross-chain swaps currently route through intent-based bridges and may incur higher gas costs relative to the transfer amount.
</Note>

The SDK signs internally, so the same flow works regardless of the payer's chain — pass a wallet account on the relevant chain. The full example below pays from EVM; to pay from Solana, attach a Solana source (`fromChainName: 'SOL'`, `fromChainId: '101'`) and quote with a Solana token mint (or `11111111111111111111111111111111` for native SOL). `submitFlowTransaction` handles the Solana versioned-transaction signing for you.

**Except for the EVM chains listed below, only mainnet is supported:**

| Name                     | `ID`         |
| :----------------------- | :----------- |
| Base Sepolia Testnet     | `"84532"`    |
| Arbitrum Sepolia Testnet | `"421614"`   |
| Arc Testnet              | `"5042002"`  |
| OP Sepolia Testnet       | `"11155420"` |

## Overview

The flow follows these steps:

1. **Attach** the source wallet with [`attachFlowSource`](/javascript/reference/client/attach-flow-source)
2. **Quote** the conversion with [`getFlowQuote`](/javascript/reference/client/get-flow-quote)
3. **Submit** the transaction with [`submitFlowTransaction`](/javascript/reference/client/submit-flow-transaction) (prepares, signs, and broadcasts)
4. **Poll** for completion with [`getFlow`](/javascript/reference/client/get-flow)

## Resume a flow from its current state

A flow ID represents one execution attempt. On page load, retry, or reconnect, call [`getFlow`](/javascript/reference/client/get-flow) before calling a mutation. Do not unconditionally call `attachFlowSource` again.

| `executionState`                    | Valid next action                                                                                            |
| :---------------------------------- | :----------------------------------------------------------------------------------------------------------- |
| `initiated`                         | Attach a source or cancel.                                                                                   |
| `source_attached`                   | Attach a different source, get a quote, record an exchange transfer, or cancel.                              |
| `quoted`                            | Re-quote, attach a different source, submit for signing, or cancel.                                          |
| `signing`                           | Continue the in-progress signing operation. If it cannot resume, re-quote, attach another source, or cancel. |
| `broadcasted`                       | Poll or process webhooks. Do not attach another source or cancel.                                            |
| `source_confirmed`                  | Poll or process webhooks until settlement completes or fails. Do not call execution mutations.               |
| `cancelled`, `expired`, or `failed` | Create a new flow for another attempt. These execution states do not allow any further transitions.          |

<Warning>
  `source_confirmed` means source execution is finished, not necessarily that
  settlement is finished. Keep tracking `settlementState` until it reaches
  `completed` or `failed`.
</Warning>

## Full example

```javascript theme={"system"}
import {
  attachFlowSource,
  getFlowQuote,
  submitFlowTransaction,
  getFlow,
} from '@dynamic-labs-sdk/client';

const pay = async (flowId, walletAccount) => {
  // Step 1: Attach the wallet source
  await attachFlowSource({
    flowId,
    fromAddress: walletAccount.address,
    fromChainId: '1',
    fromChainName: walletAccount.chain,
    sourceType: 'wallet',
  });

  // Step 2: Get a quote
  const quoted = await getFlowQuote({
    flowId,
    fromTokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
  });

  console.log('Quote:', quoted.quote);

  // Step 3: Submit (prepare + sign + broadcast)
  await submitFlowTransaction({
    flowId,
    walletAccount,
  });

  // Step 4: Poll for completion
  const final = await getFlow({ flowId });
  console.log('Final state:', final.settlementState);
};
```

## Exchange source

To accept payment from a Coinbase exchange account instead of a wallet, attach an exchange source:

```javascript theme={"system"}
import { attachFlowSource, broadcastFlow } from '@dynamic-labs-sdk/client';

// Attach exchange source — returns the flow with exchange metadata
const { flow } = await attachFlowSource({
  flowId,
  sourceType: 'exchange',
  exchangeProvider: 'coinbase',
});

// The exchange buy URL is in flow.exchangeSource.metadata.url
// Open it in an iframe or redirect the user to complete payment on Coinbase
const exchangeUrl = flow.exchangeSource?.metadata?.url;
window.open(exchangeUrl, '_blank');

// After the user completes payment on the exchange, notify the backend
await broadcastFlow({ flowId });
```

## Handling cancellation and errors

```javascript theme={"system"}
import { submitFlowTransaction, cancelFlow } from '@dynamic-labs-sdk/client';

try {
  await submitFlowTransaction({
    flowId,
    walletAccount,
  });
} catch (error) {
  if (error.message.includes('rejected')) {
    // User rejected the signing request in their wallet
    await cancelFlow({ flowId });
    console.log('Flow cancelled');
  } else {
    console.error('Flow failed:', error.message);
  }
}
```

## Error reference

Each SDK function can throw errors with an HTTP status code and a message. The table below lists common errors by step, their cause, and how to resolve them.

### Create flow (backend)

| Status | Error                                                                   | Cause                                                                                | Fix                                                                                              |
| :----- | :---------------------------------------------------------------------- | :----------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------- |
| `400`  | `Invalid flow mode. Expected one of: payment, deposit, withdraw`        | The `mode` path parameter is not a valid flow mode.                                  | Use `payment`, `deposit`, or `withdraw`.                                                         |
| `401`  | `Unauthorized`                                                          | Missing or invalid API token, or the token does not have `flow.write` scope.         | Verify the `Authorization: Bearer dyn_...` header and the token scope in the dashboard.          |
| `422`  | `Each chain should only have one destination config`                    | Duplicate chain entries in `destinationConfig.destinations`.                         | Remove duplicate chain entries so each chain appears once.                                       |
| `422`  | `Settlement for token SYMBOL: ...`                                      | A settlement chain is not supported, or the token address is invalid for that chain. | Verify the chain/token pair. Use a valid EVM address for EVM chains, a Solana mint for SOL, etc. |
| `422`  | `All settlement chains must have a destination config. Missing: ...`    | A chain in `settlementConfig` has no matching entry in `destinationConfig`.          | Add a destination for every settlement chain.                                                    |
| `422`  | `feeConfig.recipients must have between 1 and 10 entries, got ...`      | `feeConfig.recipients` is empty or has more than 10 entries.                         | Provide 1–10 fee recipients.                                                                     |
| `422`  | `Fee recipient percentage must be between 0 and 1 (exclusive), got ...` | A recipient `percentage` is `<= 0` or `>= 1`.                                        | Use a decimal strictly between `0` and `1` (e.g. `0.01`).                                        |
| `422`  | `Combined fee recipient percentage must be less than 1, got ...`        | The recipients' `percentage` values sum to `1` or more.                              | Lower the fees so the combined total is below `1`.                                               |
| `422`  | `Fee recipient walletAddress must be a valid EVM address, got ...`      | A recipient `walletAddress` is not a valid EVM address (e.g. a Solana address).      | Use a valid EVM (`0x…`) address for every fee recipient.                                         |

### Attach source (`attachFlowSource`)

| Status | Error                                                                                | Cause                                                                                         | Fix                                                                                                                                   |
| :----- | :----------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ |
| `400`  | `fromAddress, fromChainId, and fromChainName are required for wallet sources`        | Missing source fields when `sourceType` is `wallet`.                                          | Pass `fromAddress`, `fromChainId`, and `fromChainName` from the wallet account.                                                       |
| `400`  | `fromChainId and fromChainName are required for deposit_address sources`             | Missing chain fields when `sourceType` is `deposit_address`.                                  | Pass `fromChainId` and `fromChainName`.                                                                                               |
| `400`  | `fromAddress is not accepted for deposit_address sources; use refundAddress instead` | `fromAddress` was passed with `sourceType: 'deposit_address'`.                                | Remove `fromAddress` and use `refundAddress` instead.                                                                                 |
| `400`  | `refundAddress is only valid for deposit_address sources`                            | `refundAddress` was passed with `sourceType: 'wallet'` or `'exchange'`.                       | Remove `refundAddress` from non-deposit-address requests.                                                                             |
| `400`  | Chain not supported                                                                  | The source chain/chainId combination is not supported for swaps.                              | Use a supported chain. See [Supported Chains](#supported-chains).                                                                     |
| `403`  | `Flow is blocked by sanctions`                                                       | The source address failed sanctions screening (wallet sources only).                          | Use a different source wallet.                                                                                                        |
| `404`  | `Flow not found`                                                                     | The `flowId` does not exist, the flow has been deleted, or the environment ID is wrong.       | Verify the `flowId` and environment.                                                                                                  |
| `409`  | State transition error                                                               | `attachFlowSource` was called outside `initiated`, `source_attached`, `quoted`, or `signing`. | Re-fetch with `getFlow`. Poll `broadcasted` or `source_confirmed` flows; create a new flow after `cancelled`, `expired`, or `failed`. |

### Get quote (`getFlowQuote`)

| Status | Error                                                                               | Cause                                                                                                                                  | Fix                                                                                                                   |
| :----- | :---------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------- |
| `400`  | `fromChainName is required` / `fromChainId is required` / `fromAddress is required` | Source was not attached before requesting a quote.                                                                                     | Call `attachFlowSource` first.                                                                                        |
| `400`  | `fromTokenAddress is required and could not be resolved for the specified chain`    | The token could not be determined for the source chain.                                                                                | Pass `fromTokenAddress` explicitly in the quote request.                                                              |
| `403`  | `Flow is blocked by risk screening`                                                 | The flow's risk screening flagged the source.                                                                                          | Cancel the flow and retry with a different source.                                                                    |
| `409`  | State transition error                                                              | The flow is not in a valid state for quoting.                                                                                          | Re-fetch with `getFlow`. Re-quote only from a supported pre-broadcast state; create a new flow if execution is final. |
| `422`  | `No quotes available for any settlement token.`                                     | No swap provider returned a valid quote. The response includes a structured `quoteFailures` array with per-settlement failure details. | Inspect the `quoteFailures` array. See the reason codes below.                                                        |

**Quote failure response format:**

When no quotes are available, the 422 response includes a `quoteFailures` array:

```json theme={"system"}
{
  "error": "No quotes available for any settlement token.",
  "quoteFailures": [
    {
      "chainId": "728126428",
      "chainName": "TRON",
      "reason": "unknown",
      "symbol": "USDT"
    },
    {
      "chainId": "8453",
      "chainName": "EVM",
      "reason": "amount_below_minimum",
      "symbol": "USDC"
    }
  ]
}
```

Each entry identifies the settlement token that failed (`symbol` on `chainName`/`chainId`) and the failure `reason`.

**Quote failure reasons:**

| Reason                    | Meaning                                                                                                           | Fix                                                             |
| :------------------------ | :---------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- |
| `amount_below_minimum`    | The flow amount is too low for the selected token/route.                                                          | Increase the amount or choose a different source token.         |
| `amount_above_maximum`    | The flow amount exceeds the route's maximum.                                                                      | Decrease the amount or split across multiple flows.             |
| `insufficient_liquidity`  | Not enough liquidity on the swap route.                                                                           | Try a different source token, chain, or reduce the amount.      |
| `unsupported_chain_pair`  | No swap route exists between the source and settlement chains.                                                    | Use a different source chain or settlement chain.               |
| `token_not_found`         | The source or settlement token is not recognized by the swap provider.                                            | Verify the token address.                                       |
| `token_price_unavailable` | The swap provider cannot price the token for conversion.                                                          | Try a different source token.                                   |
| `slippage_too_tight`      | The slippage tolerance is too narrow for market conditions.                                                       | Increase `slippage` in the quote request (e.g. `0.5` for 0.5%). |
| `provider_rate_limited`   | The swap provider rate-limited the request.                                                                       | Wait a moment and retry.                                        |
| `provider_unavailable`    | The swap provider is temporarily down.                                                                            | Retry after a short delay.                                      |
| `unknown`                 | The swap provider returned an unclassified error. Often means the amount is too low for conversion on that route. | Try increasing the amount or using a different source token.    |

### Prepare signing (`prepareFlowSigning` / `submitFlowTransaction`)

| Status | Error                                                   | Cause                                                                                                                | Fix                                                  |
| :----- | :------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------- |
| `409`  | State transition error                                  | The flow is not in the `quoted` state.                                                                               | Ensure a quote was fetched first.                    |
| `422`  | `Quote has expired; request a new quote before signing` | The quote is older than 60 seconds.                                                                                  | Call `getFlowQuote` again, then retry.               |
| `422`  | `Flow risk screening has not been cleared`              | Risk screening is still pending or the flow was flagged.                                                             | Wait for `riskState` to become `cleared`, or cancel. |
| `422`  | `Insufficient balance ...`                              | The wallet does not have enough tokens or gas to cover the transaction. Includes `required` and `available` amounts. | Fund the wallet or choose a smaller amount.          |
| `409`  | `Flow was modified by another request`                  | A concurrent request changed the flow state.                                                                         | Re-fetch the flow and retry from the current state.  |

### Broadcast (`broadcastFlow`)

| Status | Error                                         | Cause                                             | Fix                                                                                                              |
| :----- | :-------------------------------------------- | :------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------- |
| `400`  | `txHash is required for non-exchange sources` | Wallet sources must include the transaction hash. | This is handled automatically by `submitFlowTransaction`. If calling `broadcastFlow` directly, include `txHash`. |
| `409`  | `Transaction hash already recorded`           | The same `txHash` was submitted twice.            | The broadcast already succeeded — no action needed.                                                              |
| `409`  | State transition error                        | The flow is not in the `signing` state.           | Check `executionState` before calling broadcast.                                                                 |

### Cancel (`cancelFlow`)

| Status | Error                  | Cause                                                        | Fix                                                                                              |
| :----- | :--------------------- | :----------------------------------------------------------- | :----------------------------------------------------------------------------------------------- |
| `404`  | `Flow not found`       | The flow does not exist or was already deleted.              | Verify the `flowId`.                                                                             |
| `409`  | State transition error | The flow has already been broadcasted or execution is final. | Poll the existing flow if funds were broadcast; otherwise create a new flow for another attempt. |

## Polling for status

After submission, poll [`getFlow`](/javascript/reference/client/get-flow) to track progress through execution and settlement states:

```javascript theme={"system"}
import { getFlow } from '@dynamic-labs-sdk/client';

const TERMINAL_EXECUTION = ['cancelled', 'expired', 'failed'];
const TERMINAL_SETTLEMENT = ['completed', 'failed'];

const pollFlow = async (flowId) => {
  const poll = async () => {
    const flow = await getFlow({ flowId });

    if (
      TERMINAL_EXECUTION.includes(flow.executionState) ||
      TERMINAL_SETTLEMENT.includes(flow.settlementState)
    ) {
      return flow;
    }

    await new Promise((resolve) => setTimeout(resolve, 3000));
    return poll();
  };

  return poll();
};
```

## Deposit address

With a deposit address flow the user sends funds directly to a generated address — no wallet connection or on-chain signing required. Works for BTC, SOL, EVM, and TRON. The flow uses the `attachFlowSource` / `getFlowQuote` / `getFlow` functions (not the `checkout` namespace).

<Steps>
  <Step title="Attach a deposit address source">
    ```javascript theme={"system"}
    import { attachFlowSource } from '@dynamic-labs-sdk/client';

    await attachFlowSource({
      flowId: flow.id,
      sourceType: 'deposit_address',
      fromChainId: '1',          // '1' for BTC or EVM mainnet, '101' for SOL
      fromChainName: 'BTC',      // 'BTC', 'SOL', or 'EVM'
      refundAddress: 'bc1q...', // optional: where to return funds if the transfer fails
                                 // omit when paying from a CEX with no connected wallet
    });
    ```
  </Step>

  <Step title="Get a quote — response includes the deposit address">
    ```javascript theme={"system"}
    import { getFlowQuote } from '@dynamic-labs-sdk/client';

    const quoted = await getFlowQuote({
      flowId: flow.id,
      fromTokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // optional; omit for native
    });

    const depositAddress = quoted.quote?.depositAddress;
    // Show depositAddress to the user — they send funds to it from any wallet or CEX
    ```
  </Step>

  <Step title="Poll until the transfer is detected">
    The backend detects the inbound transfer automatically. Poll `getFlow` until `executionState` leaves `"quoted"`:

    ```javascript theme={"system"}
    import { getFlow } from '@dynamic-labs-sdk/client';

    const waitForTransfer = async (flowId) => {
      const updated = await getFlow({ flowId });
      if (updated.executionState !== 'quoted') return updated;
      await new Promise((resolve) => setTimeout(resolve, 3000));
      return waitForTransfer(flowId);
    };

    const final = await waitForTransfer(flow.id);
    console.log('Final state:', final.executionState); // 'source_confirmed' or terminal
    ```
  </Step>
</Steps>

<Note>
  There is no signing step. Once the user sends funds to `depositAddress`, the transfer is detected automatically and the flow advances to `source_confirmed`.
</Note>

## Related

* [`attachFlowSource`](/javascript/reference/client/attach-flow-source) - Attach source wallet or exchange
* [`getFlowQuote`](/javascript/reference/client/get-flow-quote) - Get a conversion quote
* [`submitFlowTransaction`](/javascript/reference/client/submit-flow-transaction) - Submit for signing and broadcast
* [`getFlow`](/javascript/reference/client/get-flow) - Poll flow state
* [`cancelFlow`](/javascript/reference/client/cancel-flow) - Cancel a flow
* [`broadcastFlow`](/javascript/reference/client/broadcast-flow) - Record a broadcast (for manual or exchange flows)
* [`prepareFlowSigning`](/javascript/reference/client/prepare-flow-signing) - Prepare signing (advanced, usually called by `submitFlowTransaction`)
