Appearance
Topup intents
Start a top-up the customer approves on their phone
POST
/api/v1/tenant/topup-intentsScope topup-intents:write · Write — send Idempotency-Key and X-Request-Id
Creates a topup_c2b operation in state pending_provider with a 12-digit intentReference and an expiresAt, and sends a payment prompt for the exact amount to the customer's verified phone number — the one you name in payerIdentifierUid, else the primary one, else the one added first; no money moves yet. railCode names the payment provider; one that cannot take the payment is refused with 422 RailUnavailable, and a customer with no verified phone number, or a payerIdentifierUid that is not one, with 422. The wallet is credited when the customer approves the prompt before the intent expires (topup.confirmed). A prompt that is declined or not answered is not sent again: the intent fails at expiresAt (topup.expired).
Request body
| Field | Type | Required | Description |
|---|---|---|---|
walletUid | string | yes | The wallet the top-up credits, as returned by POST /tenant/customers. |
amount | string | yes | A positive amount with at most two decimal places; the exact amount the payer must pay in. |
currency | string | yes | ISO-4217; must equal the wallet's currency. |
railCode | string | yes | Code of the payment provider the payer will pay through; it must accept incoming payments. At most 64 characters. |
payerIdentifierUid | string | null | no | The customer's phone number to send the payment prompt to, for this top-up only: an identifiers[].uid from GET /tenant/customers/{uid} that is a verified phone number of the wallet's customer, otherwise 422. Omit it to prompt the default: the primary verified phone number, else the verified phone number added first. Only for a payment provider that prompts the customer's phone; anywhere else 422. At most 26 characters. |
expiresInSeconds | object | no | Lifetime of the intent in seconds; defaults to the payment provider's usual lifetime, else one hour. When the payment provider prompts the customer's phone, the intent lives at least twice as long as the provider allows for the answer; expiresAt in the response is the lifetime granted. Min 60. Max 86400. |
clientReference | string | yes | Your own reference; stored and returned verbatim, never interpreted; unique within your account. |
Example (placeholder values)
json
{
"walletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
"amount": "2500.00",
"currency": "ETB",
"railCode": "string",
"clientReference": "string"
}Response · 201
| Field | Type | Description |
|---|---|---|
uid | string | |
type | string | Operation type, e.g. wallet_credit, wallet_debit, payout_b2c or topup_c2b. |
state | string | Operation state: created, awaiting_authorization, authorized, held, processing, pending_provider, confirmed, failed, reversed or completed. |
amount | string | A decimal amount with two decimal places. |
fee | string | A decimal amount with two decimal places. |
tax | string | A decimal amount with two decimal places. |
currency | string | ISO-4217. |
clientReference | string | null | Your reference, exactly as you supplied it. |
sourceWalletUid | string | null | Wallet transfers only: the wallet the money left. |
destinationAccountNumber | string | null | Wallet transfers only: the account number the money was sent to. |
createdAt | string | |
completedAt | string | null | When the operation first reached a final state. |
links | object | |
links.self | string | API-relative path of this operation (GET /tenant/operations/{uid}). |
links.receipt | string | API-relative path of its receipt (GET /tenant/operations/{uid}/receipt). |
intentReference | string | The top-up's reference with the payment provider; 12 digits. |
expiresAt | string | After this instant a payment for this top-up is no longer credited. |
railCode | string | The payment provider's code the intent was issued on. |
payerInstructions | string | null | Instructions to show the payer for this payment provider; null when there are none. |
Example (placeholder values)
json
{
"uid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
"type": "string",
"state": "string",
"amount": "2500.00",
"fee": "12.50",
"tax": "1.88",
"currency": "ETB",
"clientReference": "string",
"sourceWalletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
"destinationAccountNumber": "string",
"createdAt": "2026-09-03T10:15:30.000Z",
"completedAt": "2026-09-03T10:15:30.000Z",
"links": {
"self": "string",
"receipt": "string"
},
"intentReference": "048213775902",
"expiresAt": "2026-09-03T10:15:30.000Z",
"railCode": "string",
"payerInstructions": "string"
}Errors
400 401 403 404 409 422 429 500 503 — one body shape for all of them, see Errors.