Skip to main content
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.

One endpoint, three destination types

There is no rail in the path. The type field of the destination object selects the rail. 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.
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.
  • ratesource_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 as a guide.
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.

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.

Status lifecycle

  • 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. 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: Hermes never sends a failed payout again. To try again, always create a new payout with a new Idempotency-Key.
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.

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.

Common rejections

Hermes refuses the request and holds nothing. See 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.