Skip to content

Mandates

List your mandates, newest first

GET/api/v1/tenant/mandates

Scope mandates:read

Paginated. Every mandate you registered, in any status. customerUid narrows within your own mandates; a customer outside your account matches nothing.

Query parameters

NameTypeRequiredDescription
customerUidstringnoCustomer the mandate is on
statusenumnoMandate status
pagenumbernoPage to retrieve, 1-based. Example: 1 Default Value: 1
limitnumbernoRecords per page. Values above the maximum are clamped, not rejected. Default Value: 20 Max Value: 100
sortByenum[]noSort key. Repeat the param to sort by several fields; URL order sets precedence. Format: fieldName:DIRECTION Example: sortBy=createdAt:DESC&sortBy=id:ASC Default Value: createdAt:DESC,id:DESC Available Fields createdAt id Any other field is rejected with 400.

Response · 200

FieldTypeDescription
dataobject[]
data[].uidstring
data[].customerUidstring
data[].walletUidstring
data[].beneficiaryEntityCodestring
data[].clientReferenceobject | nullYour reference, exactly as you supplied it.
data[].amountRuleenumOne of fixed, up_to, formula.
data[].fixedOrMaxAmountMinorobject | nullMinor units.
data[].frequencyenumOne of one_time, daily, weekly, monthly, on_due_date, ad_hoc.
data[].currencystring
data[].validFromstring
data[].validToobject | null
data[].statusenumOne of pending, active, suspended, revoked, expired, completed.
data[].agreementUidobject | nullThe acceptance recorded with this mandate.
data[].replacesMandateUidobject | nullThe mandate this one superseded.
data[].revokedAtobject | nullWhen the mandate was revoked; null unless status is revoked.
data[].createdAtstring
metaobject
meta.totalnumber
meta.pagenumber
meta.limitnumber
meta.totalPagesnumber
Example (generated from the schema)
json
{
  "data": [
    {
      "uid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
      "customerUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
      "walletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
      "beneficiaryEntityCode": "string",
      "clientReference": {},
      "amountRule": "fixed",
      "fixedOrMaxAmountMinor": {},
      "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"
    }
  ],
  "meta": {
    "total": 42,
    "page": 1,
    "limit": 20,
    "totalPages": 3
  }
}

Errors

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

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.
fixedOrMaxAmountMinorobjectnoInteger minor units (e.g. 250000 = ETB 2,500.00); the exact (fixed) or maximum (up_to) debit. Required unless amountRule is formula. Min 1.
formulaobjectnoThe formula rule's document. No schema is defined yet, so 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, 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
replacesMandateUidobjectnoAn existing mandate of this customer to supersede; it is revoked as this one is created.
Example (generated from the schema)
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.
fixedOrMaxAmountMinorobject | nullMinor units.
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 (generated from the schema)
json
{
  "uid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "customerUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "walletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "beneficiaryEntityCode": "string",
  "clientReference": {},
  "amountRule": "fixed",
  "fixedOrMaxAmountMinor": {},
  "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.
fixedOrMaxAmountMinorobject | nullMinor units.
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 (generated from the schema)
json
{
  "uid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "customerUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "walletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "beneficiaryEntityCode": "string",
  "clientReference": {},
  "amountRule": "fixed",
  "fixedOrMaxAmountMinor": {},
  "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 and the audit trail. 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, 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
{
  "reason": "string"
}

Response · 200

FieldTypeDescription
uidstring
customerUidstring
walletUidstring
beneficiaryEntityCodestring
clientReferenceobject | nullYour reference, exactly as you supplied it.
amountRuleenumOne of fixed, up_to, formula.
fixedOrMaxAmountMinorobject | nullMinor units.
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 (generated from the schema)
json
{
  "uid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "customerUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "walletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "beneficiaryEntityCode": "string",
  "clientReference": {},
  "amountRule": "fixed",
  "fixedOrMaxAmountMinor": {},
  "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.
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.
Example (generated from the schema)
json
{
  "walletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "amountMinor": 1,
  "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.
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.