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

# Process payouts via API (Borderless and Circle Mint)

> Integrate stablecoin-to-fiat payouts through the DFNS API with two off-ramp providers, Borderless or Circle Mint. Quote, create, confirm, and track bank deposits from your custody operations, or choose an alternative to Borderless with provider CircleMint.

export const Get = props => {
  return <code style={{
    paddingLeft: 0,
    paddingTop: 0
  }}>
      <Badge color="green" size="sm">GET</Badge>
      <span style={{
    marginLeft: '0.5em'
  }}>{props.children}</span>
    </code>;
};

export const Post = props => {
  return <code style={{
    paddingLeft: 0,
    paddingTop: 0
  }}>
      <Badge color="blue" size="sm">POST</Badge>
      <span style={{
    marginLeft: '0.5em'
  }}>{props.children}</span>
    </code>;
};

This guide covers the API flow for converting stablecoins to fiat bank deposits using [Payouts](/features/fiat-on-off-ramps).

<Note>
  Payouts support two providers. The lifecycle below uses **Borderless**; for **Circle Mint** (redeem to a linked Circle bank account or your Circle Mint balance), see [Circle Mint payouts](#circle-mint-payouts).
</Note>

## Prerequisites

* A [Borderless](https://www.borderless.xyz) account with completed KYB verification
* Borderless API key configured in your org settings (see [dashboard setup](/guides/payouts#connect-borderless))
* A wallet holding a [supported stablecoin](/features/fiat-on-off-ramps)
* `Payouts:Create`, `Payouts:Read`, and `Payouts:Write` [permissions](/core-concepts/roles-and-permissions)

## Payout lifecycle

<Steps>
  <Step title="Get a quote">
    <Post>/payouts/quote</Post> : [Request payout quote](/api-reference/payouts/request-payout-quote)

    Request a quote to see the estimated fiat amount and fees before creating a payout.

    ```json Request theme={null}
    POST /payouts/quote

    {
      "walletId": "wa-5pfuu-9euek-h0odgb6snva8ph3k",
      "asset": {
        "kind": "Erc20",
        "amount": "1000000000",
        "contract": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"
      },
      "fiatCurrency": "USD",
      "provider": "Borderless",
      "country": "US"
    }
    ```

    ```json Response theme={null}
    {
      "provider": "Borderless",
      "asset": {
        "kind": "Erc20",
        "amount": "1000000000",
        "contract": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
        "network": "Polygon",
        "metadata": {
          "symbol": "USDC",
          "decimals": 6,
          "verified": true
        }
      },
      "timestamp": "2025-03-01T12:00:00.000Z",
      "quotes": [
        {
          "offer": {
            "fiatCurrency": "USD",
            "amount": 985.50,
            "fees": 14.50
          }
        }
      ]
    }
    ```

    Quotes are informational. They don't lock in a rate. The `amount` field in the quote response is the net fiat the recipient receives after fees.

    <Tip>
      The quote endpoint does not require a [user action signature](/guides/developers/signing-requests), so you can request quotes without prompting for user approval.
    </Tip>
  </Step>

  <Step title="Create a payout">
    <Post>/payouts</Post> : [Create payout](/api-reference/payouts/create-payout)

    Create a payout to start the off-ramp process. This requires Borderless-specific fields: your account ID, payment instructions ID (the destination bank account), and a payment purpose.

    ```json Request theme={null}
    POST /payouts

    {
      "walletId": "wa-5pfuu-9euek-h0odgb6snva8ph3k",
      "asset": {
        "kind": "Erc20",
        "amount": "1000000000",
        "contract": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"
      },
      "fiatCurrency": "USD",
      "provider": "Borderless",
      "country": "US",
      "borderlessAccountId": "your-borderless-account-id",
      "paymentInstructionsId": "your-payment-instructions-id",
      "paymentPurpose": "salary payment",
      "externalId": "invoice-2025-001"
    }
    ```

    ```json Response theme={null}
    {
      "id": "pyt-4ihii-9lonb-tmhjo9bor4odmb5h",
      "walletId": "wa-5pfuu-9euek-h0odgb6snva8ph3k",
      "fiatCurrency": "USD",
      "asset": {
        "kind": "Erc20",
        "amount": "1000000000",
        "contract": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
        "network": "Polygon",
        "metadata": {
          "symbol": "USDC",
          "decimals": 6,
          "verified": true
        }
      },
      "status": "Processing",
      "provider": "Borderless",
      "dateCreated": "2025-03-01T12:01:00.000Z",
      "data": {
        "borderlessAccountId": "your-borderless-account-id",
        "paymentInstructionsId": "your-payment-instructions-id",
        "paymentPurpose": "salary payment",
        "executionStatus": "Initializing",
        "country": "US"
      }
    }
    ```

    Use `externalId` for idempotency. If you retry with the same `externalId`, the existing payout is returned instead of creating a duplicate.

    **Payment purposes**

    The `paymentPurpose` field must be one of: `salary payment`, `personal remittance`, `rent payment`, `property purchase`, `owned account abroad`, `advertising expenses`, `advisory fees`, `business insurance`, `construction`, `delivery fees`, `education`, `exports`, `donation`, `hotel`, `loan payment`, `maintenance expenses`, `medical expense`, `office expenses`, `royalty fees`, `service charge`, `shares investment`, `tax payment`, `transportation fees`, `travel`, `utility bills`, `other`.
  </Step>

  <Step title="Wait for action required">
    After creation, DFNS coordinates with Borderless to obtain a deposit address. When ready, the payout advances to `AwaitingPayoutConfirmation` and DFNS fires a [`payout.action.required`](/api-reference/payouts/payout-action-required) webhook. Subscribe to the webhook for real-time notification (recommended for automation)

    You can also poll <Get>/payouts/\{payoutId}</Get> ([Get payout status](/api-reference/payouts/get-payout-status)) and check for `data.executionStatus === "AwaitingPayoutConfirmation"`.

    The webhook payload contains the full payout object (same as [Get payout](/api-reference/payouts/get-payout-status)) with the `depositAddress` in `data`. See the [`payout.action.required`](/api-reference/payouts/payout-action-required) event reference for the full schema.

    <Warning>
      The payout expires if not confirmed within 24 hours.
    </Warning>
  </Step>

  <Step title="Confirm the payout">
    <Post>/payouts/\{payoutId}/action</Post> : [Create payout action](/api-reference/payouts/create-payout-action)

    Confirm the payout by echoing back the transfer details. DFNS validates these match the payout data, then submits the on-chain transfer from your wallet.

    ```json Request theme={null}
    POST /payouts/{payoutId}/action

    {
      "action": "Confirm",
      "walletId": "wa-5pfuu-9euek-h0odgb6snva8ph3k",
      "transfer": {
        "kind": "Erc20",
        "amount": "1000000000",
        "contract": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
        "to": "0x1234567890abcdef1234567890abcdef12345678"
      }
    }
    ```

    The `transfer.to` address must match the `depositAddress` from the payout data, and the `amount` must match the payout's asset amount. This is a deliberate confirmation step. You're approving that the correct amount goes to the correct address.

    After confirmation, DFNS handles the rest: submitting the transfer, waiting for on-chain confirmation, and monitoring until Borderless confirms fiat disbursement.

    <Note>
      Only the user who created the payout can confirm or cancel it.
    </Note>
  </Step>
</Steps>

## Cancel a payout

<Post>/payouts/\{payoutId}/action</Post> : [Create payout action](/api-reference/payouts/create-payout-action)

To cancel a payout that hasn't been confirmed yet:

```json Request theme={null}
POST /payouts/{payoutId}/action

{
  "action": "Cancel"
}
```

Cancellation is only possible while the payout is awaiting confirmation.

## Check payout status

<Get>/payouts/\{payoutId}</Get> : [Get payout status](/api-reference/payouts/get-payout-status)

Poll a payout to track its progress:

```json Request theme={null}
GET /payouts/{payoutId}
```

The response includes both the top-level `status` and the granular `data.executionStatus`. The top-level `status` values are:

| Status | Meaning |
| - | - |
| `Processing` | Payout is active, waiting for provider instructions, user confirmation, or settlement |
| `Completed` | Fiat has been disbursed to the bank account |
| `Failed` | Non-recoverable error |
| `Rejected` | Transfer was rejected by a DFNS [policy](/core-concepts/policies) |
| `Expired` | Timed out waiting for confirmation (24h) or transfer submission (4h) |
| `Canceled` | Canceled by the user |

## Circle Mint payouts

Payouts also support **Circle Mint** as a provider, redeeming USDC or EURC from your wallet to fiat. Use it when your organization settles through [Circle Mint](https://www.circle.com/circle-mint). It uses the same organization connection as [Payins](/guides/developers/payins) — one Circle Mint API key on the Integrations page enables both on- and off-ramp.

Circle Mint payouts don't use the Borderless `borderlessAccountId` / `paymentInstructionsId` / `paymentPurpose` fields. Instead you set `provider: "CircleMint"` and a `destination`:

```json Request theme={null}
POST /payouts

{
  "walletId": "wa-5pfuu-9euek-h0odgb6snva8ph3k",
  "asset": {
    "kind": "Erc20",
    "amount": "1000000000",
    "contract": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
  },
  "fiatCurrency": "USD",
  "provider": "CircleMint",
  "destination": {
    "type": "bank",
    "bankId": "<bank account id linked on Circle>",
    "rail": "wire"
  }
}
```

The `destination` is one of:

* **`bank`** — wire the fiat to a bank account you linked on Circle's website. Requires `bankId` and a `rail` (`wire`, `sepa`, or `sepa_instant`).
* **`balance`** — hold the redeemed fiat in your organization's Circle Mint balance: `{ "type": "balance" }`.

DFNS then runs the redemption: it sends the wallet's stablecoin on-chain to Circle, waits for Circle to settle the deposit, and creates the fiat payout to your chosen destination. USD redeems USDC and EUR redeems EURC. Request a quote first with [`POST /payouts/quote`](/api-reference/payouts/request-payout-quote) using `provider: "CircleMint"`, and track progress with [Get payout status](/api-reference/payouts/get-payout-status) — the top-level `status` values are the same as above, and `data.executionStatus` reflects the Circle Mint redemption steps. See the [Create payout](/api-reference/payouts/create-payout) reference for the full request and response schema.

## Policies

Payouts create a transfer from your wallet to the provider's deposit address. If you have [policies](/core-concepts/policies) configured on wallet transfers, the transfer may require approval. The top-level `status` stays `Processing`, but `data.executionStatus` changes to `PendingPolicyApproval` until the policy is resolved.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.