KashierDevelopersKashier Developers
Payouts

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.typeRequired credentials
bankrecipientFullName, bank, accountNumber
walletrecipientFullName, walletNumber
instant walletrecipientFullName, walletNumber
cardcardholderName, cardNumber, bank
octo cardrecipientFullName, octoCardId
internal accountrecipientFullName, kashierAccountId

Validation rules

RuleLimit
accountNumber7–34 characters
walletNumberEgyptian mobile number matching ^(010|011|012|015)\d{8}$
bankUppercased, then matched against Kashier's supported-bank list — see Bank codes
countryCodeRequired whenever you send a phoneNumber
cardNumberTokenized 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.

EndpointValue
TEST URLhttps://test-api.kashier.io/v2/parties?type=beneficiary&page=1&limit=20&sortType=-1&sortBy=name
LIVE URLhttps://api.kashier.io/v2/parties?type=beneficiary&page=1&limit=20&sortType=-1&sortBy=name
MethodGET

Query parameters

KeyDescription
typeRequired. Must be beneficiary.
searchSearch by name, phone number, or email address.
pagePage number. Default 1.
limitRecords per page. Default 20.
sortBySort field: name or createdAt. Default createdAt.
sortTypeSort order: 1 ascending, -1 descending. Default -1.
startDate / endDateOnly return beneficiaries created after / before this date.
labelsComma-separated label names.
branchIdsComma-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

KeyDescription
AuthorizationThe 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.

EndpointValue
TEST URLhttps://test-api.kashier.io/v2/parties/:id?type=beneficiary
LIVE URLhttps://api.kashier.io/v2/parties/:id?type=beneficiary
MethodGET
curl --location 'https://test-api.kashier.io/v2/parties/:id?type=beneficiary' \
  --header 'Authorization: YOUR_TEST_SECRET_KEY'

Headers

KeyDescription
AuthorizationThe 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.

EndpointValue
TEST URLhttps://test-api.kashier.io/v2/parties
LIVE URLhttps://api.kashier.io/v2/parties
MethodPOST

Body parameters

KeyDescription
typeRequired. Must be beneficiary.
nameThe beneficiary's name.
methodThe payment method — type plus its matching credentials. See Payment methods.
phoneNumberThe beneficiary's phone number. Optional.
countryCodeRequired when phoneNumber is set, for example +20.
emailAddressThe beneficiary's email address. Optional.
labelsArray of label strings used to organize beneficiaries. Optional.
customFieldsArray of { "name": …, "value": … } pairs. Optional.
preferredCommunicationChannelsms, email, or both. Optional.
presetNameSaves 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

KeyDescription
AuthorizationThe 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.

EndpointValue
TEST URLhttps://test-api.kashier.io/v2/parties/:id
LIVE URLhttps://api.kashier.io/v2/parties/:id
MethodPUT

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

KeyDescription
AuthorizationThe 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.

EndpointValue
TEST URLhttps://test-api.kashier.io/v2/parties/:id?type=beneficiary
LIVE URLhttps://api.kashier.io/v2/parties/:id?type=beneficiary
MethodDELETE
curl --location --request DELETE 'https://test-api.kashier.io/v2/parties/:id?type=beneficiary' \
  --header 'Authorization: YOUR_TEST_SECRET_KEY'

Headers

KeyDescription
AuthorizationThe 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.

EndpointValue
TEST URLhttps://test-fep.kashier.io/v3/transfers/single
LIVE URLhttps://fep.kashier.io/v3/transfers/single
MethodPOST
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.

EndpointValue
TEST URLhttps://test-fep.kashier.io/v3/transfers/single
LIVE URLhttps://fep.kashier.io/v3/transfers/single
MethodPOST

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.

ErrorCause
PARTY_NOT_FOUNDNo beneficiary with that id on your account, or it has been deleted.
PARTY_METHOD_MISMATCHThe 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.

transferMethodrecipientNamerecipientBankrecipientNumber
bankcredentials.bank.recipientFullNamecredentials.bank.bankcredentials.bank.accountNumber
wallet / instant walletcredentials[method].recipientFullNamecredentials[method].walletNumber
octo cardcredentials['octo card'].recipientFullNamecredentials['octo card'].octoCardId
internal accountcredentials['internal account'].recipientFullNamecredentials['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 nameCountry Code
Phone numberEmail
Preferred communication channelTransfer method
Recipient/Cardholder nameBank
Account numberWallet number
Card numberOcto card ID
Kashier account IDCard 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

EndpointValue
TEST URLhttps://test-api.kashier.io/v2/parties/import?type=beneficiary
LIVE URLhttps://api.kashier.io/v2/parties/import?type=beneficiary
MethodPOST

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

EndpointValue
TEST URLhttps://test-api.kashier.io/v2/parties/save-parties?correlationId=:correlationId&type=beneficiary
LIVE URLhttps://api.kashier.io/v2/parties/save-parties?correlationId=:correlationId&type=beneficiary
MethodGET

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

KeyDescription
AuthorizationThe 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:

ActionGrants
view_beneficiaryList and read. Scoped to own or any — an own-scoped user sees only the beneficiaries they created.
create_beneficiaryAdd a beneficiary, and bulk import.
edit_beneficiaryUpdate a beneficiary.
delete_beneficiaryDelete a beneficiary.

On this page