Skip to content

Wallet debits

Debit a wallet with the customer freshly authenticated

POST/api/v1/tenant/wallet-debits

Scope wallet-debits:write · Write — send Idempotency-Key and X-Request-Id

Moves the amount from the customer's wallet to the mandate's beneficiary immediately; no payment provider is involved. The assertion is validated on every call and must be bound to this request, with mandateUid as the binding target (see the Customer authentication guide); the mandate must be active, within its validity window and satisfy its amount rule, but its frequency is not enforced here — that applies to POST /tenant/mandates/{uid}/debits. The debit is all-or-nothing: available funds short of the amount are a 422 and nothing is debited. The response state is terminal immediately and wallet_debit.completed is delivered to your webhook.

Request body

FieldTypeRequiredDescription
walletUidstringyesThe wallet to debit, as returned by POST /tenant/customers.
mandateUidstringyesAn active mandate of the wallet's customer that you hold (POST /tenant/mandates); its beneficiary receives this debit.
amountMinornumberyesPositive integer minor units (e.g. 250000 = ETB 2,500.00); must satisfy the mandate's amount rule. Min 1.
currencystringyesISO-4217; must equal the wallet's currency.
clientReferencestringyesYour own reference; stored and returned verbatim, never interpreted; unique within your account.
assertionobjectyesThe customer's authentication for this debit.
assertion.customerUidstringyesThe customer who authenticated.
assertion.identifierUidobjectnoThe customer identifier the customer authenticated with, if known.
assertion.assuranceLevelenumyesHow strongly your channel authenticated the customer. One of single_factor, two_factor, step_up.
assertion.factor1TypestringyesThe first factor used, e.g. pin, password, biometric. At most 32 characters.
assertion.factor2TypeobjectnoRequired unless assuranceLevel is single_factor. At most 32 characters.
assertion.authenticatedAtstringyesISO-8601 — when the customer authenticated.
assertion.expiresAtstringyesISO-8601; by default at most 10 minutes after authenticatedAt. An expired assertion is refused (422).
assertion.channelCodestringyesYour channel the customer authenticated on. At most 40 characters.
assertion.deviceHashobjectnoSHA-256 hex of the device fingerprint, if your channel has one.
assertion.sourceReferencestringyesOpaque channel evidence reference (a session or login id). Presenting one again while it is live is allowed only for the same customer, and is audited. At most 160 characters.
assertion.transactionBindingobjectnoHash your channel computed over the request this assertion authorizes; a mismatch with the request received is refused.
assertion.transactionBinding.hashstringyesSHA-256 hex (64 characters) over the canonical string of this request.
assertion.transactionBinding.algorithmenumyesOne of sha256.
assertion.transactionBinding.canonicalenumyesThe field template hashed: the six values joined with |, UTF-8. The fifth is the payout destination, the mandate for a debit, or empty; clientReference is empty when absent. One of `customerUid
Example (generated from the schema)
json
{
  "walletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "mandateUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "amountMinor": 1,
  "currency": "ETB",
  "clientReference": "string",
  "assertion": {
    "customerUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
    "assuranceLevel": "single_factor",
    "factor1Type": "string",
    "authenticatedAt": "2026-09-03T10:15:30.000Z",
    "expiresAt": "2026-09-03T10:15:30.000Z",
    "channelCode": "string",
    "sourceReference": "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.
amountMinornumberInteger minor units (e.g. 250000 = ETB 2,500.00).
feeMinornumberMinor units.
taxMinornumberMinor units.
currencystringISO-4217.
clientReferencestring | nullYour reference, exactly as you supplied it.
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).
Example (generated from the schema)
json
{
  "uid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "type": "string",
  "state": "string",
  "amountMinor": 0,
  "feeMinor": 0,
  "taxMinor": 0,
  "currency": "ETB",
  "clientReference": "string",
  "createdAt": "2026-09-03T10:15:30.000Z",
  "completedAt": "2026-09-03T10:15:30.000Z",
  "links": {
    "self": "string",
    "receipt": "string"
  }
}

Errors

400 401 403 404 409 422 429 500 503 — one body shape for all of them, see Errors.