Skip to content

Topup intents ​

Start a top-up the customer approves on their phone ​

POST/api/v1/tenant/topup-intents

Scope 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 ​

FieldTypeRequiredDescription
walletUidstringyesThe wallet the top-up credits, as returned by POST /tenant/customers.
amountstringyesA positive amount with at most two decimal places; the exact amount the payer must pay in.
currencystringyesISO-4217; must equal the wallet's currency.
railCodestringyesCode of the payment provider the payer will pay through; it must accept incoming payments. At most 64 characters.
payerIdentifierUidstring | nullnoThe 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.
expiresInSecondsobjectnoLifetime 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.
clientReferencestringyesYour 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 ​

FieldTypeDescription
uidstring
typestringOperation type, e.g. wallet_credit, wallet_debit, payout_b2c or topup_c2b.
statestringOperation state: created, awaiting_authorization, authorized, held, processing, pending_provider, confirmed, failed, reversed or completed.
amountstringA decimal amount with two decimal places.
feestringA decimal amount with two decimal places.
taxstringA decimal amount with two decimal places.
currencystringISO-4217.
clientReferencestring | nullYour reference, exactly as you supplied it.
sourceWalletUidstring | nullWallet transfers only: the wallet the money left.
destinationAccountNumberstring | nullWallet transfers only: the account number the money was sent to.
createdAtstring
completedAtstring | nullWhen the operation first reached a final state.
linksobject
links.selfstringAPI-relative path of this operation (GET /tenant/operations/{uid}).
links.receiptstringAPI-relative path of its receipt (GET /tenant/operations/{uid}/receipt).
intentReferencestringThe top-up's reference with the payment provider; 12 digits.
expiresAtstringAfter this instant a payment for this top-up is no longer credited.
railCodestringThe payment provider's code the intent was issued on.
payerInstructionsstring | nullInstructions 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.