Update a beneficiary
curl -X PATCH "https://api.payhero.africa/api/global/beneficiaries/a2e0434e-17ae-41b1-a1cf-1694ad63ee99" \ -u "API_USERNAME:API_PASSWORD" \ -H "Content-Type: application/json" \ -d '{ "date_of_birth": "10/03/1997", "id_number": "32460968", "id_type": "national_id"}'Partial update — send only the fields you have just collected and they are merged into
the stored record. PUT behaves identically; both verbs merge.
This is how a tier_0 record is enriched to full: post the missing fields listed in
kyc.missing_full_kyc_fields and the level is recomputed on the response.
The level moves both ways — clearing a required field (for example
{"address": ""}) demotes the record back to tier_0 and immediately puts it back
under the tier-0 envelope.
Send {"is_active": false} to deactivate a beneficiary without deleting it; payments
referencing it are then refused with beneficiary is not active.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”The beneficiary’s uuid, as returned on create.
Example
a2e0434e-17ae-41b1-a1cf-1694ad63ee99Request Bodyrequired
Section titled “Request Bodyrequired”object
Full name, split on the first space. Required on create unless you send
first_name + last_name instead, which win if both are present.
Required for tier_0, and therefore for every beneficiary.
Required for tier_0. ISO 3166-1 alpha-2 code (or country name).
Required for full. Returned as phone_number.
Required for full.
Required for full. Accepts mm/dd/yyyy (what the cross-border providers document), yyyy-mm-dd or RFC 3339. Returned as RFC 3339.
Required for full. Stored and returned as national_id. Unique per account — the same person can be a beneficiary of several merchants.
Required for full. Must be NIN in Nigeria.
Required for full in Nigeria, where it must be BVN.
Required for full in Nigeria.
Destination account holder name.
Destination account. Backfills payment_config.account_number on payments that reference this beneficiary.
Destination account type, e.g. momo or bank.
Your own identifier for this counterparty. Unique per account, and makes POST idempotent.
Update only. Set false to stop the beneficiary transacting without deleting it.
Examples
Enrich — still tier_0 afterwards
{ "phone": "+254712345678", "address": "Moi Drive, Umoja"}Enrich — promotes the record to full
{ "date_of_birth": "10/03/1997", "id_number": "32460968", "id_type": "national_id"}Deactivate without deleting
{ "is_active": false}Responses
Section titled “Responses”The updated beneficiary, with kyc_level and kyc recomputed.
A stored counterparty. Note that a few fields are named differently on the way out than
on the way in: phone is returned as phone_number, and id_number as national_id.
object
Send this as customer.customer_uuid on payments.
The id_number you sent.
The phone you sent.
The PayHero account that owns this beneficiary, derived from your API key.
Recomputed on every write from the metadata actually present. Never settable.
The beneficiary’s KYC standing, recomputed on every write. Read-only.
object
Fields still needed to reach full. Send these to the update endpoint.
Fields still needed to reach tier_0. Non-empty only on a rejected create.
Lifetime USD total this beneficiary has transacted while at tier_0.
Headroom left before full KYC becomes mandatory.
Per-transaction ceiling while at tier_0.
Currencies a tier_0 beneficiary can never transact in.
True when the country needs a second ID (Nigeria) and it has not been supplied.
Example
{ "id": 94, "uuid": "a2e0434e-17ae-41b1-a1cf-1694ad63ee99", "first_name": "John", "last_name": "Doe", "national_id": "32460968", "date_of_birth": "1997-10-03T00:00:00Z", "phone_number": "+254712345678", "email": "john.doe@example.com", "address": "Moi Drive, Umoja", "country": "KE", "account_id": 63, "account_name": "John Doe", "account_number": "+254712345678", "account_type": "momo", "customer_type": "retail", "kyc_level": "tier_0", "tier_0_lifetime_usd": 0, "is_active": true, "kyc": { "level": "tier_0", "full_kyc": false, "missing_full_kyc_fields": [ "address", "date_of_birth", "id_number", "id_type", "phone" ], "missing_reduced_kyc_fields": [], "tier_0_lifetime_usd": 0, "tier_0_remaining_usd": 200, "tier_0_limit_reached": false, "tier_0_max_transaction_usd": 20, "tier_0_excluded_currencies": [ "BWP", "NGN", "ZAR" ], "requires_additional_id": false }}The request was rejected (validation or business-rule failure).
object
Examples
{ "error_code": "invalid_argument", "error_message": "the transaction amount is insufficient as it wont cater for cost: (4.62)", "status_code": 400}{ "error_code": "invalid_argument", "error_message": "The passed channel is not active", "status_code": 400}Missing or invalid Basic auth credentials.
object
Example
{ "error_code": "invalid_argument", "error_message": "the transaction amount is insufficient as it wont cater for cost: (4.62)", "status_code": 400}No beneficiary with that UUID exists on the account behind your API key. A UUID that belongs to another merchant also returns 404 — never 403.
object
Examples
{ "error_code": "not_found", "error_message": "beneficiary \"a2e0434e-17ae-41b1-a1cf-1694ad63ee99\" does not exist", "status_code": 404}
