Withdrawals
How it works
A withdrawal converts USDC from the user's Regini account into KES paid out directly to their M-Pesa. The flow:
- Quote (recommended): fetch a rate quote for the USDC amount. The response shows the exact KES the user will receive and the fee. The quote is locked for 10 minutes.
- Initiate: submit the withdrawal with the
rate_id. Regini debits the USDC from the account immediately and queues the M-Pesa payout. The API responds immediately; the withdrawal is now in-flight. - Payout: Regini instructs a B2C M-Pesa transfer to the phone number stored on the account.
- Webhook: once M-Pesa confirms the transfer, a
withdrawal.completedwebhook fires.
Only one withdrawal can be in-flight per account at a time. A second request while one is pending is rejected with CONCURRENT_WITHDRAWAL. The payout goes to the phone stored on the account. You do not supply it per-request.
Option A: Get a quote first (recommended)
Step 1: Get a rate quote
You can quote by USDC amount or by KES amount, whichever your UX collects from the user.
By USDC:
By KES (user enters how much KES they want to receive):
Provide either amount_usdc or amount_kes, not both. The response is the same either way:
{
"rate_id": "def456-...",
"amount_usdc": 38.0,
"fiat_amount_kes": 4970.0,
"fee_usdc": 0.38,
"fee_kes": 50.0,
"exchange_rate": 130.79
}
exchange_rate is the effective KES/USDC rate with the FX spread already applied, this is the rate shown to the user. amount_usdc in the response is always the USDC that will be debited regardless of which input type you used. Pass the returned rate_id to confirm.
Step 2: Confirm the withdrawal
POST /accounts/{account_id}/withdrawals
Idempotency-Key header required.
Response:
Regini sends the KES payout to the user's M-Pesa. Once confirmed, a withdrawal.completed webhook fires.
Option B: Auto-confirm at live rate
Skip the quote step and execute immediately. Provide either amount_usdc or amount_kes (not both).
By USDC:
By KES:
Request fields
| Field | Required | Description |
|---|---|---|
amount_usdc |
Conditional | USDC to debit. Required unless amount_kes or rate_id is provided. |
amount_kes |
Conditional | KES the user wants to receive. The USDC debit is back-calculated from the live rate. |
rate_id |
Conditional | Rate quote from GET .../withdrawal-quote. Required when auto_confirm_rate is false. |
auto_confirm_rate |
No | Default false. When true, executes at the live rate without a prior quote. |
reference |
No | Your own reference string (max 255 chars). Returned in the webhook and transaction history. |
Limits
| Limit | Value |
|---|---|
| Minimum per transaction | KES 100 |
| Maximum per transaction | KES 250,000 |
| Daily (rolling 24 hours) | KES 500,000 per account |
| Weekly (rolling 7 days) | KES 2,000,000 per account |
Deposit and withdrawal volumes are tracked independently, a KES 500,000 deposit does not affect your withdrawal allowance. Windows are rolling, not calendar-reset. Failed transactions are excluded from volume counts. Breaching a limit returns VELOCITY_LIMIT_EXCEEDED.
One withdrawal at a time
Only one withdrawal can be in-flight per account at a time. A second request while one is pending returns CONCURRENT_WITHDRAWAL.