> ## 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 available for EVM and SVM, and applies to transactions that require conversion. 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–2 recipients, at most 1 per chain (EVM or Solana). A swap pays the address on the chain the fee settles on          |
| `feeConfig.recipients[].walletAddress` | The wallet that receives (and, for claims, signs for) the fee. Must be a valid EVM (`0x…`) or Solana (base58) 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 no recipients, or more than 1 recipient in the same chain (EVM or Solana),
* 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 or Solana 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

A recipient can be an EVM address (`0x…`) or a Solana address (base58), and you can mix both in one fee config. Any other address is rejected with `422`.

Add both an EVM and a Solana fee config item so you get paid on every swap. The fee is not always paid out on the chain the payer swapped from: a swap that started on Solana can deliver your fee to an EVM address.

```json theme={"system"}
"feeConfig": {
  "recipients": [
    {
      "walletAddress": "0xYourFeeRecipientWallet",
      "percentage": 0.01
    },
    {
      "walletAddress": "YourSolanaFeeRecipientWallet",
      "percentage": 0.01
    }
  ]
}
```

Use the same `percentage` on both addresses: a swap only ever pays one of the two, so your fee rate stays the same. You can add 1 recipient per chain, for a maximum of 2 in one fee config.

The token a payer swaps *from* can be on any [supported chain](/docs/overview/fireblocks-flow-api#supported-chains-and-native-tokens). Fees to a Solana recipient arrive on-chain at swap time; anything that accrues to an EVM recipient is collected with the claim flow below.

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