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

> Integrate stablecoin-to-fiat payouts through the Dfns API: quote, create, confirm, and track bank deposits from your custody operations.

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/payouts).

## 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/payouts)
* `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                                                                  |

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