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

# Payouts (SEPA and ACH)

> Send bank transfers to your customers' beneficiaries: SEPA Instant in EUR, or ACH in USD to bank accounts in the United States, paid in USD from your ops balances

**Pay out to a bank account in Europe or in the United States with four API calls.** You fund a balance in USDC, your verified customer saves the bank account of a beneficiary, and each payout is sent by SEPA Instant in EUR or by ACH in USD. You get a firm quote before anything is debited, and a webhook at every step.

<CardGroup cols={3}>
  <Card title="EUR, SEPA Instant" icon="building-columns">
    Transfers in EUR to bank accounts in 45 countries, including countries outside the EU. Funds usually arrive within minutes.
  </Card>

  <Card title="USD, ACH (United States)" icon="flag-usa">
    Transfers in USD to checking and savings accounts in the United States, with no currency conversion. Paid from your Ops Others balance.
  </Card>

  <Card title="Quote before you pay" icon="file-invoice-dollar">
    Each quote gives the amount received, the fee and the total debited. Nothing is debited until you confirm. A failed transfer is refunded automatically.
  </Card>
</CardGroup>

| | SEPA | ACH |
| - | - | - |
| **Beneficiary** | Bank account in a SEPA country (`rail` `sepa`) | Bank account in the United States (`rail` `us`) |
| **Received by the beneficiary** | EUR | USD, no conversion |
| **Paid from** | Payout SEPA balance (`payout_sepa`) | Ops Others balance (`others`) |
| **Amount per payout** | 25 to 2,499 USD | 100 to 2,499 USD |
| **`rail` of the quote and payout** | `sepa` | `ach` |
| **Typical delay** | Usually within minutes | A few business days |

The endpoints, the fees, the statuses and the webhooks are the same for both. This page first describes the SEPA flow, then what changes for [ACH payouts to the United States](#ach-payouts-to-the-united-states).

## How it works

<Steps>
  <Step title="List the countries">
    `GET /payouts/countries` returns the countries you can pay to (SEPA countries and the United States) and the regions or states of each country.
  </Step>

  <Step title="Save a beneficiary">
    `POST /payouts/beneficiaries` saves the bank account of a beneficiary for a verified customer.
  </Step>

  <Step title="Request a quote">
    `POST /payouts/quotes` prices the payout. Nothing is debited.
  </Step>

  <Step title="Confirm with your reference">
    `POST /payouts` debits your balance (Payout SEPA for SEPA, Ops Others for ACH) and sends the payout. It returns `202` with status `pending`.
  </Step>

  <Step title="Follow the webhooks">
    `payout.processing` when the payout is sent to the bank, then `payout.completed`, or `payout.failed` followed by `payout.refunded`.
  </Step>
</Steps>

## Prerequisites

### A verified customer

Only a customer with `verification_level` `verified` or `full` (identity verification approved) and status `active` can have beneficiaries and send payouts. Otherwise the call returns `422 VALIDATION_ERROR`:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Payouts require a verified customer (identity verification approved).",
    "details": {
      "required_level": "verified",
      "customer_level": "basic"
    }
  }
}
```

See [KYC levels](/guides/kyc-levels) and the [KYC flow](/guides/kyc-flow) to verify a customer.

### A funded Payout SEPA balance

Payouts are paid in USD from a dedicated ops balance: **Ops Payout SEPA** (`balance_type` `payout_sepa`). It is funded with USDC like your other [ops balances](/guides/ops-balance):

1. In your [partner dashboard](https://kemycard.com/en/api), open the **Ops balance** section and request the deposit address of the Payout SEPA balance.
2. Send USDC to this address. The balance is credited once the deposit is confirmed, minus the [deposit fee](/getting-started/plans#ops-balance-deposits) of your plan.

Each SEPA payout debits the amount **plus** the fee from this balance. ACH payouts to the United States are paid from your **Ops Others** balance instead: see [Funding from Ops Others](#funding-from-ops-others).

### Your plan

Payouts (SEPA and ACH) are available on **every plan, including Découverte**. On Découverte, payouts are limited to **5,000 USD per calendar month**, all customers combined, SEPA and ACH together. See [Fees and limits](#fees-and-limits).

## Step 1: List the countries

```bash theme={null}
curl -X GET https://api.kemycard.com/v1/payouts/countries \
  -H "Authorization: Bearer sk_live_YOUR_KEY"
```

```json theme={null}
{
  "success": true,
  "data": [
    {
      "country": "DE",
      "rail": "sepa",
      "regions": [
        { "value": "BB", "label": "Brandenburg (BB)" },
        { "value": "BE", "label": "Berlin (BE)" },
        { "value": "BW", "label": "Baden-Württemberg (BW)" }
      ]
    },
    {
      "country": "US",
      "rail": "us",
      "regions": [
        { "value": "alabama", "label": "Alabama" },
        { "value": "new_york", "label": "New York" },
        { "value": "texas", "label": "Texas" }
      ]
    }
  ]
}
```

Every entry has a `rail`: `sepa` for a SEPA country, `us` for the United States. The `country` of a SEPA beneficiary must be one of the `sepa` countries, and its `state_province` one of the `value` fields listed for that country. For the United States, `regions` lists the 57 accepted states and territories (for example `new_york` or `district_of_columbia`). Any other value is refused.

## Step 2: Save a beneficiary

```bash theme={null}
curl -X POST https://api.kemycard.com/v1/payouts/beneficiaries \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_code": "019abc12-3456-7890-abcd-ef0123456789",
    "type": "individual",
    "name": "JANE DOE",
    "bank_name": "Test Bank",
    "iban": "DE89370400440532013000",
    "bic_swift": "COBADEFFXXX",
    "street": "1 Test Street",
    "city": "Berlin",
    "state_province": "BE",
    "postal_code": "10115",
    "country": "DE"
  }'
```

| Field | Required | Description |
| - | - | - |
| `customer_code` | Yes | Code of the customer (`verified` or `full`, `active`) |
| `rail` | No | `sepa` (default). Use `us` for a bank account in the United States: see [ACH payouts](#ach-payouts-to-the-united-states) |
| `type` | No | `individual` (default) or `business` |
| `name` | Yes | Name of the account holder (max 140 characters) |
| `bank_name` | Yes | Name of the bank (max 140 characters) |
| `iban` | Yes | Full IBAN. It must belong to `country` |
| `bic_swift` | Yes | BIC / SWIFT code of the bank |
| `street` | Yes | Street address (max 200 characters) |
| `city` | Yes | City (max 100 characters) |
| `state_province` | Yes | A `value` from `GET /payouts/countries` for this country |
| `postal_code` | Yes | Postal code (max 20 characters) |
| `country` | Yes | Country of the bank account (ISO 3166-1 alpha-2) |

Response `201`:

```json theme={null}
{
  "success": true,
  "data": {
    "code": "0199e3a0-1b2c-7d3e-8f40-5a6b7c8d9e0f",
    "customer_code": "019abc12-3456-7890-abcd-ef0123456789",
    "type": "individual",
    "name": "JANE DOE",
    "bank_name": "Test Bank",
    "iban_masked": "DE89 **** 3000",
    "bic_swift": "COBADEFFXXX",
    "address": {
      "street": "1 Test Street",
      "city": "Berlin",
      "state_province": "BE",
      "postal_code": "10115",
      "country": "DE"
    },
    "rail": "sepa",
    "created_at": "2026-10-15T10:00:00+00:00"
  }
}
```

* The full IBAN is never returned: responses only show `iban_masked`.
* A customer can have up to **50** active beneficiaries. The same IBAN cannot be saved twice for a customer.
* Invalid fields are returned in `details.fields`, one message per field:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid beneficiary.",
    "details": {
      "fields": {
        "iban": "The IBAN does not match the selected country.",
        "state_province": "Please choose the region from the list."
      }
    }
  }
}
```

Manage beneficiaries with `GET /payouts/beneficiaries?customer_code=…`, `GET /payouts/beneficiaries/{code}` and `DELETE /payouts/beneficiaries/{code}`. To change the bank details of a beneficiary, delete it and save it again.

## Step 3: Request a quote

```bash theme={null}
curl -X POST https://api.kemycard.com/v1/payouts/quotes \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_code": "019abc12-3456-7890-abcd-ef0123456789",
    "beneficiary_code": "0199e3a0-1b2c-7d3e-8f40-5a6b7c8d9e0f",
    "amount_usd": 100
  }'
```

`amount_usd` is the amount of the payout in USD, from **25** to **2,499**, with at most 2 decimals. The fee is added on top.

Response `201` (example on the Starter plan, fee \$5 + 2%):

```json theme={null}
{
  "success": true,
  "data": {
    "quote_code": "0199e3a1-7c2b-7d40-9a51-3f6e2b8c4d10",
    "rail": "sepa",
    "status": "open",
    "customer_code": "019abc12-3456-7890-abcd-ef0123456789",
    "beneficiary": {
      "code": "0199e3a0-1b2c-7d3e-8f40-5a6b7c8d9e0f",
      "name": "JANE DOE",
      "iban_masked": "DE89 **** 3000",
      "bank_name": "Test Bank",
      "country": "DE"
    },
    "amount_usd": 100.0,
    "fee_usd": 7.0,
    "total_debited_usd": 107.0,
    "received_amount": 86.0,
    "received_currency": "EUR",
    "amount_eur": 86.0,
    "exchange_rate": 0.86,
    "balance_type": "payout_sepa",
    "expires_at": "2026-10-15T10:07:00+00:00",
    "created_at": "2026-10-15T10:00:00+00:00"
  }
}
```

| Field | Description |
| - | - |
| `rail` | `sepa`, or `ach` for a beneficiary in the United States |
| `received_amount` | Amount the beneficiary will receive, in `received_currency` (`EUR` for SEPA, `USD` for ACH) |
| `amount_eur` | Amount in EUR the beneficiary will receive (`null` for ACH) |
| `exchange_rate` | `amount_eur` / `amount_usd` (`null` for ACH) |
| `fee_usd` | KemyCard fee of your plan |
| `total_debited_usd` | Amount + fee, debited when you confirm from the balance given by `balance_type` |
| `balance_type` | `payout_sepa` for SEPA, `others` for ACH |
| `expires_at` | Confirm before this date, otherwise request a new quote |

<Info>
  Nothing is debited when a quote is created. A quote that is not confirmed simply expires.
</Info>

The quote already runs the checks of the payout: verified and active customer, Découverte monthly limit, and a balance (Payout SEPA, or Ops Others for ACH) that covers `total_debited_usd`. They are run again at confirmation.

## Step 4: Confirm with your reference

```bash theme={null}
curl -X POST https://api.kemycard.com/v1/payouts \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "quote_code": "0199e3a1-7c2b-7d40-9a51-3f6e2b8c4d10",
    "reference": "order-1001"
  }'
```

`total_debited_usd` is debited from your Payout SEPA balance (Ops Others for ACH) and the payout is created with status `pending`. Response `202`:

```json theme={null}
{
  "success": true,
  "data": {
    "code": "0199e3a1-7c2b-7d40-9a51-3f6e2b8c4d10",
    "reference": "order-1001",
    "rail": "sepa",
    "status": "pending",
    "customer_code": "019abc12-3456-7890-abcd-ef0123456789",
    "beneficiary": {
      "code": "0199e3a0-1b2c-7d3e-8f40-5a6b7c8d9e0f",
      "name": "JANE DOE",
      "iban_masked": "DE89 **** 3000",
      "bank_name": "Test Bank",
      "country": "DE"
    },
    "amount_usd": 100.0,
    "fee_usd": 7.0,
    "total_debited_usd": 107.0,
    "received_amount": 86.0,
    "received_currency": "EUR",
    "amount_eur": 86.0,
    "exchange_rate": 0.86,
    "balance_type": "payout_sepa",
    "failure_reason": null,
    "created_at": "2026-10-15T10:00:00+00:00",
    "confirmed_at": "2026-10-15T10:01:00+00:00",
    "sent_at": null,
    "completed_at": null,
    "refunded_at": null
  }
}
```

The `code` of the payout is the `quote_code`. The payout is then sent to the bank asynchronously: follow it with the webhooks below, or with `GET /payouts/{code}` and `GET /payouts?status=…&customer_code=…`.

If the quote has expired by the time the payout is sent to the bank, KemyCard requests a new quote automatically. If the amount received would be **more than 1% lower** than the quote you accepted, the payout is cancelled and refunded instead.

### Idempotency with `reference`

`reference` is **required** and **unique per partner account**: 1 to 100 characters among letters, digits and `- _ . : /`. Use your own ID, for example your order or withdrawal ID.

| You send | Result |
| - | - |
| A new `reference` with an open quote | `202` — payout created, balance debited |
| The same `quote_code` and the same `reference` again | `200` — the payout already created is returned, nothing is debited again |
| A `reference` already used by another payout | `422 VALIDATION_ERROR` with `details.reference` and `details.payout_code` |

If a call times out or your server restarts, send the same request again: you never pay twice.

## Step 5: Follow the webhooks

| Event | Sent when |
| - | - |
| `payout.processing` | The payout has been sent to the bank |
| `payout.completed` | The payout has been credited to the beneficiary's bank |
| `payout.failed` | The bank transfer could not be completed |
| `payout.refunded` | The amount and the fee have been credited back to the balance that paid the payout (Payout SEPA, or Ops Others for ACH) |

The `data` of each event is the payout object, the same as `GET /payouts/{code}`, with its `rail`, `received_amount` and `received_currency`. When you confirm a payout, you also receive [`balance.debited`](/guides/webhooks#balance-events) with `reason` `payout_sepa`, `payout_code` and `reference`, for SEPA and ACH payouts alike: `balance_type` tells you which balance was debited (`payout_sepa` or `others`).

With SEPA Instant, funds usually arrive within minutes. An ACH transfer can take a few business days to arrive. Subscribe to these events when you [register your webhook endpoint](/guides/webhooks), or use `"*"`.

## Payout statuses

```
pending ──▶ processing ──▶ completed
                 │
                 └──▶ failed ──▶ refunded
```

| Status | Meaning |
| - | - |
| `pending` | Accepted, your balance is debited, not yet sent to the bank |
| `processing` | Sent to the bank |
| `completed` | Credited to the beneficiary's bank account |
| `failed` | The bank transfer could not be completed |
| `refunded` | The amount and the fee have been credited back to the balance that paid the payout (`failure_reason` is set) |

A failed payout is always refunded automatically: you do not need to do anything. To send it again, request a new quote.

## ACH payouts to the United States

Send USD by ACH to a checking or savings account in the United States. The beneficiary receives USD: there is no currency conversion. You use the same endpoints and the same flow as for SEPA (beneficiary, quote, confirmation with your `reference`, webhooks). This section describes what changes.

<Note>
  RTP (Real-Time Payments) is not available. Payouts to the United States are sent by ACH only.
</Note>

### Prerequisites

* **A verified customer**, exactly as for SEPA: `verification_level` `verified` or `full` and status `active`. See [A verified customer](#a-verified-customer).
* **A funded Ops Others balance**: ACH payouts are paid from your Ops Others balance (`others`), not from your Payout SEPA balance. See [Funding from Ops Others](#funding-from-ops-others).
* **Your plan**: ACH payouts are available on every plan, including Découverte, with the same fees as SEPA payouts.

### Funding from Ops Others

Each ACH payout debits the amount **plus** the fee from your **Ops Others** balance (`balance_type` `others`), the balance that also pays wallets, virtual accounts, KYC and plan payments. Your Payout SEPA balance is never used for ACH payouts, and the Ops Others balance is never used for SEPA payouts.

The Ops Others balance is funded with USDC like your other [ops balances](/guides/ops-balance#fund-your-balance). On this balance:

* the debit of an ACH payout is recorded with the nature `PAYOUT`;
* the refund of a failed ACH payout (amount + fee) is credited back with the nature `REFUND`.

When you confirm an ACH payout, you receive [`balance.debited`](/guides/webhooks#balance-events) with `reason` `payout_sepa` (the same reason as for SEPA payouts), `balance_type` `others`, `payout_code` and `reference`.

If the Ops Others balance does not cover `total_debited_usd`, the quote or the confirmation returns `400 INSUFFICIENT_BALANCE`:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Insufficient Ops Others balance.",
    "details": {
      "required": 515.0,
      "available": 200.0,
      "balance_type": "others"
    }
  }
}
```

### Save a US beneficiary

Send `"rail": "us"` to `POST /payouts/beneficiaries`:

```bash theme={null}
curl -X POST https://api.kemycard.com/v1/payouts/beneficiaries \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_code": "019abc12-3456-7890-abcd-ef0123456789",
    "rail": "us",
    "type": "individual",
    "name": "JOHN SMITH",
    "account_type": "checking",
    "account_number": "123456789012",
    "routing_number": "021000021",
    "bank_name": "Test Bank USA",
    "bank_street": "1 Bank Plaza",
    "bank_city": "New York",
    "bank_state": "new_york",
    "bank_postal_code": "10001",
    "street": "10 Main Street",
    "city": "Austin",
    "state_province": "texas",
    "postal_code": "73301"
  }'
```

| Field | Required | Description |
| - | - | - |
| `customer_code` | Yes | Code of the customer (`verified` or `full`, `active`) |
| `rail` | Yes | `us` |
| `type` | No | `individual` (default) or `business` |
| `name` | Yes | Name of the account holder (max 140 characters) |
| `account_type` | Yes | `checking` or `savings` |
| `account_number` | Yes | Account number, 4 to 17 digits. Spaces and dashes are removed |
| `routing_number` | Yes | ABA routing number of the bank, 9 digits. Its check digit is verified |
| `bank_name` | Yes | Name of the bank (max 140 characters) |
| `bank_street` | Yes | Street address of the bank (max 200 characters) |
| `bank_city` | Yes | City of the bank (max 100 characters) |
| `bank_state` | Yes | State of the bank: a `value` from `GET /payouts/countries` for `US` |
| `bank_postal_code` | Yes | ZIP code of the bank: 5 digits, or 5 + 4 digits (`12345-6789`) |
| `street` | Yes | Street address of the account holder (max 200 characters) |
| `city` | Yes | City of the account holder (max 100 characters) |
| `state_province` | Yes | State of the account holder: a `value` from `GET /payouts/countries` for `US` |
| `postal_code` | Yes | ZIP code of the account holder: 5 digits, or 5 + 4 digits |

The address of the account holder (`street`, `city`, `state_province`, `postal_code`) must be in the **United States**. `country` is not sent: it is always `US`.

Response `201`:

```json theme={null}
{
  "success": true,
  "data": {
    "code": "0199e3b2-4d5e-7f60-8a71-2b3c4d5e6f70",
    "customer_code": "019abc12-3456-7890-abcd-ef0123456789",
    "rail": "us",
    "type": "individual",
    "name": "JOHN SMITH",
    "bank_name": "Test Bank USA",
    "account_type": "checking",
    "account_number_masked": "**** 9012",
    "routing_number": "021000021",
    "bank_address": {
      "street": "1 Bank Plaza",
      "city": "New York",
      "state": "new_york",
      "postal_code": "10001",
      "country": "US"
    },
    "address": {
      "street": "10 Main Street",
      "city": "Austin",
      "state_province": "texas",
      "postal_code": "73301",
      "country": "US"
    },
    "created_at": "2026-10-15T10:00:00+00:00"
  }
}
```

* The full account number is never returned: responses only show `account_number_masked` (last 4 digits).
* The same bank account (same `routing_number` and `account_number`) cannot be saved twice for a customer: the call returns `422 VALIDATION_ERROR` with `details.field` = `account_number`.
* US beneficiaries count in the limit of **50** active beneficiaries per customer.
* Invalid fields are returned in `details.fields`, one message per field:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid beneficiary.",
    "details": {
      "fields": {
        "account_number": "The account number must have 4 to 17 digits.",
        "routing_number": "The routing number (ABA) is not valid. It is the 9-digit number of the US bank.",
        "bank_state": "Please choose the state from the list.",
        "postal_code": "The ZIP code must have 5 digits (or 5 + 4 digits)."
      }
    }
  }
}
```

### Quote and send an ACH payout

Request a quote with the `beneficiary_code` of the US beneficiary. `amount_usd` is from **100** to **2,499** USD, with at most 2 decimals:

```bash theme={null}
curl -X POST https://api.kemycard.com/v1/payouts/quotes \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_code": "019abc12-3456-7890-abcd-ef0123456789",
    "beneficiary_code": "0199e3b2-4d5e-7f60-8a71-2b3c4d5e6f70",
    "amount_usd": 500
  }'
```

Response `201` (example on the Starter plan, fee \$5 + 2%):

```json theme={null}
{
  "success": true,
  "data": {
    "quote_code": "0199e3b3-8e9f-7a01-9b12-4c5d6e7f8091",
    "rail": "ach",
    "status": "open",
    "customer_code": "019abc12-3456-7890-abcd-ef0123456789",
    "beneficiary": {
      "code": "0199e3b2-4d5e-7f60-8a71-2b3c4d5e6f70",
      "name": "JOHN SMITH",
      "account_number_masked": "**** 9012",
      "routing_number": "021000021",
      "bank_name": "Test Bank USA",
      "country": "US"
    },
    "amount_usd": 500.0,
    "fee_usd": 15.0,
    "total_debited_usd": 515.0,
    "received_amount": 500.0,
    "received_currency": "USD",
    "amount_eur": null,
    "exchange_rate": null,
    "balance_type": "others",
    "expires_at": "2026-10-15T10:10:00+00:00",
    "created_at": "2026-10-15T10:00:00+00:00"
  }
}
```

* `rail` is `ach`. `received_amount` is the amount in USD the beneficiary will receive. `amount_eur` and `exchange_rate` are always `null` for ACH.
* `balance_type` is `others`: `total_debited_usd` will be debited from your Ops Others balance when you confirm.
* Confirm before `expires_at`, otherwise request a new quote.

Confirm with `POST /payouts` and your `reference`, exactly as for SEPA (see [Step 4](#step-4-confirm-with-your-reference)). Response `202`:

```json theme={null}
{
  "success": true,
  "data": {
    "code": "0199e3b3-8e9f-7a01-9b12-4c5d6e7f8091",
    "reference": "withdrawal-2001",
    "rail": "ach",
    "status": "pending",
    "customer_code": "019abc12-3456-7890-abcd-ef0123456789",
    "beneficiary": {
      "code": "0199e3b2-4d5e-7f60-8a71-2b3c4d5e6f70",
      "name": "JOHN SMITH",
      "account_number_masked": "**** 9012",
      "routing_number": "021000021",
      "bank_name": "Test Bank USA",
      "country": "US"
    },
    "amount_usd": 500.0,
    "fee_usd": 15.0,
    "total_debited_usd": 515.0,
    "received_amount": 500.0,
    "received_currency": "USD",
    "amount_eur": null,
    "exchange_rate": null,
    "balance_type": "others",
    "failure_reason": null,
    "created_at": "2026-10-15T10:00:00+00:00",
    "confirmed_at": "2026-10-15T10:01:00+00:00",
    "sent_at": null,
    "completed_at": null,
    "refunded_at": null
  }
}
```

The idempotency rules of `reference`, the statuses and the `payout.*` webhooks are the same as for SEPA. If an ACH payout fails, `total_debited_usd` is credited back to your Ops Others balance and `failure_reason` says so.

### Timing

An ACH transfer can take **a few business days** to arrive on the beneficiary's account. The payout moves to `processing` when it is sent to the bank, then to `completed` once the funds have arrived: rely on the `payout.completed` webhook (or `GET /payouts/{code}`) rather than on a fixed delay.

## Fees and limits

### Fee per payout

The fee is the same for SEPA and ACH payouts. It is charged on top of the amount and debited with it, from your Payout SEPA balance (SEPA) or your Ops Others balance (ACH).

| | Découverte | Starter | Business | Premium |
| - | - | - | - | - |
| **Fee per payout (SEPA or ACH)** | \$6 + 2.5% | \$5 + 2% | \$5 + 1.75% | \$5 + 1.5% |
| **Example: payout of 100 USD** | 108.50 USD debited | 107 USD debited | 106.75 USD debited | 106.50 USD debited |

### Deposit on the Payout SEPA balance

| | Découverte | Starter | Business | Premium |
| - | - | - | - | - |
| **Deposit fee** | \$1 + 1% | \$0.75 + 0.75% | \$0.50 + 0.50% | \$0.25 + 0.25% |

Deposits on the Ops Others balance, which pays ACH payouts, carry the deposit fee of that balance: see [Ops balance deposits](/getting-started/plans#ops-balance-deposits).

### Limits

| Limit | Value |
| - | - |
| Amount per SEPA payout | 25 to 2,499 USD |
| Amount per ACH payout | 100 to 2,499 USD |
| Beneficiaries per customer | 50 active (SEPA and US combined) |
| Découverte plan | 5,000 USD of payouts per calendar month (UTC), all customers combined, SEPA and ACH together, counting the payout amounts without fees |

When the Découverte limit would be exceeded, the quote or the confirmation returns `403 PLAN_LIMIT_REACHED`:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "PLAN_LIMIT_REACHED",
    "message": "The Découverte plan allows up to 5,000 USD of payouts per month. Upgrade your plan to send more.",
    "details": {
      "plan": "decouverte",
      "monthly_limit_usd": 5000,
      "sent_this_month_usd": 4900
    }
  }
}
```

## Test mode

With a `sk_test_` key, every payout endpoint validates your request (required fields, beneficiary fields including the US fields, format of `amount_usd` and `reference`) and returns simulated data. A US beneficiary created in test mode is returned with its US fields, but quotes and payouts in test mode are always simulated SEPA payouts (`rail` `sepa`, EUR). Nothing is stored, nothing is debited, nothing is sent to a bank and no webhook is sent. `GET /payouts/countries` returns the real list of countries. See [Test mode](/getting-started/test-mode).

## Error handling

| HTTP | Code | When |
| - | - | - |
| 422 | `VALIDATION_ERROR` | Missing or invalid field, invalid `rail` (`details.allowed`), amount out of range (`details.min_amount_usd`, `details.max_amount_usd`), customer not verified (`details.required_level`, `details.customer_level`), customer not active when saving a beneficiary, IBAN or US bank account already saved, 50 beneficiaries reached, quote expired or no longer open, `reference` already used |
| 404 | `RESOURCE_NOT_FOUND` | Customer, beneficiary, quote or payout not found in your account |
| 400 | `INSUFFICIENT_BALANCE` | The balance of the rail does not cover `total_debited_usd` (`details`: `required`, `available`, `balance_type` = `payout_sepa` for SEPA, `others` for ACH) |
| 403 | `PLAN_LIMIT_REACHED` | Découverte monthly limit of 5,000 USD reached (`details`: `monthly_limit_usd`, `sent_this_month_usd`) |
| 403 | `FORBIDDEN` | The customer is not active (quote or confirmation) |
| 402 | `PLAN_UPGRADE_REQUIRED` | Payouts are not available on your plan |
| 400 | `OPERATION_FAILED` | Payouts are temporarily unavailable, or the bank details of the beneficiary were refused when quoting (the message says what to correct). Try again later or fix the beneficiary |
| 421 | `wrong_api_host` | The request was not sent to `https://api.kemycard.com/v1`. `kemycard.dev` is the website and does not serve the API |

```json theme={null}
{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Insufficient Payout SEPA balance.",
    "details": {
      "required": 107.0,
      "available": 50.0,
      "balance_type": "payout_sepa"
    }
  }
}
```

For an ACH payout, the message is `Insufficient Ops Others balance.` and `details.balance_type` is `others`.

See [Error Handling](/guides/error-handling) for the general error format.

## Questions

For any question about payouts, contact us at [hi@kemycard.com](mailto:hi@kemycard.com).


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