Beneficiaries
The people and businesses you pay, with their attached payment methods.
A payment needs two IDs from this endpoint: beneficiaryId (who) and beneficiaryPaymentMethodId (how — which of their attached payment methods receives the funds).
Listing beneficiaries
GET /v1/beneficiaries — scope beneficiaries:read.
curl 'https://public-sandbox.tryjeeves.com/v1/beneficiaries?page=1&limit=50' \
-H "Authorization: Bearer $JEEVES_ACCESS_TOKEN"{
"data": [
{
"beneficiaryId": "3f1c9a52-6b7e-4d80-9a13-5c2e8f0b4d76",
"entityType": "individual",
"firstName": "Ana",
"lastName": "Gómez",
"name": null,
"bankCountry": "MX",
"bankCurrency": "MXN",
"createdAt": "2026-09-10T14:02:11Z",
"paymentMethods": [
{
"paymentMethodId": "b7d2e4a1-8c05-49f3-a6b2-1e7d9c3f5024",
"paymentMethod": "spei",
"accountNumber": "••••0001"
}
]
},
{
"beneficiaryId": "8a4e7c13-2d95-4f6b-8e01-7b3a5d9c2f48",
"entityType": "business",
"firstName": null,
"lastName": null,
"name": "Tortillería del Norte S.A. de C.V.",
"bankCountry": "MX",
"bankCurrency": "MXN",
"createdAt": "2026-09-11T09:41:55Z",
"paymentMethods": [
{ "paymentMethodId": "c91a06b4-7f38-4e52-b8d7-0a5c4e1f9362", "paymentMethod": "spei", "accountNumber": "••••4417" },
{ "paymentMethodId": "d04b18e7-5a29-4c63-9f81-2d6e7b0a4c15", "paymentMethod": "wire", "accountNumber": "••••1897" }
]
}
],
"count": 42
}Reading the shape
| Field | Notes |
|---|---|
entityType | individual populates firstName/lastName; business populates name. |
bankCountry / bankCurrency | Where and in what currency this beneficiary is paid — this is the destination side of your corridor. |
paymentMethods[] | Every live method attached to the beneficiary (spei, wire, ach, swift, …). May be empty — a beneficiary with no methods can't be paid yet. |
accountNumber | Masked — only the last four characters are shown. An account of four characters or fewer is masked completely, and a method with no account number is null. |
There is no status field on a beneficiary or on a payment method: deleted beneficiaries and deleted payment methods are simply never returned.
Pick the method whose rail you want: for the business above, spei delivers via the local Mexican rail, wire via international wire.
Destination coverage: beneficiaries can be paid in 190+ countries. bankCountry/bankCurrency and the attached payment methods define the corridor — the local rail differs by country (SPEI in Mexico, PIX in Brazil, SEPA in the Eurozone, ACH in the US, Faster Payments in the UK, and so on), with international wire as the broad fallback. The examples in these docs use a handful of corridors for concreteness — see Destination coverage for the flagship rails and how to confirm a specific corridor.
Pagination
Page-based, matching the rest of the Jeeves Public API: pass page (1-based, default 1) and limit (default 50, values above 200 are clamped to 200, not rejected). A search parameter is also accepted — free text, up to 100 characters, matching name, firstName and lastName as a substring and a payment method's account number by suffix. Any other query parameter is rejected with 400 validation_failed and code: unknown_field — unknown parameters are not ignored.
The envelope is data and count, and nothing else. count is the total matching your query across all pages — stop once page × limit reaches it. The request page and limit are not echoed back, so keep track of them yourself.
Getting one beneficiary
GET /v1/beneficiaries/{beneficiaryId} — scope beneficiaries:read.
curl 'https://public-sandbox.tryjeeves.com/v1/beneficiaries/3f1c9a52-6b7e-4d80-9a13-5c2e8f0b4d76' \
-H "Authorization: Bearer $JEEVES_ACCESS_TOKEN"The response is the bare beneficiary object, identical to one entry of data[] in the list — there is no data or count envelope. It includes contactInformation and paymentMethods[]:
{
"beneficiaryId": "3f1c9a52-6b7e-4d80-9a13-5c2e8f0b4d76",
"entityType": "individual",
"firstName": "Ana",
"lastName": "Gómez",
"name": null,
"bankCountry": "MX",
"bankCurrency": "MXN",
"contactInformation": {
"streetAddress": null,
"city": null,
"state": null,
"postcode": null,
"countryCode": null,
"phoneNumber": null,
"emailAddress": null
},
"createdAt": "2026-09-10T14:02:11Z",
"paymentMethods": [
{
"paymentMethodId": "b7d2e4a1-8c05-49f3-a6b2-1e7d9c3f5024",
"paymentMethod": "spei",
"accountNumber": "••••0001"
}
]
}Account numbers are masked to the last four characters (an account of four characters or fewer is masked completely, and a method with no account number is null).
An unknown, deleted, malformed or other-customer beneficiaryId returns the same 404 beneficiary_not_found body — never a 403, so the API never confirms that someone else's beneficiary exists.
Creating beneficiaries
Creating a payee takes three calls: create the beneficiary, ask what the corridor needs, then attach a payment method. All three use the beneficiaries:write scope, except the schema read, which needs beneficiaries:read. Add beneficiaries:write to the scopes you request on the token call.
1. Create the beneficiary
POST /v1/beneficiaries — scope beneficiaries:write.
curl -X POST 'https://public-sandbox.tryjeeves.com/v1/beneficiaries' \
-H "Authorization: Bearer $JEEVES_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"entityType": "individual",
"firstName": "Ana",
"lastName": "Gómez",
"bankCountry": "MX",
"bankCurrency": "MXN"
}'{
"beneficiaryId": "3f1c9a52-6b7e-4d80-9a13-5c2e8f0b4d76",
"status": "active",
"createdAt": "2026-11-02T14:02:11Z"
}| Field | Rule |
|---|---|
entityType | individual or business. |
firstName + lastName | For an individual. |
name | For a business. |
bankCountry | Uppercase ISO 3166-1 alpha-2, for example MX. |
bankCurrency | One of USD, MXN, BRL, COP, EUR, GBP, CAD, USDB, USDC, USDT, EURC. Any other value is a 400 with unsupported_value. |
Each name field is at most 255 characters. A required name field that is missing, or one that belongs to the other entityType, is a 422. Unknown fields are rejected with unknown_field, and a JSON array body is a 400. The beneficiary is created with no payment methods — attach one in step 3.
| Status | code | Meaning |
|---|---|---|
| 400 | validation_failed / invalid_request_body | The body is not valid, or could not be parsed. |
| 422 | validation_failed | A name field is missing (required) or belongs to the other entityType (unknown_field). The offending fields are listed in errorData.errors[], not in the top-level errors[]. |
| 422 | validation_failed | bankCountry is well formed but not a known country: errors[] carries unsupported_value. |
| 422 | validation_failed with errors[].code: duplicate | A beneficiary with the same name, bank country and bank currency already exists. errors[] names name for a business, or firstName and lastName for an individual. This also matches a Boleto-source vendor that the list endpoint doesn't show. |
A name-field mismatch looks like this:
{
"message": "The name fields do not match entityType: individuals use firstName and lastName, businesses use name.",
"code": "validation_failed",
"statusCode": 422,
"requestId": "7186288b-2f41-4c6a-9d85-1b0e3a7c5f92",
"errorData": {
"errors": [
{ "field": "firstName", "code": "required", "message": "firstName is required." },
{ "field": "lastName", "code": "required", "message": "lastName is required." },
{ "field": "name", "code": "unknown_field", "message": "name is not allowed." }
]
}
}This call is not idempotent. No Idempotency-Key is read; a header you send is ignored. The duplicate check is your retry protection. Two simultaneous identical requests can both succeed, because nothing enforces uniqueness at the database — don't fire the same create twice in parallel.
2. Ask what the corridor needs
GET /v1/beneficiary-schemas — scope beneficiaries:read.
curl 'https://public-sandbox.tryjeeves.com/v1/beneficiary-schemas?sourceCurrency=USD&productAccountType=self-funded&destinationCountry=MX&destinationCurrency=MXN&entityType=individual' \
-H "Authorization: Bearer $JEEVES_ACCESS_TOKEN"| Query parameter | Notes |
|---|---|
sourceCurrency | Required. The currency you pay from — one of the supported currencies listed for bankCurrency above. |
productAccountType | Required. credit-line, self-funded or crypto-wallet. |
destinationCountry | Required. Uppercase ISO 3166-1 alpha-2. |
destinationCurrency | Required. The currency the beneficiary receives — one of the supported currencies listed for bankCurrency above. |
entityType | Required, because the form depends on it. |
paymentMethod | Optional. Narrows the response to one method, using a paymentMethod value as this endpoint returns it. |
{
"paymentMethods": [
{
"paymentMethod": "local_bank_transfer",
"fields": [
{
"fieldName": "accountNumber",
"type": "string",
"required": true,
"pattern": "^\\d{10,18}$",
"description": "Account number"
},
{
"fieldName": "bankCode",
"type": "string",
"required": true,
"description": "Bank code"
}
],
"contactFields": [],
"purposeCodes": [
{ "code": "purchase_of_services", "description": "Purchase of services" },
{ "code": "travel", "description": "Travel" }
]
}
]
}The values above are illustrative — always read the real list for your corridor.
| Key | What it is |
|---|---|
paymentMethod | The value to send, unchanged, as paymentMethod in step 3. |
fields[] | { fieldName, type, required, pattern?, allowedValues?, description }, with type one of string, enum or number; allowedValues entries can be strings or numbers. The fieldName values are the keys of bankingInformation in step 3. |
contactFields[] | The same shape. The fieldName values are the keys of contactInformation. Empty when the method needs none. |
purposeCodes[] | { code, description } — the exact list that payment-time validation accepts. |
Patterns and purposeCode are enforced again when you create the payment. Your company must hold the funding source you name with sourceCurrency and productAccountType; the methods and purpose codes returned are those for that funding source.
Two vocabularies.
paymentMethodhere (for examplelocal_bank_transfer) is the value you send in step 3. When you later read the beneficiary, its methods are reported in a normalised vocabulary (ach,spei,wire, and so on — see Coverage), so the two are not interchangeable.
| Status | code | Meaning |
|---|---|---|
| 400 | validation_failed | A query parameter is missing or invalid. A missing one appears as errors[].code: required — there is no missing_parameter code. A currency outside the supported list, or a destinationCountry that is not an alpha-2 code, reports unsupported_value. |
| 422 | unsupported_corridor | No payment method is available for this destination. |
| 422 | unsupported_payment_method | The paymentMethod you asked for isn't offered for this corridor. |
| 422 | validation_failed | errors[] reports unsupported_value on sourceCurrency when your company doesn't hold that funding source. |
3. Attach a payment method
POST /v1/payment-methods — scope beneficiaries:write.
curl -X POST 'https://public-sandbox.tryjeeves.com/v1/payment-methods' \
-H "Authorization: Bearer $JEEVES_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beneficiaryId": "3f1c9a52-6b7e-4d80-9a13-5c2e8f0b4d76",
"paymentMethod": "local_bank_transfer",
"bankingInformation": { "accountNumber": "0123456789", "bankCode": "021000021" }
}'{
"paymentMethodId": "b7d2e4a1-8c05-49f3-a6b2-1e7d9c3f5024",
"status": "active"
}Send the returned paymentMethodId as beneficiaryPaymentMethodId when you create a payment.
- No corridor parameters. The corridor is taken from the beneficiary's stored bank country and currency.
paymentMethodmust be a value from the schema response in step 2, sent unchanged.bankingInformationkeys must equalfields[].fieldName, and each value is checked against itspattern.contactInformationphoneNumbertakes the form+<dialing code> <number>(for example+1 2125551234) andpostcodeis at most 15 characters; both are published aspatternincontactFields. It is required when the method'scontactFieldsis not empty, unless the beneficiary already has that value stored. WhencontactFieldsis empty, any contact key is rejected withunknown_field.
| Status | code | Meaning |
|---|---|---|
| 404 | beneficiary_not_found | Unknown, deleted, malformed or other-customer beneficiaryId. |
| 409 | payment_method_already_exists | The beneficiary already has a payment method on this rail. |
| 422 | validation_failed | errors[] names bankingInformation.<field> for banking fields, keeps contactInformation.<field> for contact fields and uses paymentMethod for the method itself. Reasons: required, invalid_format (the value fails its pattern, which the message names), unsupported_value (a paymentMethod the corridor doesn't offer), unknown_field. A wallet blocked by screening is also a 422 validation_failed, with no per-field detail. |
One payment method per rail per beneficiary. The duplicate check uses paymentMethod only, not the account number. To replace a method you would have to delete the old one first, and the public API has no delete — plan the account number before you attach it.
This call is not idempotent. No Idempotency-Key is read, and a retry after a success gets 409.
Bank-detail validation is corridor-specific — a detail that fails the corridor's rules at payment time surfaces as 422 validation_failed.