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

# Payouts

> One endpoint pays a bank account, a mobile money number, or a wallet address, in any enabled asset.

A payout moves money out of one of your balances to a beneficiary. You create
a payout with one call, `POST /v2/payouts`. Hermes holds the money at once.
Hermes then tracks the transfer to a terminal state.

For money that comes *into* your balance, see [Collections](/v2/collections).

## One endpoint, three destination types

There is no rail in the path. **The `type` field of the `destination` object
selects the rail.**

| `type`   | Fields                                                                                                           | Destination                       |
| -------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `bank`   | `institution_id`, `account_number`, `beneficiary_name`                                                           | A bank account                    |
| `momo`   | `institution_id`, `mobile_number`, `beneficiary_name`                                                            | A mobile money number             |
| `wallet` | `network`, `address`, `beneficiary_name`, `beneficiary_type`, `beneficiary_country`, `beneficiary_date_of_birth` | A stablecoin address on a network |

Rules for each type:

* `institution_id` comes from `GET /v2/institutions?asset=KES`. The list is
  per asset. Each entry tells you its `type`. Keep the list in a cache, and
  read it again if a create request refuses the id.
* `mobile_number` is in E.164 form, with the `+`. An example is
  `+254712345678`. The country code must agree with the asset. Hermes
  refuses a number in a different form, and never corrects it for you.
* `account_number` must agree with the digit rule of the destination bank.
  **Hermes does not check the name on a KES bank account.** Hermes sends
  `beneficiary_name` to the bank and stores it. Nobody compares it with the
  account.
* `network` is `ethereum`, `solana`, or `tron`. The asset must exist on that
  network. USDT is on the three networks. USDC is on `ethereum` and `solana`.
  A different pair gets `invalid_wallet_address`.
* `address` must have the correct shape for the network. On `ethereum`, an
  address in mixed case must pass the EIP-55 checksum. An address in lower
  case only needs the correct shape.
* `beneficiary_type` is `individual` or `business`. The default is
  `individual`. `beneficiary_date_of_birth` is necessary for each
  `individual`, in `YYYY-MM-DD` form. `beneficiary_country` is an ISO-2 code.
* Hermes does not support a wallet destination that needs a memo or a tag.

Hermes checks each destination field before it holds money. A bad field gets
`400` or `422`, and Hermes makes no payout.

## The money fields

`amount` and `asset` are always **what the beneficiary receives**. Hermes
never changes them.

`source_asset` is the asset that you pay from. The default is `asset`. When
`source_asset` is the same as `asset`, the payout is a same-asset payout.
The response shows `source_amount` equal to `amount`, and `rate` is `null`.

### The FX payout

When `source_asset` is different from `asset`, the payout is an **FX payout**.
Hermes converts and pays in one movement. An example is a KES payout that you
pay from USDT.

```json theme={null}
{
  "amount": "10000.00",
  "asset": "KES",
  "source_asset": "USDT",
  "destination": { "type": "bank", "institution_id": "kes-bank-kenya-commercial-bank-limited", "account_number": "1000012345", "beneficiary_name": "Jane Doe" }
}
```

The response adds three figures:

* **`source_amount`** — what Hermes debits from your `source_asset` balance,
  with the conversion in it. The fee is not in it.
* **`fee`** — the payout fee of Jenzy, in `source_asset`. One total, not a
  breakdown.
* **`rate`** — `source_amount` divided by `amount`, at eight decimal places.
  The rate is for display. The two amounts are the truth.

The pair must be one fiat asset and one stablecoin. Fiat to fiat, and
stablecoin to stablecoin, get `unsupported_pair` (422). The rate is the rate
at the time that Hermes accepts the payout. There is no quote step for a
payout. If you want to see the rate first, use a
[conversion quote](/v2/conversions) as a guide.

<Note>
  When Hermes accepts a payout, it holds `source_amount`, the `fee`, and a
  reserve for the provider fee, all in `source_asset`. Your `available` in
  that asset must cover the three together. If it does not, you get
  `insufficient_funds` (402), and Hermes makes no payout.
</Note>

## Idempotency

Each create request needs an **`Idempotency-Key`** header. A request with no
key gets `400 validation_error`. Make one key for each payout that you intend
to send. You can use a UUID.

Send the same key with the same body again, and Hermes returns the first
response again, with the same bytes. Because of this, a network timeout or a
second request cannot cause two payments.

The same key with a *different* body gets `409 conflict`. One key group
covers `/v1` and `/v2`. Do not use one key on both APIs.

## A payout can be `failed` in its creation response

Hermes sends the payout to the provider inside the create call. Most refusals
happen before Hermes holds money, and those are a `4xx` with no payout. In
rare cases the provider refuses the payout at once. Then the `201` response
carries the payout with `status: "failed"`, and `failure` set.

Hermes already released the hold. Hermes also sends `payout.failed`. Read the
`status` of each `201` response. Do not assume that a `201` is a payout in
progress.

## The payout resource

Read payouts with `GET /v2/payouts` and `GET /v2/payouts/{id}`.
`GET /v2/payouts` puts the newest payout first and uses keyset pagination.
Filter with `asset` and `status`. The create response and the `data` field of
each payout webhook use the same shape.

```json theme={null}
{
  "id": "0d5e63b2-59f3-4d2a-8f57-2f14a3c14f6d",
  "status": "succeeded",
  "asset": "KES",
  "amount": "10000.00",
  "source_asset": "USDT",
  "source_amount": "77.8875",
  "fee": "0.8",
  "rate": "0.00778875",
  "destination": {
    "type": "bank",
    "institution_id": "kes-bank-kenya-commercial-bank-limited",
    "account_number": "1000012345",
    "beneficiary_name": "Jane Doe"
  },
  "failure": null,
  "created_at": "2026-09-09T09:14:02.000Z",
  "terminal_at": "2026-09-09T09:14:31.000Z"
}
```

### Status lifecycle

```
held → pending → succeeded | failed
```

* **`held`** — the money is on hold. The provider did not confirm the payout
  yet. A payout stays here for a short time, or when the provider is not
  available. Hermes continues from here by itself.
* **`pending`** — the provider has the payout and works on it. This is the
  only status where a payout waits. Hermes reads the status again. Hermes
  does not send the payout a second time.
* **`succeeded`** / **`failed`** — terminal. No other status is final.
* **`reversed`** — the payout succeeded, and then the provider took the money
  back. This is not a failure. The `failure` field stays `null`. The
  `terminal_at` field keeps the time of the *first* success.

The status words are the same as on `/v1`. The `failure.code` words are the
same too. The reference also lists `received`, `validated`, and `submitted`.
A `/v2` payout passes through them inside the create call. You do not see
them in a response or in an event.

## How to learn the result

**Webhooks (recommended)** — Hermes sends `payout.succeeded`, `payout.failed`,
or (not often) `payout.reversed`. A `/v2` delivery carries
`api_version: "v2"`, and `data` is the payout in the shape above. See
[Webhooks](/webhooks).

**A poll** — read `GET /v2/payouts/{id}` again until the status is terminal.
A poll and a webhook give the same data. So you can always use
`GET /v2/payouts` to catch up after an outage.

When a payout fails, Hermes releases the full hold back to `available` in
`source_asset`. A `failed` payout shows `fee: "0"`. The `failure.code` field
tells you if a second payout can succeed:

| Code                        | Can a second payout succeed?                              |
| --------------------------- | --------------------------------------------------------- |
| `recipient_account_invalid` | Only after you correct the destination.                   |
| `declined`                  | Not usually. Examine the payout before you send it again. |
| `provider_unavailable`      | Yes. Send a new payout after a short time.                |
| `temporarily_unavailable`   | Yes. Send a new payout later. Hermes took no funds.       |

Hermes never sends a failed payout again. To try again, always create a
**new** payout with a **new** `Idempotency-Key`.

<Warning>
  **A reversal needs your attention, and your balance can be late.**

  A `payout.reversed` event tells you that the provider took back a payout
  that succeeded before. The recipient no longer has the money. If you gave
  goods or services after `payout.succeeded`, this event tells you to act.

  For a short time after the event, `GET /v2/balances` can still show the
  payout as spent. A person in Jenzy operations posts the correction. Use
  the event as the correct signal, and let your balance catch up.

  A reversal is not usual, and Jenzy always examines one.
</Warning>

## Limits

Each asset and each destination type has a minimum and a maximum for one
payout. Hermes measures the limits on `amount`, the figure that the
beneficiary receives.

* Below the minimum gets `amount_below_minimum` (422).
* Above the maximum gets `limit_exceeded` (422).

There is no daily cap on `/v2`. Your `available` in `source_asset` is the
ceiling. The request rate limit of your org still applies. See
[Authentication](/authentication).

## Common rejections

Hermes refuses the request and holds **nothing**. See [Errors](/v2/errors)
for the complete list.

* `insufficient_funds` (402) — `source_amount` plus the fee is more than
  your `available` in `source_asset`.
* `corridor_not_enabled` (403) — Jenzy did not enable this asset, or this
  operation in this asset, for your org.
* `unsupported_pair` (422) — `source_asset` and `asset` are both fiat, or
  both stablecoins.
* `invalid_wallet_address` (422) — the address has the wrong shape for the
  network, or the asset does not exist on the network.
* `rate_unavailable` / `service_unavailable` (503) — Hermes has no fresh rate
  for an FX payout, or no rate to price the fee. Send the request again after
  a short time.
* `amount_below_minimum` / `limit_exceeded` (422) — see Limits above.
