Convert USD to KES
Turn the USD in your USD account into KES in your KES account with one call, and know exactly what rate you were paid.
A conversion takes USD from your USD account and pays KES into your KES
account. It is available on a Collections account and needs the
conversions:write scope to create and conversions:read to quote and read
back — see Products and scopes.
Only USD to KES is supported. A conversion needs no confirmation code: it moves money between two accounts that are both yours.
GET /v1/balances shows both accounts on a
Collections credential: the USD you can convert under usd, and the KES a
conversion pays into under kes. If usd.available is null, your USD account
cannot be read yet and usd.unavailableReason says why — see
Balances.
1. Ask for a quote (optional)
Send from, to and amount as query parameters, for example
?from=USD&to=KES&amount=100.00:
GET /v1/conversions/quoteThe answer:
{
"rate": "128.35",
"source": "100.00",
"target": "12835.00",
"indicative": true,
"asOf": "2026-10-24T10:00:00.000Z"
}rateis the KES you would be paid per 1 USD, to 2 decimal places.targetissource × rate, rounded down to the cent.- A quote is indicative. It does not hold a rate and it does not reserve money. The rate is fixed when the conversion executes.
2. Create the conversion
POST /v1/conversions
Content-Type: application/json{
"from": "USD",
"to": "KES",
"amount": "100.00",
"partnerReference": "payout-run-2026-10-24",
"idempotencyKey": "cnv-2026-10-24-0001"
}The answer is a 202. The conversion has been accepted and is running:
{
"id": "c0a8012e-7b1a-4c39-9d3e-2f5c8a1b6d40",
"status": "processing"
}idempotencyKeymakes the create safe to retry. The same key with the same body returns the same conversion, and never converts twice. The same key with a different body is refused withIDEMPOTENCY_KEY_REUSED.- Before accepting, we check that your USD account holds at least
amount. If it does not, the create is refused withCONVERSION_INSUFFICIENT_BALANCEand nothing moves.usd.availableonGET /v1/balancesis the figure to sizeamountfrom, but it is a reading up to thirty seconds old, not a reservation. - The minimum is USD 1.00. There is also one maximum per conversion; above it the
create is refused with
CONVERSION_AMOUNT_ABOVE_MAXIMUM. It is the same for every partner and is not set on your account. Contact us for the current figure. See Limits.
3. Wait for the result
A conversion ends in one of two states, and each sends a webhook:
status | Webhook | What it means |
|---|---|---|
processing | none | Still running. Wait for the webhook or read it back again later. If it is still processing 30 minutes after you created it, contact us and quote the conversion id. |
settled | conversion.settled | The USD left your USD account and the KES is in your KES account. |
failed | conversion.failed | No KES was paid. Any USD it took has been returned to your USD account. |
Read it back with GET /v1/conversions/{id}:
GET /v1/conversions/{id}{
"id": "c0a8012e-7b1a-4c39-9d3e-2f5c8a1b6d40",
"status": "settled",
"from": "USD",
"to": "KES",
"source": "100.00",
"target": "12835.00",
"rate": "128.35",
"partnerReference": "payout-run-2026-10-24",
"failureReason": null,
"createdAt": "2026-10-24T10:00:00.000Z",
"settledAt": "2026-10-24T10:00:41.000Z",
"failedAt": null
}rate and target are null until the rate is fixed. Once they are set they
never change: target is exactly source × rate, rounded down to the cent, and
it is the KES paid into your account.
Why a conversion fails
failureReason (and reason on conversion.failed) is one of:
| Reason | What happened | Your USD |
|---|---|---|
insufficient_balance | Your USD account no longer held amount when the conversion ran. | Never taken |
debit_refused | The USD could not be taken from your account. | Never taken |
fx_unavailable | The exchange could not be made. | Taken and returned |
provider_otp_not_received | The exchange could not be authorised in time. | Taken and returned |
rate_moved_beyond_tolerance | The rate moved too far between your create and the exchange, so we did not trade. | Taken and returned |
In every case no KES was paid, and you can create a new conversion with a new
idempotencyKey. A conversion is reported failed only once any USD it took is
back in your USD account.
Conversions and credit.received
A conversion moves money into one of your accounts, and a credit to one of your
accounts usually also sends a credit.received
event. So a conversion usually produces more events than its own two:
- A
settledconversion sendsconversion.settled, and usually also acredit.receivedin KES on your KES account for the KES that landed. - A
failedconversion that had taken your USD sendsconversion.failed, and usually also acredit.receivedin USD on your USD account for the USD returned.
Do not rely on either credit.received: a conversion's legs do not always
produce one, and when one arrives it carries no conversion id. Count a
conversion once, on conversion.settled's target. Never count a
credit.received for a conversion's funds as well, and never count the USD
returned by a conversion.failed as income: it is your own USD coming back, and
your USD balance is what it was before the conversion.
Limits
Conversions count against none of your caps. The per-order, per-instruction and rolling 24-hour ceilings in Caps and limits apply to orders, transfers and USDT sends, and a conversion draws on none of them, nor adds to what they have used.
The only limits on a conversion are these two, both applied to the USD amount
when you create it:
- a minimum of USD 1.00 (
CONVERSION_AMOUNT_BELOW_MINIMUM), and - one maximum per conversion, the same for every partner and not set per account
(
CONVERSION_AMOUNT_ABOVE_MAXIMUM). To convert more, send several conversions.
Testing in sandbox
In sandbox a conversion runs on your simulated balances: it takes simulated USD
from your USD account and pays simulated KES into your KES account, and sends
the same webhooks as in production. Fund your USD account first with a
simulated collection deposit, then watch usd.available go down and
kes.available go up on GET /v1/balances once the conversion settles (a
balance can be up to thirty seconds behind) — see
Testing in sandbox.
Errors worth handling
Every code on these routes is catalogued in Errors — Conversions.
| Status | Meaning |
|---|---|
422 | A pair other than USD to KES, an invalid or out-of-range amount, or not enough USD. Nothing moved. |
503 | Conversions are not enabled, a rate or your balance could not be read right now, or (CONVERSIONS_UNAVAILABLE) conversions are temporarily unavailable. Nothing moved; retry with the same idempotencyKey. |
404 | No conversion with that id belongs to you. |
Open a collection
Mint a reference and USD deposit instructions for one collection, read the fee terms up front, then read the collection back at any time.
Collect in the US, pay out in Kenya
Collect USD from US contributors, be paid the net into your USD account, convert it to KES, and pay it out in Kenya in KES or USD — all live, on one credential.