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

# Adding your own fee

> Collect your own fee on every Fireblocks Flow swap and claim accrued fee balances.

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

Charge your own fee on every swap a flow runs. You attach a **fee config** when you create a flow, and on each swap a percentage of the source amount is collected and delivered to the recipient wallet(s) you specify. This is direct revenue for your business, layered on top of Dynamic's own routing costs.

Depending on the route used to fill a swap, fees are collected one of two ways:

* **Distributed on-chain at swap time** — the fee is sent straight to the wallet address you specified in the fee configs when creating the flow, as part of the settlement transaction. Nothing to claim; it arrives with the swap.
* **Accrued as a claimable balance** — the fee accumulates off-chain (typically as USDC) and you sweep it later with the claim flow below.

Because either can happen, poll the [balances endpoint](#check-claimable-balances) and run the [claim flow](#claim-accrued-fees) to collect anything that accrued rather than being distributed directly.

<Note>
  Fee collection is an **EVM-only** feature. See [Supported chains for fees](#supported-chains-for-fees).
</Note>

## Add a fee config

`feeConfig` is an optional field on the **create flow** body (Step 1 of any mode — payment, deposit, or withdrawal). It is fixed at creation and cannot be changed later.

```
POST /server/{environmentId}/flow/{mode}
Authorization: Bearer dyn_your_api_token
Content-Type: application/json
```

```json theme={"system"}
{
  "amount": "25.00",
  "currency": "USD",
  "settlementConfig": { "...": "..." },
  "destinationConfig": { "...": "..." },
  "feeConfig": {
    "recipients": [
      {
        "walletAddress": "0xYourFeeRecipientWallet",
        "percentage": 0.01
      }
    ]
  }
}
```

| Field                                  | Description                                                                                              |
| :------------------------------------- | :------------------------------------------------------------------------------------------------------- |
| `feeConfig.recipients`                 | 1–10 recipients, each receiving a share of the source amount on every swap                               |
| `feeConfig.recipients[].walletAddress` | The wallet that receives (and, for claims, signs for) the fee. Must be a valid EVM (`0x…`) address       |
| `feeConfig.recipients[].percentage`    | Fraction of the source amount to collect, as a decimal in the exclusive range `0`–`1` (e.g. `0.01` = 1%) |

**Validation.** The create call returns `422` if:

* there are fewer than 1 or more than 10 recipients,
* any `percentage` is not strictly between `0` and `1`,
* the recipients' combined `percentage` is `1` or greater (that would consume the entire swap), or
* any `walletAddress` is not a valid EVM address (see [Supported chains for fees](#supported-chains-for-fees)).

With multiple recipients, each recipient's share is `percentage × sourceAmount`, split independently — so `[{ 0.01 }, { 0.005 }]` collects 1.5% total and delivers each recipient their own cut.

## Supported chains for fees

Fee collection is an **EVM-only** feature:

* **Recipient addresses must be EVM (`0x…`).** A non-EVM recipient (for example a Solana address) is rejected at flow creation with `422`. Both underlying settlement models require an EVM recipient — on-chain distribution sends to an EVM receiver, and claimable balances are released by an EVM signature from the recipient wallet.
* **Fees are collected on EVM swaps** and any claimable balance settles on an EVM chain, denominated per token and chain in the [balances response](#check-claimable-balances).

The source token a payer swaps *from* can still be on any [supported chain](/docs/overview/fireblocks-flow-api#supported-chains-and-native-tokens); the fee itself is always collected to, and claimed from, an EVM address.

## Check claimable balances

Check whether a recipient has any fees waiting to be claimed. Returns a per-token, per-chain breakdown plus a `hasClaimableFees` convenience flag.

```
GET /server/{environmentId}/flow/fees/balances?recipientAddress=0xYourFeeRecipientWallet
Authorization: Bearer dyn_your_api_token
```

| Query param        | Description                                                           |
| :----------------- | :-------------------------------------------------------------------- |
| `recipientAddress` | Required. The fee recipient's EVM wallet address                      |
| `chainId`          | Optional filter. Only return balances settled on this chain           |
| `tokenAddress`     | Optional filter. Only return balances for this token contract address |

**Response (200):**

```json theme={"system"}
{
  "balances": [
    {
      "amount": "75021651210714015",
      "chainId": "8453",
      "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amountUSD": "116.235557",
      "tokenSymbol": "USDC"
    }
  ],
  "hasClaimableFees": true
}
```

| Field                     | Description                                                                       |
| :------------------------ | :-------------------------------------------------------------------------------- |
| `balances`                | One entry per claimable token/chain balance. Empty when there is nothing to claim |
| `balances[].amount`       | Claimable amount as a string in the token's smallest unit                         |
| `balances[].chainId`      | Chain the balance can be claimed and settled on                                   |
| `balances[].tokenAddress` | Token contract address the balance is denominated in                              |
| `balances[].amountUSD`    | Approximate USD value, when available                                             |
| `balances[].tokenSymbol`  | Human-readable token symbol, when available                                       |
| `hasClaimableFees`        | `true` when there is at least one claimable balance                               |

<Note>
  An empty `balances` list (`hasClaimableFees: false`) means there is nothing to claim right now — either no fees have accrued, or every fee was distributed directly on-chain at swap time.
</Note>

**Error (400):** `recipientAddress` is not a valid EVM address.

## Claim accrued fees

Claiming is a two-step, sign-in-the-middle flow: **initiate** to get the payload(s) the recipient must sign, then **submit** the signatures. The recipient wallet authorizes the release — no provider details are ever exposed.

**1. Initiate the claim.**

```
POST /server/{environmentId}/flow/fees/claim
Authorization: Bearer dyn_your_api_token
Content-Type: application/json
```

```json theme={"system"}
{
  "recipientAddress": "0xYourFeeRecipientWallet"
}
```

| Field              | Description                                                          |
| :----------------- | :------------------------------------------------------------------- |
| `recipientAddress` | Required. The fee recipient's EVM wallet address                     |
| `chainId`          | Optional filter. Only claim balances settled on this chain           |
| `tokenAddress`     | Optional filter. Only claim balances for this token contract address |

**Response (200):**

```json theme={"system"}
{
  "requestId": "e2b1c3d4-5678-90ab-cdef-1234567890ab",
  "steps": [
    {
      "id": "step-1",
      "type": "signature",
      "data": {
        "message": "0x0747b5e4d6cab0e29ad37654c41c2556524b85e0114ca2d61c76375bc98fb3a4",
        "signatureKind": "eip191"
      }
    }
  ]
}
```

| Field                        | Description                                                                      |
| :--------------------------- | :------------------------------------------------------------------------------- |
| `requestId`                  | Opaque identifier that correlates this claim on submit — store it                |
| `steps`                      | One step per claimable balance. Each is a payload the recipient wallet must sign |
| `steps[].id`                 | Step identifier, echoed back as `stepId` when you submit its signature           |
| `steps[].type`               | The step type — `signature` for a message the recipient wallet must sign         |
| `steps[].data.message`       | The exact message to sign. Sign it as-is; do not reconstruct or modify it        |
| `steps[].data.signatureKind` | How to sign the message — `eip191` (a personal-sign / `personal_sign` message)   |

An empty `steps` array means there was nothing to claim — no signing or submit needed.

**Error (400):** `recipientAddress` is not a valid EVM address.

**2. Sign each step** with the recipient wallet, then **submit the signatures.**

```
POST /server/{environmentId}/flow/fees/claim/submit
Authorization: Bearer dyn_your_api_token
Content-Type: application/json
```

```json theme={"system"}
{
  "requestId": "e2b1c3d4-5678-90ab-cdef-1234567890ab",
  "signatures": [
    {
      "stepId": "step-1",
      "signature": "0xRecipientSignatureOverStep1Payload"
    }
  ]
}
```

| Field                    | Description                                           |
| :----------------------- | :---------------------------------------------------- |
| `requestId`              | The `requestId` returned when you initiated the claim |
| `signatures`             | One signature per step returned by initiate           |
| `signatures[].stepId`    | The `id` of the step this signature is for            |
| `signatures[].signature` | The recipient's signature over that step's payload    |

**Response (200):**

```json theme={"system"}
{
  "status": "DONE"
}
```

| Field    | Description                                                  |
| :------- | :----------------------------------------------------------- |
| `status` | The outcome of the claim (e.g. `DONE` when it was submitted) |

After a successful submit, re-check [balances](#check-claimable-balances) — the claimed balance should now be empty.

### Example: claim in TypeScript

```typescript title="claim-fees.ts" theme={"system"}
import { type WalletClient } from 'viem';

const API = 'https://app.dynamicauth.com/api/v0';
const ENV_ID = 'your-environment-id';
const API_TOKEN = 'dyn_your_api_token'; // must have flow.write scope

// The recipient wallet from feeConfig — it signs to authorize the claim.
declare const wallet: WalletClient;

const claimFees = async (recipientAddress: `0x${string}`) => {
  const headers = {
    Authorization: `Bearer ${API_TOKEN}`,
    'Content-Type': 'application/json',
  };

  // 1. Is there anything to claim?
  const balancesRes = await fetch(
    `${API}/server/${ENV_ID}/flow/fees/balances?recipientAddress=${recipientAddress}`,
    { headers },
  );
  const { hasClaimableFees } = await balancesRes.json();
  if (!hasClaimableFees) {
    return;
  }

  // 2. Initiate — get the steps to sign.
  const initRes = await fetch(`${API}/server/${ENV_ID}/flow/fees/claim`, {
    body: JSON.stringify({ recipientAddress }),
    headers,
    method: 'POST',
  });
  const { requestId, steps } = await initRes.json();

  // 3. Sign each step's message with the recipient wallet.
  // data.signatureKind is `eip191` — viem's signMessage produces exactly that.
  const signatures = await Promise.all(
    steps.map(async (step: { data: { message: string }; id: string }) => ({
      signature: await wallet.signMessage({
        account: recipientAddress,
        message: step.data.message,
      }),
      stepId: step.id,
    })),
  );

  // 4. Submit the signatures.
  const submitRes = await fetch(
    `${API}/server/${ENV_ID}/flow/fees/claim/submit`,
    {
      body: JSON.stringify({ requestId, signatures }),
      headers,
      method: 'POST',
    },
  );

  return submitRes.json();
};
```

<Note>
  Sign `data.message` exactly as returned — don't reconstruct or modify it. `data.signatureKind` (`eip191`) tells you how to sign: it's a standard personal-sign message, so most libraries' `signMessage` (for example viem's) produce the right signature directly.
</Note>
