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

# Payouts

> The life of a payout from creation to settlement: its states, the approval gate, where it is paid, and the events that report each step.

Follow a payout from the moment you create it to the moment its money is final.
For developers who create payouts and track them to completion.

<Note>
  **Agents:** fetch this page as plain markdown at `https://docs.usecleff.com/concepts/payouts.md`.
</Note>

Four resources cooperate to move money: the **Cleff Account** funds are drawn
from, the **Payout** that instructs the movement, the **Decision** that approves
it, and the **webhook events** that report its progress. This page is the
narrative; each resource's reference page has the field-level detail. To do it
step by step, follow [Send a payout](/guides/send-a-payout).

## Fund the payouts

Payouts draw from your Cleff Account, and a shortfall surfaces when a payout is
sent, not when it is created. Read the account's balance and the deposit
instructions to fund it with
[`GET /v1/funding/account`](/api-reference/funding-account-get). A `404` means
your Business has no Cleff Account yet: in sandbox, submit your business
profile in the dashboard; in production, finish [going live](/go-live).

## Follow a payout through its states

A payout is a single instruction: pay this Beneficiary (the payee), at this
destination, this amount. It moves through an explicit state machine:

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending_approval : create
    pending_approval --> approved : approve
    pending_approval --> rejected : reject
    approved --> canceled : provider cancels
    approved --> disbursed : money leaves
    approved --> failed : send fails
    approved --> compliance_hold : provider review
    compliance_hold --> approved : review cleared
    disbursed --> settled : return window closes
    disbursed --> returned : receiving bank returns it
    settled --> returned : late return
    rejected --> [*]
    canceled --> [*]
    failed --> [*]
    returned --> [*]
    settled --> [*]
```

| From | To | Caused by |
| - | - | - |
| (none) | `pending_approval` | You [create the payout](/api-reference/payouts-create). |
| `pending_approval` | `approved` | A Decision approves it. |
| `pending_approval` | `rejected` | A Decision rejects it. |
| `approved` | `canceled` | The provider cancels the payment before any money moves. There is no cancel request; to stop a payout that is waiting for approval, reject it. |
| `approved` | `disbursed` | The provider confirms the money has left your Cleff Account. |
| `approved` | `failed` | Sending it fails: for example, your balance is too low, or the destination's bank refuses it. |
| `approved` | `compliance_hold` | The provider pauses the payout for an anti-money-laundering or sanctions review. |
| `compliance_hold` | `approved` | The review clears, and sending resumes. |
| `disbursed` | `settled` | The window in which the receiving bank can return the money closes without a return. |
| `disbursed` | `returned` | The receiving bank sends the money back. |
| `settled` | `returned` | The receiving bank sends the money back after the return window closed, reversing a payment that was final. |

Each state means one thing, and says what you do about it:

| State | Meaning | Recovery |
| - | - | - |
| `pending_approval` | Created, with its destination fixed, and waiting for a Decision. Nothing moves yet. | [Approve](/api-reference/payouts-approve) or [reject](/api-reference/payouts-reject) it. |
| `approved` | Approved; the payment is being sent or is in flight. | Nothing. Wait for `payout.disbursed`, or for `payout.failed` or `payout.canceled`; it can also pause in `compliance_hold`. |
| `compliance_hold` | Paused for the provider's anti-money-laundering or sanctions review. No webhook event reports entering or leaving it. | Nothing through the API. It returns to `approved` when the review clears; read the payout with [`GET /v1/payouts/{payoutId}`](/api-reference/payouts-get) to see where it stands. |
| `disbursed` | The money has left your Cleff Account. Provisional: the receiving bank can still return it. | Nothing. It becomes `settled` when the return window closes. |
| `settled` | The return window closed without a return; the money is final. | Nothing. A late return can still move it to `returned`. |
| `rejected` | A Decision refused the payout before it was sent. Terminal. | Nothing on this payout. To try again, create a new one. |
| `canceled` | Canceled before any money moved. Terminal. | Nothing on this payout. To try again, create a new one. |
| `failed` | The money did not leave your Cleff Account, unless `failure_code` is `indeterminate`: then Cleff can't confirm either way. Terminal. | Read `failure_code` and follow its [recovery](/api-reference/webhook-events-payout#why-a-payout-failed). If `replaceable` is `true`, [replace the payout](#replace-a-payout-that-did-not-arrive). |
| `returned` | The money left, then the receiving bank sent it back. Terminal. | Read `failure_code` and follow its [recovery](/api-reference/webhook-events-payout#why-a-payout-failed). If `replaceable` is `true`, [replace the payout](#replace-a-payout-that-did-not-arrive). |

## Name where the payout goes

A payout is paid to one of the Beneficiary's **destinations**: a bank account or
a debit card. Its `destination_id` names which one, and `destination_kind` says
whether it is a `bank` or a `card`.

`destination_id` is optional when you create the payout. Leave it out and the
payout goes to the Beneficiary's **declared destination**, the one they chose
through a collection link. If they have declared none, it goes to the only
destination they hold, and a Beneficiary who holds several needs you to name
one. [Beneficiaries & compliance](/concepts/beneficiaries#know-which-destination-a-payout-goes-to)
has the full table of what you send and what happens.

The destination is fixed when the payout is created. If the Beneficiary
declares a different destination afterwards, their next payout goes there; this
one does not move.

Creation checks the payout up front: the Beneficiary must be `compliant` and
the destination `payout_usable`. In production, your Business must not be
blocked (`BUSINESS_BLOCKED`). A payout that fails a check is refused and nothing
is created, so it is safe to correct and resend. The idempotency key you send makes creation safe to retry for 24
hours; see [Idempotency](/idempotency).

## Know which rail carries the money

The destination decides the Payout Rail, the network that carries the money:

| `destination_kind` | Paid over | Needs |
| - | - | - |
| `bank` | ACH, which arrives in one to three business days. Once your Business is approved for RTP / FedNow, it can also go over the real-time network, which arrives within seconds at any hour when the receiving bank is on it. | Nothing for ACH. Approval for RTP / FedNow. |
| `card` | The card network, which arrives within seconds. | Approval for card payouts. A card payout without it fails with `failure_code: business_not_enrolled`. |

The dashboard's **Settings → Plan & Billing** page lists your payout rails,
which of them are active, and how to request the others.

<Danger>
  A card payout your Business is not approved for is created and approved without complaint, then
  fails when it is sent. Check that card payouts are active on **Plan & Billing** before you pay a
  Beneficiary whose destination is a card.
</Danger>

## Pass the approval gate

Nothing is sent without a Decision. A Decision is an approve or reject verdict
recorded by a validator: a person on your team, or your own risk system calling
the API.

| Request | What it does |
| - | - |
| [`POST /v1/payouts/{payoutId}/approve`](/api-reference/payouts-approve) | Approves the payout, which queues it to be sent |
| [`POST /v1/payouts/{payoutId}/reject`](/api-reference/payouts-reject) | Rejects the payout; needs at least one entry in `reasons` |
| [`POST /v1/payouts/{payoutId}/decisions`](/api-reference/payouts-decisions-create) | The canonical form of both, with `status` naming the verdict |
| [`GET /v1/payouts/{payoutId}/decisions`](/api-reference/payouts-decisions-list) | Lists every Decision recorded on the payout |

`approve` and `reject` are conveniences over `decisions` and share its
guarantees. Every Decision carries a `decision_id` you mint, a ULID, which makes
it safe to retry: resending it unchanged returns the original Decision with HTTP
200, and resending it with a different verdict is refused with
`IDEMPOTENCY_CONFLICT`. Approving or rejecting a payout that is no longer
`pending_approval` is refused with `INVALID_STATE_TRANSITION`.

A Decision with `status: "pending_review"` is recorded without moving the
payout: it stays in its current state until an approve or reject follows.

<Warning>
  Rejection is terminal. A rejected payout can't be approved later or replaced; create a new payout
  instead.
</Warning>

In production, a Business Cleff has blocked can't create or approve payouts
(`BUSINESS_BLOCKED`), but can still reject them, so nothing waiting for approval
is stranded.

## Know what happens after approval

Approval queues the payout to be sent, and sending happens asynchronously, so
the payout stays `approved` while the payment is in flight. In production your
balance is checked at this point, not before: a payout your Cleff Account can't cover
fails with `failure_code: insufficient_funds`.

On the way, the provider may acknowledge the payout, give an expected delivery
date, and assign `rail_ref`, the payment's reference on the rail. The
acknowledgment and the expected date arrive only as webhook events; `rail_ref`
appears on the payout and in each event's `data`. When the provider confirms the
money has left, the payout reaches `disbursed`.

`disbursed` is not the end. The receiving bank can still return the money, so
the payout stays provisional until the return window closes and it becomes
`settled`. The money arriving in the Beneficiary's account is reported by
`payout.delivered`, not by a state: a delivered payout is still `disbursed`
until it settles.

In sandbox, every payout that is approved reaches `disbursed` and stops there.

## Replace a payout that did not arrive

A payout that is `failed` or `returned` can be answered with a replacement: a
new payout, created with `replaces_payout_id` set to the original's ID. Check
`replaceable` on the original first. It is `false` when:

* a replacement already exists, so creating another is refused with
  `PREDECESSOR_ALREADY_REPLACED`;
* the failure is `indeterminate`, because the money may already have moved.
  Contact Cleff support instead.

Fix what the `failure_code` names before you replace the payout, or the
replacement fails the same way. A replacement goes through the approval gate
like any other payout.

## Track payouts without polling

[Webhooks](/webhooks) report each step as it happens. There are ten `payout.*`
events:

| Event | Sent when | State after |
| - | - | - |
| `payout.disbursement_submitted` | The payout is sent to the provider | `approved` |
| `payout.acknowledged` | The provider confirms it received the payout | Unchanged |
| `payout.estimated_arrival_at` | An expected delivery date is given or updated | Unchanged |
| `payout.disbursed` | The money has left your Cleff Account | `disbursed` |
| `payout.delivered` | The money has arrived in the Beneficiary's account | `disbursed` |
| `payout.settled` | The return window closed without a return | `settled` |
| `payout.canceled` | The payout was canceled before any money moved | `canceled` |
| `payout.rejected` | A Decision refused the payout | `rejected` |
| `payout.failed` | The money did not leave your Cleff Account | `failed` |
| `payout.returned` | The money left, then came back | `returned` |

No event is sent for creating or approving a payout, including one approved by
a teammate in the dashboard; `payout.disbursement_submitted` is the first event
after approval. To catch up on changes you did not see, list payouts with
[`updatedSince`](/api-reference/payouts-list). [Payout events](/api-reference/webhook-events-payout) describes what
each one carries.

## Read amounts in minor and major units

The API speaks **minor units**: whole numbers of the currency's smallest unit,
in fields that end in `_minor`. For USD that is cents, so
`amount_minor: 50000` is \$500.00. Integers keep amounts exact; there is no
rounding on the way in or out.

Webhooks speak **major units**. The `amount` field on every `payout.*` event is
a decimal string in the currency's own unit, carrying exactly as many decimal
places as the currency uses. Jane Doe's payout arrives as:

```json theme={null}
{ "amount": "500.00", "currency": "USD" }
```

Both describe the same \$500.00. To reconcile a webhook with the payout you
created, match on `payout_id` rather than on the amount. If you do compare
amounts, convert the string to minor units with a decimal parser, or by
dropping the decimal point, and compare integers. Parsing it as a
floating-point number can round it.

| Where | Field | Unit | \$500.00 is |
| - | - | - | - |
| Requests and responses | `amount_minor`, `*_minor` | Minor | `50000` |
| [Payout events](/api-reference/webhook-events-payout) | `amount` | Major | `"500.00"` |

## Next steps

<CardGroup cols={2}>
  <Card title="Send a payout" icon="send" href="/guides/send-a-payout">
    Pay Jane Doe \$500.00 and follow it to `disbursed`.
  </Card>
</CardGroup>


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