API referenceBeneficiaries

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_name is the bank-of-record name returned by NUBAN (or the merchant-supplied name for non-resolvable currencies). name is what the merchant labels them as (often the same).
  • verification is one of pending / verified / failed — set when NUBAN resolves successfully.
  • source is manual when created via API/dashboard, auto_saved when 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:

ScenariocreatedrestoredHTTP
Fresh rowtrue(omitted)201
Existing active rowfalse(omitted)200
Restored from archivefalsetrue200
Existing row is blacklistedrefusedrefused400 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

ParamNotes
limit1-100, default 50
starting_afterCursor
currencyFilter to one currency
qSearch 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.