Before You Start
| Prerequisite | Detail |
|---|---|
| Bearer token | Issued via POST /v1/oauth/token |
| Approved customer | Customer customer_status must be APPROVED |
| Reference data | Country, bank, and branch identifiers (covered in Step 1) |
Onboarding Flow
Step 1 — Fetch Reference Data
Three catalogue endpoints feed into beneficiary creation. These are V1-only — no newer versions exist.1a. List Supported Countries
code (ISO alpha-3) → maps to country and currency in the create call. available_methods tells you whether to build a bank form or e-wallet form.
1b. List Bank / E-Wallet Identifiers
| Parameter | Required | Values |
|---|---|---|
country_code | Yes | ISO alpha-3 (from Step 1a) |
method | Yes | BANK or E_WALLET |
id → used as bank_routing[].number with scheme BANK_IDENTIFIER. Check has_branch — if true, proceed to 1c.
1c. List Branch Identifiers (conditional)
Only call this ifhas_branch is true from Step 1b.
id → used as bank_routing[].number with scheme BRANCH_IDENTIFIER.
Step 2 — Create the Beneficiary
Request Structure
Account Holder
Theaccount_holder object is polymorphic based on type.
Individual:
Account Holder Address
state uses ISO 3166-2 subdivision codes. Fetch valid values from GET /v1/countries/{country_code}/subdivisions.
Receiver Metadata
transaction_purpose_id → fetch from GET /v1/transaction-purposes?type=INDIVIDUAL or ?type=BUSINESS. occupation_id → fetch from GET /v1/occupations.
Relationship values by account holder type:
When account_holder.type is INDIVIDUAL:
| Category | Values |
|---|---|
| Employment | EMPLOYEE, FREELANCER, GIG_WORKER, CONTRACTOR |
| Personal | FAMILY_MEMBER, FRIEND, SELF, OTHER |
| Commercial | CUSTOMER, AFFILIATE |
account_holder.type is BUSINESS:
| Category | Values |
|---|---|
| Supply chain | SUPPLIER, VENDOR, SERVICE_PROVIDER, MERCHANT |
| Corporate structure | SUBSIDIARY, PARENT_COMPANY, AFFILIATE_BUSINESS |
| Financial | BANK_ACCOUNT, BROKER, EXCHANGE, WALLET |
| Other | CONTRACTOR, OTHER |
Developer Fee
Your markup on each payout through this beneficiary.fixed + (percentage × payout amount).
Deposit & Refund Instructions
Deposit instruction — how source funds arrive:Settlement Config (optional)
true, payouts execute automatically once funds hit the liquidation address. When false, you must call the settle endpoint manually.
Destination: Bank Account
Add three fields:bank_account, bank_routing, and bank_address.
bank_account:
| Field | Values |
|---|---|
scheme | LOCAL, SWIFT |
type | CHECKING, SAVINGS, CURRENT |
| Scheme | When to use |
|---|---|
SWIFT | International transfers — BIC/SWIFT code |
ACH | US domestic — ACH routing number |
WIRE | US domestic — Fedwire routing number |
IBAN | Europe, Middle East — IBAN |
BSB | Australia — Bank/State/Branch code |
IFSC | India — IFSC code |
BANK_IDENTIFIER | Generic — use id from /v1/beneficiaries/methods |
BRANCH_IDENTIFIER | Generic — use id from /v1/beneficiaries/methods/:id/branches |
BANK_CODE | Country-specific bank code |
BRANCH_CODE | Country-specific branch code |
TRANSIT_NUMBER | Canada — transit number |
Destination: E-Wallet
Replace the three bank fields with a singlee_wallet object.
scheme values come from the name field in the /v1/beneficiaries/methods?method=E_WALLET response.
Response
beneficiary_id — you’ll need it for payouts, document uploads, and detail lookups.
De-Duplication Rules
Fin rejects duplicate beneficiaries with a409 Conflict. Uniqueness is determined by:
| Destination | Fields checked |
|---|---|
| Bank | bank_account.scheme + bank_account.number + bank_routing.scheme + bank_routing.number |
| E-Wallet | e_wallet.scheme + e_wallet.number |
409 response includes the existing beneficiary_id so you can reuse it instead of creating a new one.
Country-Specific Validation
| Country | Rule |
|---|---|
| GBR | Bank account number must match ^[0-9]{4,9}$ for both LOCAL and SWIFT schemes |
Step 3 — Upload Documents (Optional)
attachments array in POST /v1/transactions/transfer-payout.
Step 4 — Verify & Confirm
Fetch Beneficiary Details
| Field | What to check |
|---|---|
active | Boolean — controlled by you via PATCH /v1/beneficiaries |
status | Enum — controlled by Fin’s internal validation |
deposit_instruction.liquidation_address | System-assigned — this is where you send USDC to trigger a payout |
bank_routing[].name | System-resolved bank/branch names from the identifiers you passed |
Eligibility for Transactions
A beneficiary can receive payouts only when both conditions are met:| Field | Required value | Controlled by |
|---|---|---|
active | true | You (via PATCH /v1/beneficiaries) |
status | ACTIVE | Fin (system-managed) |
active is your on/off switch. status reflects Fin’s validation outcome.
status values:
| Status | Meaning |
|---|---|
PROCESSING | Beneficiary is being validated by Fin |
ACTIVE | Fin validation passed |
INACTIVE | Fin has deactivated the beneficiary |
REJECTED | Fin validation failed — cannot be used |
PROCESSING with active = true. Listen for the beneficiary.status webhook to know when Fin transitions it to ACTIVE or REJECTED.
Managing Beneficiaries
List All Beneficiaries for a Customer
Deactivate / Reactivate
active flag only — it does not change status. Setting active: false makes the beneficiary ineligible for payouts even if status is ACTIVE. Set active: true to re-enable.
Note: If Fin has setstatustoREJECTEDorINACTIVE, the beneficiary cannot transact regardless of theactiveflag. ForREJECTEDbeneficiaries, create a new one instead.
Webhooks
Subscribe to these events to track beneficiary lifecycle changes asynchronously.| Event | Fires when |
|---|---|
beneficiary.created | Beneficiary successfully created |
beneficiary.status | Status changes (PROCESSING → ACTIVE, ACTIVE → INACTIVE, PROCESSING → REJECTED) |
beneficiary.liquidation.deposit | USDC hits the beneficiary’s liquidation address |
beneficiary.created
beneficiary.liquidation.deposit
Fires when any transfer hits the liquidation address — useful for confirming funds arrival before payout execution.Error Reference
| Status | Cause | Action |
|---|---|---|
400 | Field validation failed | Check errors array for field-level details |
401 | Invalid / expired token | Re-authenticate via POST /v1/oauth/token |
409 | Duplicate beneficiary | Use the returned beneficiary_id from the error response |
422 | Request format error | Check required fields and data types |
V3 vs V2 — Key Differences
Create Beneficiary
| Aspect | V2 (POST /v2/beneficiaries) | V3 (POST /v3/beneficiaries) |
|---|---|---|
customer_id format | Free string (e.g., cust_123456) | UUID (strict) |
counter_party | Not supported | Required — FIRST_PARTY / THIRD_PARTY |
occupation_remarks in receiver_meta_data | Required | Optional |
| Response body | { data: { id, status } } | { data: { beneficiary_id } } |
| Response code | 200 | 200 |
Fetch Beneficiary Details
| Aspect | V2 (GET /v2/beneficiaries/details) | V3 (GET /v3/beneficiaries/details) |
|---|---|---|
status field | Not present | PROCESSING / ACTIVE / INACTIVE / REJECTED |
counter_party field | Not present | Present |
bank_routing[].number | Not returned (only name) | Returned alongside name |
What’s Next
With the beneficiary onboarded, you can:- Send a single payout →
POST /v1/transactions/transfer-payout - Send batch payouts →
POST /v1/batch/transactions/commit - Preview fees →
POST /v1/fee-calculation(passbeneficiary_idfor developer-fee-aware calculations) - Check FX rates →
GET /v1/fx-rate?currency_code={code}
