Skip to content

Mandates ​

Register a customer's standing consent to be debited ​

POST/api/v1/tenant/mandates

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

Records the customer's acceptance of the consent document (agreementType = mandate) and the mandate that cites it, together or not at all. A customer authentication assertion is required because creating a mandate is itself consent (see the Customer authentication guide). The mandate is active when validFrom has passed and pending until then. beneficiaryEntityCode must identify a beneficiary that can collect mandate debits — a business with a collection wallet at Neo Wallet, or a beneficiary Neo Wallet has configured; anything else is a 422. replacesMandateUid supersedes an existing mandate atomically — it is revoked with reason superseded and its agreement moved to superseded, never edited. mandate.created is delivered to your webhook.

Request body ​

FieldTypeRequiredDescription
customerUidstringyesThe customer giving the standing consent.
walletUidstringyesThe customer's wallet the mandate debits.
beneficiaryEntityCodestringyesCode of the beneficiary the debits pay; it must be a beneficiary set up with Neo Wallet to collect payments. At most 40 characters.
clientReferencestringyesYour own reference; stored and returned verbatim, never interpreted.
amountRuleenumyesHow the amount of each debit is bounded. One of fixed, up_to, formula.
fixedOrMaxAmountstringnoA positive amount with at most two decimal places; the exact (fixed) or maximum (up_to) debit. Required unless amountRule is formula.
formulaobjectnoThe formula rule's document. Not supported yet: amountRule formula is refused (422).
frequencyenumyesHow often the mandate may be debited. One of one_time, daily, weekly, monthly, on_due_date, ad_hoc.
currencystringyesISO-4217; must equal the wallet's currency.
validFromstringyesISO-8601; the mandate is pending until then and active after.
validToobjectnoISO-8601; must be after validFrom. Absent = open-ended.
retryPolicyobjectnoAdvisory, for your own use; Neo Wallet never schedules or retries a debit.
agreementobjectyesThe consent document the customer accepted for this mandate.
agreement.documentTypestringyesKind of document accepted, e.g. debit_mandate_terms. At most 40 characters.
agreement.documentVersionstringyesVersion of the accepted document. At most 40 characters.
agreement.documentUristringyesURI of the accepted document. At most 500 characters.
agreement.documentHashstringyesSHA-256 of the accepted document, 64 hex characters.
agreement.acceptedAtstringyesWhen the customer accepted the document, ISO-8601.
agreement.acceptedChannelstringyesYour channel the consent was captured in; portal is not accepted.
agreement.evidenceobjectyesYour proof of the acceptance; hashed and stored, never returned.
assertionobjectyesThe customer's authentication for giving this consent.
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. 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
replacesMandateUidobjectnoAn existing mandate of this customer to supersede; it is revoked as this one is created.
Example (placeholder values)
json
{
  "customerUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "walletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "beneficiaryEntityCode": "string",
  "clientReference": "string",
  "amountRule": "fixed",
  "frequency": "one_time",
  "currency": "ETB",
  "validFrom": "2026-09-03T10:15:30.000Z",
  "agreement": {
    "documentType": "string",
    "documentVersion": "string",
    "documentUri": "string",
    "documentHash": "string",
    "acceptedAt": "2026-09-03T10:15:30.000Z",
    "acceptedChannel": "string",
    "evidence": {}
  },
  "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
customerUidstring
walletUidstring
beneficiaryEntityCodestring
clientReferenceobject | nullYour reference, exactly as you supplied it.
amountRuleenumOne of fixed, up_to, formula.
fixedOrMaxAmountstring | nullA decimal amount with two decimal places.
frequencyenumOne of one_time, daily, weekly, monthly, on_due_date, ad_hoc.
currencystring
validFromstring
validToobject | null
statusenumOne of pending, active, suspended, revoked, expired, completed.
agreementUidobject | nullThe acceptance recorded with this mandate.
replacesMandateUidobject | nullThe mandate this one superseded.
revokedAtobject | nullWhen the mandate was revoked; null unless status is revoked.
createdAtstring
Example (placeholder values)
json
{
  "uid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "customerUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "walletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "beneficiaryEntityCode": "string",
  "clientReference": {},
  "amountRule": "fixed",
  "fixedOrMaxAmount": "2500.00",
  "frequency": "one_time",
  "currency": "ETB",
  "validFrom": "2026-09-03T10:15:30.000Z",
  "validTo": {},
  "status": "pending",
  "agreementUid": {},
  "replacesMandateUid": {},
  "revokedAt": {},
  "createdAt": "2026-09-03T10:15:30.000Z"
}

Errors ​

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

Get one of your mandates ​

GET/api/v1/tenant/mandates/{uid}

Scope mandates:read

Path parameters ​

NameTypeRequiredDescription
uidstringyes

Response · 200 ​

FieldTypeDescription
uidstring
customerUidstring
walletUidstring
beneficiaryEntityCodestring
clientReferenceobject | nullYour reference, exactly as you supplied it.
amountRuleenumOne of fixed, up_to, formula.
fixedOrMaxAmountstring | nullA decimal amount with two decimal places.
frequencyenumOne of one_time, daily, weekly, monthly, on_due_date, ad_hoc.
currencystring
validFromstring
validToobject | null
statusenumOne of pending, active, suspended, revoked, expired, completed.
agreementUidobject | nullThe acceptance recorded with this mandate.
replacesMandateUidobject | nullThe mandate this one superseded.
revokedAtobject | nullWhen the mandate was revoked; null unless status is revoked.
createdAtstring
Example (placeholder values)
json
{
  "uid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "customerUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "walletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "beneficiaryEntityCode": "string",
  "clientReference": {},
  "amountRule": "fixed",
  "fixedOrMaxAmount": "2500.00",
  "frequency": "one_time",
  "currency": "ETB",
  "validFrom": "2026-09-03T10:15:30.000Z",
  "validTo": {},
  "status": "pending",
  "agreementUid": {},
  "replacesMandateUid": {},
  "revokedAt": {},
  "createdAt": "2026-09-03T10:15:30.000Z"
}

Errors ​

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

Revoke one of your mandates ​

POST/api/v1/tenant/mandates/{uid}/revoke

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

reason is always required and recorded. Without assertion you revoke on your own authority; with one, the revocation is recorded as the customer's, and the assertion must be the mandate's customer's (see the Customer authentication guide). The mandate moves to revoked with revokedAt, the acceptance it cites moves to revoked, and mandate.revoked is delivered to your webhook. A revoked mandate is terminal — it is never reactivated; register a new one instead. Revoking a mandate that is already revoked, expired or completed is a 409.

Path parameters ​

NameTypeRequiredDescription
uidstringyes

Request body ​

FieldTypeRequiredDescription
reasonstringyesWhy the mandate is revoked; recorded on the mandate. At most 500 characters.
assertionobjectnoThe customer's authentication when you revoke on the customer's behalf; it must be for the mandate's customer. Omit when you revoke on your own authority.
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. 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 (placeholder values)
json
{
  "reason": "string"
}

Response · 200 ​

FieldTypeDescription
uidstring
customerUidstring
walletUidstring
beneficiaryEntityCodestring
clientReferenceobject | nullYour reference, exactly as you supplied it.
amountRuleenumOne of fixed, up_to, formula.
fixedOrMaxAmountstring | nullA decimal amount with two decimal places.
frequencyenumOne of one_time, daily, weekly, monthly, on_due_date, ad_hoc.
currencystring
validFromstring
validToobject | null
statusenumOne of pending, active, suspended, revoked, expired, completed.
agreementUidobject | nullThe acceptance recorded with this mandate.
replacesMandateUidobject | nullThe mandate this one superseded.
revokedAtobject | nullWhen the mandate was revoked; null unless status is revoked.
createdAtstring
Example (placeholder values)
json
{
  "uid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "customerUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "walletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "beneficiaryEntityCode": "string",
  "clientReference": {},
  "amountRule": "fixed",
  "fixedOrMaxAmount": "2500.00",
  "frequency": "one_time",
  "currency": "ETB",
  "validFrom": "2026-09-03T10:15:30.000Z",
  "validTo": {},
  "status": "pending",
  "agreementUid": {},
  "replacesMandateUid": {},
  "revokedAt": {},
  "createdAt": "2026-09-03T10:15:30.000Z"
}

Errors ​

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

Debit a wallet on a standing mandate, customer not present ​

POST/api/v1/tenant/mandates/{uid}/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, like POST /tenant/wallet-debits, but on the standing consent alone: no authentication assertion is accepted, and the mandate's frequency is enforced, so a second debit inside the current period, or any debit under a one_time mandate that has already completed one, is a 422. Neo Wallet never schedules these — you trigger each one, and the mandate's retryPolicy is advisory to you. A wallet that cannot fund the debit is not an error: the 201 carries the operation in state failed and you also receive wallet_debit.failed; a failed attempt does not consume the mandate's period, so it may be retried inside it under a fresh clientReference.

Path parameters ​

NameTypeRequiredDescription
uidstringyes

Request body ​

FieldTypeRequiredDescription
walletUidstringyesThe wallet to debit; must be the mandate's own wallet.
amountstringyesA positive amount with at most two decimal places; must satisfy the mandate's amount rule.
currencystringyesISO-4217; must equal the wallet's currency.
clientReferencestringyesYour own reference; stored and returned verbatim, never interpreted; unique within your account.
Example (placeholder values)
json
{
  "walletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "amount": "2500.00",
  "currency": "ETB",
  "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.
destinationWalletUidstring | nullWallet transfers only: the wallet the money reached.
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 (placeholder values)
json
{
  "uid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "type": "string",
  "state": "string",
  "amount": "2500.00",
  "fee": "12.50",
  "tax": "1.88",
  "currency": "ETB",
  "clientReference": "string",
  "sourceWalletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "destinationWalletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "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.