> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stableyard.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# Bank accounts: link a beneficiary for payouts

> Save a bank beneficiary as a payout destination: fields per country, states, coverage.

A bank account is an existing bank beneficiary saved under a Stableyard account as a payout destination. It can belong to a supplier, contractor or the account holder; linking does not prove ownership by the UPA subject. Stableyard does not open it. In the API it is a `BankAccount`, with an id prefixed `bank_account_`.

```mermaid theme={null}
flowchart LR
  R["GET bank-account-requirements<br/>fields and availability"] --> L["POST bank-accounts<br/>link"]
  L --> V["Verified<br/>status: active"]
  V --> P["POST /v2/payments<br/>destination.type: bank_account"]
```

## When to use it

Use it for repeated payouts to a saved beneficiary or withdrawals to the customer's own bank. US payouts require a verified linked bank; one-time US `external_bank` transfers are not released. Other enabled markets support one-time beneficiaries with `destination.type: "external_bank"`. See [Sending payments](/payments/sending-payments).

## Coverage

| Country | Can be linked | Can receive payouts | Can receive settlement |
| - | - | - | - |
| United States | Yes | Implemented; staging-certified | No |
| Philippines | Yes | No | No |
| Vietnam | Yes | No | No |
| 47 other countries | No. Field schemas only, `implementation: "planned"` | No | No |

Philippine and Vietnamese links are saved, but cannot execute a payout today. No market can receive settlement to a bank yet. See [Supported regions and currencies](/supported-regions-and-currencies).

## What linking needs

| Account | Needs |
| - | - |
| `individual` | Approved identity verification. See [Individual KYC](/concepts/individual-kyc) |
| `business` | An active `linked_bank` capability from hosted business verification. US banks only |
| Either | A linked-bank route enabled for your app in that country |

A payout to the bank needs more: the bank `active`, and an active `linked_bank` capability on the account. See [Capability activation](/concepts/capability-activation).

## Read the fields for each country

```bash theme={null}
curl https://staging-api-v2.stableyard.fi/v2/accounts/acct_123/bank-account-requirements \
  -u "$APP_ID:$APP_SECRET"
```

```json theme={null}
{
  "version": 1,
  "accountId": "acct_123",
  "countries": [
    {
      "country": "US",
      "name": "United States",
      "currency": "USD",
      "implementation": "supported",
      "available": true,
      "unavailableReason": null,
      "schema": { "type": "object", "properties": { "accountNumber": { "type": "string" } } },
      "alternatives": []
    }
  ]
}
```

Each entry carries a strict JSON Schema for that country's fields, shared with server validation. Render your form from `schema`, and offer a country only when `available` is `true`.

| `unavailableReason` | Meaning |
| - | - |
| `bank_country_not_enabled` | Not implemented for linking, including every `planned` country |
| `bank_link_route_unavailable` | Implemented, but no route is enabled for your app |
| `bank_details_storage_unavailable` | Secure storage for bank details is not available right now |

`alternatives` lists shortcut identifiers, such as a phone-number alias, that a country defines. Every one is `available: false` today.

## Link a bank

<Tabs>
  <Tab title="United States">
    ```bash theme={null}
    curl -X POST https://staging-api-v2.stableyard.fi/v2/accounts/acct_123/bank-accounts \
      -u "$APP_ID:$APP_SECRET" \
      -H "Idempotency-Key: bank:acct_123:v1" \
      -H "Content-Type: application/json" \
      -d '{
        "country": "US",
        "currency": "USD",
        "accountHolderName": "Jane Customer",
        "beneficiaryType": "individual",
        "bankName": "Example Bank",
        "accountNumber": "000123456789",
        "accountType": "checking",
        "routing": { "type": "aba", "routingNumber": "021000021" },
        "rail": "ach",
        "beneficiaryAddress": { "street1": "100 Example Street", "city": "Denver", "region": "CO", "postalCode": "80202", "country": "US" },
        "bankAddress": { "street1": "270 Park Avenue", "city": "New York", "region": "NY", "postalCode": "10017", "country": "US" }
      }'
    ```

    | Field | Required | Notes |
    | - | - | - |
    | `country`, `currency` | Yes | `US`, `USD` |
    | `accountHolderName` | Yes | Up to 160 characters |
    | `beneficiaryType` | No | `individual` or `business`. Defaults to `individual` |
    | `bankName` | Yes | Up to 160 characters |
    | `accountNumber` | Yes | 4 to 17 digits |
    | `accountType` | Yes | `checking` or `savings` |
    | `routing` | Yes | `{ "type": "aba", "routingNumber": "<9 digits>" }`, for the selected rail |
    | `rail` | No | `ach`, `fedwire` or `fednow`, from the rails available to you. Defaults to `ach` |
    | `beneficiaryAddress`, `bankAddress` | Yes | `street1`, `city`, `region`, `postalCode`, `country`; `street2` optional |

    Stableyard registers the bank with a banking partner. When that completes, the bank is `active`. If the partner needs the customer to act first, `provisioning.nextAction.type` is `complete_provider_onboarding` with a short-lived `url`, returned only in that response.
  </Tab>

  <Tab title="Philippines">
    ```bash theme={null}
    curl -X POST https://staging-api-v2.stableyard.fi/v2/accounts/acct_123/bank-accounts \
      -u "$APP_ID:$APP_SECRET" \
      -H "Idempotency-Key: bank:acct_123:ph:v1" \
      -H "Content-Type: application/json" \
      -d '{
        "country": "PH",
        "currency": "PHP",
        "accountHolderName": "Juan Dela Cruz",
        "bankName": "Example Bank",
        "bankCode": "directory_code",
        "accountNumber": "0000123456"
      }'
    ```

    | Field | Required | Notes |
    | - | - | - |
    | `country`, `currency` | Yes | `PH`, `PHP` |
    | `accountHolderName` | Yes | Up to 160 characters |
    | `beneficiaryType` | No | `individual` or `business`. Defaults to `individual` |
    | `bankName` | Yes | From the bank directory |
    | `bankCode` | Yes | `code` from the bank directory |
    | `accountNumber` | Yes | 4 to 34 digits |
    | `rail` | No | `bank_transfer`, the only value |

    A Philippine link stays `pending_verification`. Being in the bank directory never verifies ownership.
  </Tab>

  <Tab title="Vietnam">
    ```bash theme={null}
    curl -X POST https://staging-api-v2.stableyard.fi/v2/accounts/acct_123/bank-accounts \
      -u "$APP_ID:$APP_SECRET" \
      -H "Idempotency-Key: bank:acct_123:vn:v1" \
      -H "Content-Type: application/json" \
      -d '{
        "country": "VN",
        "currency": "VND",
        "accountHolderName": "Nguyen Van A",
        "bankName": "Example Bank",
        "bankCode": "directory_code",
        "accountNumber": "0000123456"
      }'
    ```

    The fields match the Philippines, with `VN` and `VND`. A banking partner verifies the beneficiary when you link it: a verified bank is `active`, otherwise it stays `pending_verification`.
  </Tab>
</Tabs>

Read the bank directory for the Philippines and Vietnam. It returns codes and names only:

```bash theme={null}
curl "https://staging-api-v2.stableyard.fi/v2/accounts/acct_123/bank-account-banks?country=PH" \
  -u "$APP_ID:$APP_SECRET"
```

```json theme={null}
{ "country": "PH", "currency": "PHP", "banks": [{ "code": "directory_code", "name": "Example Bank" }] }
```

## What a link returns

```json theme={null}
{
  "bankAccount": {
    "id": "bank_account_123",
    "accountId": "acct_123",
    "accountHolderName": "Jane Customer",
    "country": "US",
    "currency": "USD",
    "bankName": "Example Bank",
    "accountNumberLast4": "6789",
    "rail": "ach",
    "status": "active",
    "verification": { "version": 1, "verifiedAt": "2026-10-01T12:00:04Z" },
    "disabledAt": null,
    "createdAt": "2026-10-01T12:00:00Z",
    "updatedAt": "2026-10-01T12:00:04Z"
  },
  "settlementDestination": { "id": "destination_123", "type": "bank_account", "status": "restricted", "preferred": false },
  "provisioning": { "providerProvisioningCompleted": true, "settlementReady": false, "nextAction": { "type": "none" } }
}
```

* **Raw details never come back.** Only the last four digits. Stableyard stores the full details encrypted.
* **`settlementReady` is `false`.** Linking makes a payout destination, not a settlement destination.
* **Retry with the same key.** `Idempotency-Key` is required and must contain 1–256 characters after trimming surrounding whitespace. The same key with identical input returns the original link, and changed input conflicts.

## Statuses

```mermaid theme={null}
stateDiagram-v2
  [*] --> pending_verification: linked, not yet verified
  [*] --> active: linked and verified
  pending_verification --> active: verified
  pending_verification --> disabled: DELETE
  active --> disabled: DELETE
  disabled --> [*]
```

| `status` | Can receive a payout | Meaning |
| - | - | - |
| `pending_verification` | No | Saved. Verification is not finished |
| `active` | United States only | Verified |
| `restricted` | No | Not in service for new payouts |
| `disabled` | No | Disabled. History and masked identity are kept |

List an account's banks with `GET /v2/accounts/{accountId}/bank-accounts`, filtered by `status` and paged with `limit` (up to 100, default 20) and `cursor`. Read one with `GET /v2/accounts/{accountId}/bank-accounts/{bankAccountId}`.

## Disable a bank

```bash theme={null}
curl -X DELETE https://staging-api-v2.stableyard.fi/v2/accounts/acct_123/bank-accounts/bank_account_123 \
  -u "$APP_ID:$APP_SECRET"
```

Disabling stops future use and keeps the financial history and masked identity. The encrypted details become eligible for deletion after 30 days, later if a settlement still references them. Disabling twice keeps the first date. A preferred destination must be changed before it can be disabled.

## Payment rail identifiers

`POST /v2/accounts/{accountId}/payment-rail-identifiers` saves a shortcut destination, such as an alias, as an opaque token issued by a provider. It never accepts a raw alias or QR payload, and the identifier stays `pending_verification`: no rail can pay one today.

## Errors

| Code | HTTP | When |
| - | - | - |
| `bad_request` | 400 | A field fails the country's schema |
| `conflict` | 409 | An `individual` without approved identity verification (`details.nextAction.type: "complete_kyc"`), or the bank is already linked |
| `idempotency_conflict` | 409 | The same key with different input |
| `payment_method_not_supported` | 422 | Linking is not enabled for this country, or a `business` account has no active banking relationship |
| `payment_provider_unavailable` | 503 | No linked-bank route is available right now |

## Webhooks

There is no bank-account event. `account.updated` fires when a bank is linked, verified or disabled; read the bank account for its status.

## Related

* [Capability activation](/concepts/capability-activation): the `linked_bank` approval payouts need.
* [Sending payments](/payments/sending-payments): pay out to a linked bank, and the amount rule for each route.
* [On-ramp accounts](/concepts/on-ramp-accounts): the other direction.
* [Choose your pattern](/white-label/patterns): where linking fits in each integration.

<Card title="Next: Wallets and handles" icon="wallet" href="/concepts/wallets-and-handles">
  Connected wallets, payment handles and public payment acceptance.
</Card>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.