Skip to main content
A payout moves money out of your prefunded balance to a beneficiary in Malawi. You create a payout with one API call: POST /payouts/bank or POST /payouts/momo. Hermes holds the funds at once. Hermes then tracks the transfer to a terminal state. For money that comes into your balance, see Pay-ins.

The two rails

There is no rail field to set. The endpoint that you call selects the rail. The two rails use the same institution_id from GET /institutions. Each entry in that list tells you its rail. If you send an institution to the wrong endpoint, Hermes refuses the request with unknown_institution. Hermes does not send the money to a different rail. See Quickstart for the full request body of each rail.

Idempotency

Each create request needs an Idempotency-Key header. 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 gives 409 conflict. Use a key a second time only to send the same payout again. A 202 response means that Hermes decided: the funds are on hold and the payout is in a queue. A 4xx response means that nothing happened. There is no payout and there are no funds on hold.

What a payout costs

Jenzy passes payout fees to you at cost and adds no margin. This is the schedule: The convenience fee applies to each payout, at all amounts. There is no minimum amount, and Jenzy does not remove the fee for large amounts. The government levy applies only above 100,000 MWK. At or below that amount, the levy is zero. The figures above include VAT, in a rounded form. Hermes rounds each fee line to the tambala separately. So a fee that you calculate from these rates can be one tambala different. POST /fees/quote gives the exact figure. Example — a payout of 10,000 MWK: Two rules come from the pass-through fee:
  • The create response does not tell you the exact fee in advance. Call POST /fees/quote with direction: "payout" before you send the payout. The quote gives the exact fee and the total_debit. The total_debit is the amount plus the fee, and it is the money that leaves your balance. Your beneficiary still gets the full amount_mwk. Use POST /fees/quote as the correct source of the exact figures. The table above is a simple version of your agreed commercial terms. It is not a replacement for the quote. The quote shows VAT as its own vat line and does not add VAT to each fee line. So compare your figures on total_fee, and not line by line.
  • Hermes knows the fee for certain only after the provider reports it. The fee field of a payout is null while the transfer is in progress. Hermes then writes the charge from the provider into the field. Hermes never calculates that charge itself.
When Hermes accepts a payout, it holds the amount_mwk and the quoted fee. Your available balance must cover the two together. Your balance can be less than the amount, or less than the amount plus the fee. In both conditions, you get insufficient_funds (402).
More fee rules:
  • Hermes charges a fee only on a payout that succeeds. A failed payout always shows fee: "0.00". Hermes releases the full hold, the amount and the fee, back to your available balance.
  • A reversed payout keeps its fee, because the payout did succeed before the provider took the money back.
  • A null fee does not mean that the payout is free. It means that Hermes does not know the fee yet. Do not use null as zero. Do not use null to decide if a payout is still in progress. The status field tells you that.
  • Hermes calculates the hold from the fee schedule. When the payout settles, Hermes compares the hold with the charge from the provider. Hermes returns the difference to your available balance. The fee field always shows the charge that Hermes observed, and never the first quote. So the debit from your balance can be a small amount different from the quote.

The payout resource

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

Status lifecycle

  • held — the funds are on hold. Hermes did not send the payout to the provider yet.
  • pending — Hermes sent the payout and waits for the result from the provider. This is the only status where a payout waits. A payout can stay here for some time if the provider is slow to confirm. 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, and not the time of the reversal.
Hermes sets terminal_at one time, when the payout first gets to succeeded or failed. The field then never changes, not even at a later reversal.

How to learn the result

Webhooks (recommended) — an org admin registers an endpoint in the Jenzy portal. Hermes then sends payout.succeeded, payout.failed, or (not often) payout.reversed a few seconds after the result. See Webhooks for the event list and how to verify a signature. A poll — read GET /payouts/{id} again until the status is terminal. A poll and a webhook give the same data. So you can always use GET /payouts to catch up after an outage. When a payout fails, Hermes releases the full hold, the amount and the fee, back to your available balance. 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. If you use the old key, you get the first failure again.
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 the payout.succeeded event, this event tells you to act.For a short time after the event, GET /balance can still show the payout as spent. A person in Jenzy operations makes the correction manually. Jenzy confirms case by case if the provider returns its own fee. Use the event as the correct signal, and let your balance catch up.A reversal is not usual, and Jenzy always examines one. Speak to support if you get a reversal that you cannot explain.

Limits

Three limits apply to each payout. The limits do not depend on your balance.
  • Per-payout maximum — Hermes refuses one payout above the cap with amount_limit_exceeded (422).
  • Daily cap — the payout volume of your org in the last 24 hours.
  • Velocity — the payout count of your org in the last 1 hour.
The daily cap and the velocity limit both give org_limit_exceeded (429). Send the payout again later. Hermes measures the limits on the amount that you asked to send, and not on the amount plus the fee. See Errors for the full table.

Common rejections

Hermes refuses the request and holds nothing. See Errors for the complete list.
  • insufficient_funds (402) — the amount plus the fee is more than your available balance.
  • unknown_institution (422) — the rail of this endpoint cannot pay the institution_id. Read GET /institutions again.
  • invalid_mobile_number / mobile_number_institution_mismatch (422, momo only) — the number is not in the full international form. Or the number belongs to the other network.
  • invalid_account_number (422, bank only) — the account number is not valid for the destination bank.
  • amount_limit_exceeded (422) — the amount is above the per-payout maximum.
  • rate_limited (429) / org_limit_exceeded (429) — send the request again after the given time.