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

# Wallet Payout

> Pay out a customer wallet balance to their bank account via SEPA credit transfer over the API.

The Wallet Payout API pays out a customer's wallet balance to a bank account. Fynn debits the balance immediately and generates a SEPA credit transfer. How the transfer reaches your bank depends on the configured submission mode: in **manual mode** (default) you download the generated SEPA file and submit it to your bank yourself, then confirm the upload so the payout moves to `submitted`; in **automatic mode** Fynn submits it over EBICS. The payout is only finalized once the bank confirms the transfer via the account statement.

<Info>
  Bank payouts are subject to origin-based routing rules. A wallet balance can only be paid out to a bank account if it originated from a bank transfer or an in-house SEPA direct debit. Balance funded by card, PayPal, or GoCardless must be refunded through the original channel. See [Origin routing](#origin-routing) below.
</Info>

## Create a Payout

```bash theme={null}
curl -X POST https://coreapi.io/api/customers/CUSTOMER_ID/wallet/payouts \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "amount": 5000,
    "currencyCode": "EUR",
    "reference": "Wallet payout March 2026"
  }'
```

**Response (201 Created):**

```json theme={null}
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "created",
  "amount": 5000,
  "currencyCode": "EUR",
  "endingBalance": 0,
  "recipientIban": "DE89 **** **** **** **89",
  "endToEndId": "WP2026030812345678",
  "createdAt": "2026-03-08T10:00:00Z"
}
```

### Request Fields

| Field           | Type    | Required | Description                                                                      |
| --------------- | ------- | -------- | -------------------------------------------------------------------------------- |
| `amount`        | integer | Yes      | Payout amount in the smallest currency unit (cents). Must be greater than `0`    |
| `currencyCode`  | string  | Yes      | ISO 4217 currency code (e.g. `EUR`)                                              |
| `iban`          | string  | No       | Recipient IBAN. Defaults to the customer's stored bank connection (SEPA mandate) |
| `bic`           | string  | No       | Recipient BIC, when an explicit `iban` is provided                               |
| `accountHolder` | string  | No       | Recipient account holder name. Defaults to the customer's display name           |
| `reference`     | string  | No       | Remittance reference shown on the bank statement (max 140 characters)            |
| `description`   | string  | No       | Internal description of the payout (max 140 characters)                          |

<Note>
  Amounts are integers in cents, not decimals. `5000` means `50,00 EUR`.
</Note>

### Response Fields

| Field           | Type    | Description                                                        |
| --------------- | ------- | ------------------------------------------------------------------ |
| `id`            | string  | Payout UUID                                                        |
| `status`        | string  | Payout status (`created` on success)                               |
| `amount`        | integer | Payout amount in cents                                             |
| `currencyCode`  | string  | ISO 4217 currency code                                             |
| `endingBalance` | integer | Remaining wallet balance in cents after the payout was booked      |
| `recipientIban` | string  | Masked recipient IBAN                                              |
| `endToEndId`    | string  | Unique end-to-end reference used to match the bank statement entry |
| `createdAt`     | string  | ISO 8601 timestamp                                                 |

<Tip>
  Without an `iban`, the payout uses the customer's stored bank connection (the active SEPA mandate's bank account). Pass an explicit `iban` to pay out to a different account; it is recorded on the payout for audit.
</Tip>

## Payout Status

A payout moves through a fixed lifecycle:

| Status      | Description                                                                                                                                                                                                       |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `created`   | Payout booked, wallet balance debited. The credit transfer is waiting to be submitted: in manual mode until you download the SEPA file and confirm the upload, in automatic mode until Fynn submits it over EBICS |
| `submitted` | The credit transfer was submitted to the bank, either after you confirmed the upload (manual mode) or over EBICS (automatic mode)                                                                                 |
| `executed`  | The bank executed the transfer, confirmed via the account statement. Terminal state                                                                                                                               |
| `failed`    | Submission failed. The wallet booking was automatically reversed and the balance restored. Terminal state                                                                                                         |

<Warning>
  `created` does not mean the money has left the bank account yet. The payout is only complete once it reaches `executed`. Subscribe to the [webhooks](#webhooks) to react to the terminal states.
</Warning>

## Origin Routing

Fynn records the origin of every wallet inflow and enforces where the balance may flow back. A bank payout is only allowed when every funding portion of the payout comes from an allowed origin.

| Origin of the balance        | Bank payout | Correct alternative                  |
| ---------------------------- | ----------- | ------------------------------------ |
| Bank transfer                | Allowed     | Payout via SEPA credit transfer      |
| SEPA direct debit (in-house) | Allowed     | Payout via SEPA credit transfer      |
| Card (Stripe, Unzer)         | Blocked     | Refund via the original card         |
| PayPal                       | Blocked     | Refund via PayPal                    |
| GoCardless                   | Blocked     | Refund via GoCardless                |
| Missing / unknown origin     | Blocked     | Manual review, then original channel |

A blocked payout is **not** booked. The request returns `422` with an error code identifying the reason. Mixed funding follows an all-or-nothing rule: a single blocked portion blocks the entire payout.

## Error Handling

| Status Code | Error Code                               | Scenario                                                                                                                                                                              |
| ----------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `422`       | `CUSTOMER_BALANCE__NEGATIVE_NOT_ALLOWED` | Insufficient wallet balance for the requested amount                                                                                                                                  |
| `422`       | `WALLET_PAYOUT__ROUTE_NOT_ALLOWED`       | Balance origin does not permit a bank payout (e.g. card-funded)                                                                                                                       |
| `422`       | `WALLET_PAYOUT__ROUTE_UNKNOWN_ORIGIN`    | Origin of the balance is unknown and requires manual review                                                                                                                           |
| `422`       | `WALLET_PAYOUT__MISSING_BANK_DATA`       | No recipient bank connection available and no `iban` provided                                                                                                                         |
| `422`       | `WALLET_PAYOUT__NOT_CONFIGURED`          | Required configuration is missing. In manual mode this means the SEPA credit transfer settings (sender bank account); in automatic mode it additionally requires the EBICS connection |
| `404`       | (none)                                   | Unknown customer                                                                                                                                                                      |

<Note>
  When a payout is blocked or rejected, nothing is booked. The wallet balance stays exactly as it was before the request.
</Note>

## Webhooks

Subscribe to these events to react to the terminal states of a payout. Both carry the same payload shape.

| Event                    | When                                                                          |
| ------------------------ | ----------------------------------------------------------------------------- |
| `wallet.payout.executed` | The bank executed the transfer and it was confirmed via the account statement |
| `wallet.payout.failed`   | Submission failed; the wallet balance was automatically restored              |

**Payload:**

```json theme={null}
{
  "payoutId": "550e8400-e29b-41d4-a716-446655440000",
  "customerId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
  "amount": 5000,
  "currencyCode": "EUR",
  "status": "executed",
  "endToEndId": "WP2026030812345678",
  "reference": "Wallet payout March 2026",
  "failReason": null,
  "executedAt": "2026-03-10T08:15:00Z",
  "failedAt": null
}
```

| Field          | Type           | Description                                                 |
| -------------- | -------------- | ----------------------------------------------------------- |
| `payoutId`     | string         | Payout UUID                                                 |
| `customerId`   | string         | Customer UUID                                               |
| `amount`       | integer        | Payout amount in cents                                      |
| `currencyCode` | string         | ISO 4217 currency code                                      |
| `status`       | string         | `executed` or `failed`                                      |
| `endToEndId`   | string         | End-to-end reference matching the bank statement entry      |
| `reference`    | string         | Remittance reference of the payout                          |
| `failReason`   | string \| null | Reason for failure; `null` for executed payouts             |
| `executedAt`   | string \| null | ISO 8601 timestamp when executed; `null` for failed payouts |
| `failedAt`     | string \| null | ISO 8601 timestamp when failed; `null` for executed payouts |

See [Webhooks](/guide/webhooks/introduction) for signature verification and setup.

## Related Documentation

* [Guthaben auszahlen (Benutzerhandbuch)](/guide/invoices/wallet-payout) - Step-by-step guide and origin rules
* [Kundenguthaben](/guide/invoices/wallet-balance) - How the wallet balance works
* [Wallet Top-Up](/api-reference/guides/wallet-top-up) - Adding funds to a wallet balance
