Beneficiaries
Save payout recipients once and reuse them on later transfers
A beneficiary is a saved payout recipient: a name, optional contact details, and one payment method with its credentials. Save a recipient once, then pay them again without resending their bank account, wallet number, or card.
There are three ways to create one — through the beneficiaries API below, in bulk from a spreadsheet, or automatically while you create a transfer by setting saveBeneficiary: true.
Two hosts are involved
Beneficiaries are managed on api.kashier.io (test-api in test), the same host as the rest of the dashboard API. Transfers are created on fep.kashier.io (test-fep in test). Each endpoint below states its own host — see Hosts for why payouts span both.
Beneficiaries share a store with Customers, which is why these routes are /v2/parties and why type=beneficiary is required on every call. It is also why a list response names its array customers and its total totalCustomers.
Beneficiary object structure
{
"_id": "67ba0311bf4f31001203c6c2",
"id": "67ba0311bf4f31001203c6c2",
"type": "beneficiary",
"name": "John Doe",
"phoneNumber": "01555539512",
"countryCode": "+20",
"emailAddress": "",
"preferredCommunicationChannel": "sms",
"customerId": "B-1740243729820",
"merchantId": "MID-XXXXX-XXX",
"transferMethod": "bank",
"credentials": {
"bank": {
"recipientFullName": "John Doe",
"bank": "CIB",
"accountNumber": "010001000"
}
},
"labels": ["vip"],
"customFields": [
{
"name": "custom-key-1",
"value": "custom-value-1"
}
],
"createdByUserId": "612cdca034b3a9001f38879d",
"createdAt": "2025-02-22T17:02:10.098Z",
"updatedAt": "2025-02-25T12:31:40.474Z"
}customerId is Kashier's own identifier for the beneficiary and always starts with B-. The _id is what you pass as a path parameter and as partyId on a transfer.
The method field is named differently on the way in and on the way out
You send method.type and method.credentials. You receive transferMethod, plus the credentials nested under that same name inside credentials. Only the sub-document for that one method is ever present.
Payment methods
method.type decides which fields method.credentials must carry. A credentials object that does not match its type is rejected.
method.type | Required credentials |
|---|---|
bank | recipientFullName, bank, accountNumber |
wallet | recipientFullName, walletNumber |
instant wallet | recipientFullName, walletNumber |
card | cardholderName, cardNumber, bank |
octo card | recipientFullName, octoCardId |
internal account | recipientFullName, kashierAccountId |
Validation rules
| Rule | Limit |
|---|---|
accountNumber | 7–34 characters |
walletNumber | Egyptian mobile number matching ^(010|011|012|015)\d{8}$ |
bank | Uppercased, then matched against Kashier's supported-bank list — see Bank codes |
countryCode | Required whenever you send a phoneNumber |
cardNumber | Tokenized on save. Only a masked number and an internal token are stored, and only the masked number is returned |
List beneficiaries
Retrieve your saved beneficiaries, paginated.
| Endpoint | Value |
|---|---|
| TEST URL | https://test-api.kashier.io/v2/parties?type=beneficiary&page=1&limit=20&sortType=-1&sortBy=name |
| LIVE URL | https://api.kashier.io/v2/parties?type=beneficiary&page=1&limit=20&sortType=-1&sortBy=name |
| Method | GET |
Query parameters
| Key | Description |
|---|---|
type | Required. Must be beneficiary. |
search | Search by name, phone number, or email address. |
page | Page number. Default 1. |
limit | Records per page. Default 20. |
sortBy | Sort field: name or createdAt. Default createdAt. |
sortType | Sort order: 1 ascending, -1 descending. Default -1. |
startDate / endDate | Only return beneficiaries created after / before this date. |
labels | Comma-separated label names. |
branchIds | Comma-separated branch IDs, for branch-scoped merchants. |
curl --location 'https://test-api.kashier.io/v2/parties?type=beneficiary&page=1&limit=20&sortType=-1&sortBy=name' \
--header 'Authorization: YOUR_TEST_SECRET_KEY'Headers
| Key | Description |
|---|---|
| Authorization | The Authorization is the secret key that is used to identify the merchant. You can obtain it from Kashier dashboard. Learn more about Authorization. |
Response
{
"body": {
"customers": [
{
"_id": "67ba0311bf4f31001203c6c2",
"type": "beneficiary",
"name": "John Doe",
"phoneNumber": "01555539512",
"countryCode": "+20",
"emailAddress": "",
"customerId": "B-1740243729820",
"merchantId": "MID-XXXXX-XXX",
"transferMethod": "bank",
"credentials": {
"bank": {
"recipientFullName": "John Doe",
"bank": "CIB",
"accountNumber": "010001000"
}
},
"labels": ["vip"],
"createdByUserId": "612cdca034b3a9001f38879d",
"createdAt": "2025-02-22T17:02:10.098Z",
"updatedAt": "2025-02-25T12:31:40.474Z"
}
],
"totalCustomers": 42,
"limit": 20,
"totalPages": 3,
"page": 1,
"pagingCounter": 1,
"hasPrevPage": false,
"hasNextPage": true,
"prevPage": null,
"nextPage": 2
},
"message": "success"
}Full parameter and response reference → List beneficiaries.
Get beneficiary details
Retrieve one beneficiary by its _id.
| Endpoint | Value |
|---|---|
| TEST URL | https://test-api.kashier.io/v2/parties/:id?type=beneficiary |
| LIVE URL | https://api.kashier.io/v2/parties/:id?type=beneficiary |
| Method | GET |
curl --location 'https://test-api.kashier.io/v2/parties/:id?type=beneficiary' \
--header 'Authorization: YOUR_TEST_SECRET_KEY'Headers
| Key | Description |
|---|---|
| Authorization | The Authorization is the secret key that is used to identify the merchant. You can obtain it from Kashier dashboard. Learn more about Authorization. |
Full parameter and response reference → Get beneficiary details.
Add a beneficiary
Save a new beneficiary. Kashier assigns the customerId.
| Endpoint | Value |
|---|---|
| TEST URL | https://test-api.kashier.io/v2/parties |
| LIVE URL | https://api.kashier.io/v2/parties |
| Method | POST |
Body parameters
| Key | Description |
|---|---|
type | Required. Must be beneficiary. |
name | The beneficiary's name. |
method | The payment method — type plus its matching credentials. See Payment methods. |
phoneNumber | The beneficiary's phone number. Optional. |
countryCode | Required when phoneNumber is set, for example +20. |
emailAddress | The beneficiary's email address. Optional. |
labels | Array of label strings used to organize beneficiaries. Optional. |
customFields | Array of { "name": …, "value": … } pairs. Optional. |
preferredCommunicationChannel | sms, email, or both. Optional. |
presetName | Saves the customFields keys as a reusable preset under this name. Optional. |
curl --location 'https://test-api.kashier.io/v2/parties' \
--header 'Authorization: YOUR_TEST_SECRET_KEY' \
--header 'Content-Type: application/json' \
--data '{
"type": "beneficiary",
"name": "John Doe",
"phoneNumber": "01555539512",
"countryCode": "+20",
"emailAddress": "[email protected]",
"method": {
"type": "bank",
"credentials": {
"recipientFullName": "John Doe",
"bank": "CIB",
"accountNumber": "010001000"
}
},
"labels": ["vip"],
"preferredCommunicationChannel": "sms"
}'Headers
| Key | Description |
|---|---|
| Authorization | The Authorization is the secret key that is used to identify the merchant. You can obtain it from Kashier dashboard. Learn more about Authorization. |
Response
{
"body": {
"_id": "67bde55fd5a33d00120b202d",
"type": "beneficiary",
"name": "John Doe",
"phoneNumber": "01555539512",
"countryCode": "+20",
"emailAddress": "[email protected]",
"customerId": "B-1740498271115",
"merchantId": "MID-XXXXX-XXX",
"transferMethod": "bank",
"credentials": {
"bank": {
"recipientFullName": "John Doe",
"bank": "CIB",
"accountNumber": "010001000"
}
},
"labels": ["vip"],
"preferredCommunicationChannel": "sms",
"createdByUserId": "67a0b85fbc8100100f70bde0",
"createdAt": "2025-02-25T15:44:31.120Z",
"updatedAt": "2025-02-25T15:44:31.120Z"
},
"message": "success"
}Full parameter and response reference → Add a beneficiary.
Update a beneficiary
Update a saved beneficiary.
| Endpoint | Value |
|---|---|
| TEST URL | https://test-api.kashier.io/v2/parties/:id |
| LIVE URL | https://api.kashier.io/v2/parties/:id |
| Method | PUT |
type, name, and method are required on every update
Even when their values have not changed. An update that omits method is rejected rather than treated as a partial edit, so read the beneficiary first and send its method back if you are only changing a label.
curl --location --request PUT 'https://test-api.kashier.io/v2/parties/:id' \
--header 'Authorization: YOUR_TEST_SECRET_KEY' \
--header 'Content-Type: application/json' \
--data '{
"type": "beneficiary",
"name": "John Doe",
"phoneNumber": "01555539512",
"countryCode": "+20",
"emailAddress": "[email protected]",
"method": {
"type": "bank",
"credentials": {
"recipientFullName": "John Doe",
"bank": "CIB",
"accountNumber": "010001000"
}
},
"labels": ["vip"]
}'Headers
| Key | Description |
|---|---|
| Authorization | The Authorization is the secret key that is used to identify the merchant. You can obtain it from Kashier dashboard. Learn more about Authorization. |
Response
This route nests its payload one level deeper than the others — the updated record is at body.body.
{
"body": {
"message": "Customer updated successfully",
"body": {
"_id": "67ba05fd87c9970012389429",
"type": "beneficiary",
"name": "John Doe",
"customerId": "B-1740244477202",
"merchantId": "MID-XXXXX-XXX",
"transferMethod": "bank",
"credentials": {
"bank": {
"recipientFullName": "John Doe",
"bank": "CIB",
"accountNumber": "010001000"
}
},
"labels": ["vip"],
"updatedAt": "2025-02-26T09:55:34.630Z"
}
},
"message": "success"
}Full parameter and response reference → Update a beneficiary.
Delete a beneficiary
Delete a saved beneficiary. The response echoes back only the identifying fields of the record that was removed.
| Endpoint | Value |
|---|---|
| TEST URL | https://test-api.kashier.io/v2/parties/:id?type=beneficiary |
| LIVE URL | https://api.kashier.io/v2/parties/:id?type=beneficiary |
| Method | DELETE |
curl --location --request DELETE 'https://test-api.kashier.io/v2/parties/:id?type=beneficiary' \
--header 'Authorization: YOUR_TEST_SECRET_KEY'Headers
| Key | Description |
|---|---|
| Authorization | The Authorization is the secret key that is used to identify the merchant. You can obtain it from Kashier dashboard. Learn more about Authorization. |
Full parameter and response reference → Delete a beneficiary.
Save a beneficiary while creating a transfer
Set saveBeneficiary: true on a Create transfer request to save the recipient as you pay them. Same endpoint, one extra flag — recipientName becomes the beneficiary's name.
| Endpoint | Value |
|---|---|
| TEST URL | https://test-fep.kashier.io/v3/transfers/single |
| LIVE URL | https://fep.kashier.io/v3/transfers/single |
| Method | POST |
curl --location 'https://test-fep.kashier.io/v3/transfers/single' \
--header 'Authorization: YOUR_TEST_SECRET_KEY' \
--header 'Content-Type: application/json' \
--data '{
"amount": 10,
"method": "wallet",
"recipientName": "John Doe",
"recipientNumber": "01111111111",
"saveBeneficiary": true
}'The recipient number above is the sandbox number that forces a successful wallet transfer — see Payouts sandbox testing.
Saving is best-effort
The beneficiary is saved alongside the transfer, but a failure to save it never fails the transfer. If the method has no beneficiary equivalent, or beneficiary storage rejects the details, the payout still goes through and you simply get no saved record back.
Pay a saved beneficiary
Send partyId on a transfer and Kashier resolves the saved recipient for you. There is no beneficiaryId field — partyId is it.
| Endpoint | Value |
|---|---|
| TEST URL | https://test-fep.kashier.io/v3/transfers/single |
| LIVE URL | https://fep.kashier.io/v3/transfers/single |
| Method | POST |
recipientName, recipientNumber, recipientBank, and cardToken are all filled in from the saved record, so the request carries only the id, the amount, and the method.
curl --location 'https://test-fep.kashier.io/v3/transfers/single' \
--header 'Authorization: YOUR_TEST_SECRET_KEY' \
--header 'Content-Type: application/json' \
--data '{
"amount": 10,
"method": "bank",
"partyId": "67ba0311bf4f31001203c6c2",
"merchantTransferId": "TRF-YOUR-OWN-UNIQUE-ID"
}'Paste the _id of one of your own beneficiaries into the panel — List beneficiaries above returns it.
| Error | Cause |
|---|---|
PARTY_NOT_FOUND | No beneficiary with that id on your account, or it has been deleted. |
PARTY_METHOD_MISMATCH | The method you sent disagrees with the beneficiary's saved transferMethod. Send the method the beneficiary was saved with. |
A beneficiary you just created may not be payable yet
Payouts resolve partyId against their own copy of your beneficiaries, which is updated asynchronously. A record created moments ago can briefly return PARTY_NOT_FOUND. If you need to pay a recipient the instant you save them, use saveBeneficiary on the transfer itself — one call that pays and saves, with no id to wait for.
Or send the details yourself
If you would rather build the transfer body from the saved record, read it with Get beneficiary details and map the credentials onto the transfer's recipient fields.
transferMethod | recipientName | recipientBank | recipientNumber |
|---|---|---|---|
bank | credentials.bank.recipientFullName | credentials.bank.bank | credentials.bank.accountNumber |
wallet / instant wallet | credentials[method].recipientFullName | — | credentials[method].walletNumber |
octo card | credentials['octo card'].recipientFullName | — | credentials['octo card'].octoCardId |
internal account | credentials['internal account'].recipientFullName | — | credentials['internal account'].kashierAccountId |
Cards cannot be mapped this way
A saved card returns only a masked number, never the card itself, so there is nothing to copy into recipientNumber. Pay a saved card beneficiary with partyId instead — the stored token travels with the record and never has to pass through your systems.
Bulk import
Importing beneficiaries from a spreadsheet takes two calls: one to upload and validate, one to commit. Nothing is saved until the second call, which is what lets you correct a sheet before it reaches your account.
Sheet columns
| Column | |
|---|---|
Beneficiary name | Country Code |
Phone number | Email |
Preferred communication channel | Transfer method |
Recipient/Cardholder name | Bank |
Account number | Wallet number |
Card number | Octo card ID |
Kashier account ID | Card number token |
Transfer method takes a human label: Bank, Wallet, Instant Wallet, Card, Octo Card, or Internal Account. For each row, fill in only the credential columns that match that row's method and leave the rest blank.
Upload and validate
| Endpoint | Value |
|---|---|
| TEST URL | https://test-api.kashier.io/v2/parties/import?type=beneficiary |
| LIVE URL | https://api.kashier.io/v2/parties/import?type=beneficiary |
| Method | POST |
The upload is an XLSX file sent as multipart form data under the field name file.
curl --location --request POST 'https://test-api.kashier.io/v2/parties/import?type=beneficiary' \
--header 'Authorization: YOUR_TEST_SECRET_KEY' \
--form 'file=@"beneficiaries.xlsx"'Every row comes back with an errors field: null when the row is valid, or an object of field: reason pairs when it is not. If at least one row passed, the response also carries a correlationId — your handle on the validated batch.
{
"beneficiaries": [
{
"name": "John Doe",
"phoneNumber": "1555539512",
"countryCode": "+20",
"preferredCommunicationChannel": "sms",
"method": {
"type": "bank",
"credentials": {
"recipientFullName": "John Doe",
"bank": "CIB",
"accountNumber": "010001000"
}
},
"labels": ["vip"],
"errors": null
},
{
"name": "Jane Doe",
"phoneNumber": "1509876543",
"countryCode": "+20",
"method": {
"type": "wallet",
"credentials": {}
},
"errors": {
"walletNumber": "is required"
}
}
],
"correlationId": "48701e20-ffa2-4c33-be5e-fef3dbfaf94a",
"message": "Some beneficiaries were ignored because they are invalid"
}Full parameter and response reference → Upload a beneficiaries sheet for review.
Save the imported beneficiaries
| Endpoint | Value |
|---|---|
| TEST URL | https://test-api.kashier.io/v2/parties/save-parties?correlationId=:correlationId&type=beneficiary |
| LIVE URL | https://api.kashier.io/v2/parties/save-parties?correlationId=:correlationId&type=beneficiary |
| Method | GET |
Rows that failed validation in the upload step are not saved.
curl --location 'https://test-api.kashier.io/v2/parties/save-parties?correlationId={{correlationId}}&type=beneficiary' \
--header 'Authorization: YOUR_TEST_SECRET_KEY'Headers
| Key | Description |
|---|---|
| Authorization | The Authorization is the secret key that is used to identify the merchant. You can obtain it from Kashier dashboard. Learn more about Authorization. |
Full parameter and response reference → Save the uploaded beneficiaries.
Permissions
Beneficiary endpoints are permissioned separately from the rest of the dashboard API. A user calling them needs the matching action on their role:
| Action | Grants |
|---|---|
view_beneficiary | List and read. Scoped to own or any — an own-scoped user sees only the beneficiaries they created. |
create_beneficiary | Add a beneficiary, and bulk import. |
edit_beneficiary | Update a beneficiary. |
delete_beneficiary | Delete a beneficiary. |