Beneficiaries
Saved recipients you pay regularly. Idempotent upsert + per-currency rail validation. Soft-deleted beneficiaries are preserved for audit but excluded from active lookups.
The beneficiary object
{
"object": "beneficiary",
"id": "ckben_xxxxxxxxxxxx",
"name": "ADAEZE OKONKWO",
"email": "adaeze@example.com",
"phone": "+2348012345678",
"currency": "NGN",
"env": "live",
"bank_code": "044",
"bank_name": "Access Bank",
"account_number": "0690000032",
"account_name": "ADAEZE OKONKWO",
"interac_email": null,
"interac_first_name": null,
"interac_last_name": null,
"verification": "verified",
"is_archived": false,
"is_blacklisted": false,
"source": "manual",
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-05-05T12:34:56Z"
}Field notes:
account_nameis the bank-of-record name returned by NUBAN (or the merchant-supplied name for non-resolvable currencies).nameis what the merchant labels them as (often the same).verificationis one ofpending/verified/failed— set when NUBAN resolves successfully.sourceismanualwhen created via API/dashboard,auto_savedwhen created automatically from a successful payout.interac_*fields populate only on CAD beneficiaries; bank-rail fields are null in that case.
Upsert a beneficiary
POST /v1/beneficiaries
Idempotent on the tuple (merchant, currency, env, bank_code, account_number). If a beneficiary already exists for those identifiers:
- The existing row is updated with new name / email / phone (you can correct typos).
- Soft-deleted rows are restored.
- Blacklisted rows refuse update — un-blacklist first.
Request — by currency
NGN / GBP / USD / EUR (bank-rail)
{
"currency": "NGN",
"name": "ADAEZE OKONKWO",
"account_number": "0690000032",
"bank_code": "044",
"bank_name": "Access Bank",
"email": "adaeze@example.com",
"phone": "+2348012345678"
}bank_code + bank_name + account_number are all required for bank-rail currencies. email + phone are optional metadata. NGN: the bank-of-record name is captured into account_name automatically — your supplied name is preserved as your label.
CAD-Interac
{
"currency": "CAD",
"name": "Adaeze Okonkwo",
"interac_email": "recipient@example.com",
"interac_first_name": "Adaeze",
"interac_last_name": "Okonkwo"
}CAD beneficiaries require all three Interac fields. Bank fields are not used for CAD.
Identity fields are immutable. You can’t change account_number, bank_code, currency, interac_email, etc. via PATCH. To change the destination account, soft-delete the old beneficiary and create a fresh one.
Idempotency semantics
When a beneficiary already exists for the same (merchant, currency, env, bank_code, account_number) (or (merchant, currency, env, interac_email) for CAD), the response includes a created flag:
| Scenario | created | restored | HTTP |
|---|---|---|---|
| Fresh row | true | (omitted) | 201 |
| Existing active row | false | (omitted) | 200 |
| Restored from archive | false | true | 200 |
| Existing row is blacklisted | refused | refused | 400 beneficiary_blacklisted |
Example
curl https://api.swappr.me/v1/beneficiaries \
-H "Authorization: Bearer sk_test_..." \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"currency": "NGN",
"name": "ADAEZE OKONKWO",
"account_number": "0690000032",
"bank_code": "044"
}'Response
The response is the Beneficiary object extended with the created (and optional restored) flag(s) per the idempotency table above.
List beneficiaries
GET /v1/beneficiaries
| Param | Notes |
|---|---|
limit | 1-100, default 50 |
starting_after | Cursor |
currency | Filter to one currency |
q | Search by name or account number (case-insensitive contains) |
Excludes soft-deleted rows by default. To list deleted rows, use the dashboard.
Retrieve a beneficiary
GET /v1/beneficiaries/{id}
Returns the beneficiary including soft-deleted state. For deleted beneficiaries, the deleted_at field is populated and account_number shows the original (un-mangled) value.
Update a beneficiary
PATCH /v1/beneficiaries/{id}
Narrow scope — name, email, phone only. Identity fields immutable.
Request
{
"name": "Adaeze N. Okonkwo",
"email": "adaeze.new@example.com",
"phone": "+2348023456789"
}Response
200 OK with updated Beneficiary.
Soft-delete a beneficiary
DELETE /v1/beneficiaries/{id}
Marks the row deleted but preserves it for audit. The unique-tuple (merchant, currency, env, bank_code, account_number) is freed by mangling the stored account_number; the original is preserved in deleted_account_number for audit + retrieval.
You can re-add the same beneficiary after deletion — that creates a fresh row + leaves the deleted one in history.
Request (optional reason)
{
"reason": "No longer paying this vendor"
}Response
200 OK with a beneficiary_delete_result:
{
"object": "beneficiary_delete_result",
"id": "ckben_xxxxxxxxxxxx",
"deleted": true,
"was_already_deleted": false
}was_already_deleted is true if you DELETE the same beneficiary twice — the call is idempotent.