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

# Bank accounts and wallets

> Add and verify bank accounts per rail, and save customer crypto wallets.

A **bank account** is where a payout lands. A **wallet** is a saved crypto address for a customer. Both belong to one customer.

## Bank accounts

### Add a bank account

The customer must be `VERIFIED`. Call [`POST /bank/create`](/api-reference-exchange/endpoint/bank/create) with the rail in `bank_account_type` and its fields in `identifiers`.

<Tabs>
  <Tab title="India: account and IFSC">
    ```json theme={null}
    {
      "customer_id": "c2cf861b-342b-4318-a90e-85cd0312e82f",
      "bank_account_type": "ACCOUNT_DETAILS",
      "identifiers": { "account_number": "7627389201", "ifsc": "SBIN0001829" }
    }
    ```
  </Tab>

  <Tab title="India: UPI">
    ```json theme={null}
    {
      "customer_id": "c2cf861b-342b-4318-a90e-85cd0312e82f",
      "bank_account_type": "UPI",
      "identifiers": { "vpa": "priya@oksbi" }
    }
    ```
  </Tab>
</Tabs>

You can instead send the flat fields `account_number` and `ifsc`, or `vpa`, without `identifiers`. Save the returned `id`. Quotations use it as `bank_id`.

<Note>
  For beneficiaries in other markets (UAE, UK, SEPA, Brazil, Mexico, US and others), use [`POST /bank/payout/create`](/guides/customers/payout-only-onboarding) instead.
</Note>

### Rules

* The account holder's name must match the customer's KYC name. Zapyd checks it.
* A customer can have up to **3 active accounts per rail**.
* An account that's already linked to another customer is rejected.
* NRE accounts aren't supported in India.
* India remittance payouts (RDA) need `ACCOUNT_DETAILS`. UPI isn't supported for them.

### Verification

Zapyd verifies each new account, for example with a penny drop in India. The status moves from `PROCESSING` to `VERIFIED` or `FAILED`, and Zapyd sends a `BANK` webhook.

| `failure_reason` | Meaning | What to tell the user |
| - | - | - |
| `INVALID_IFSC` | The IFSC doesn't exist or doesn't match the bank | Check the IFSC |
| `INVALID_ACCOUNT_NUMBER` | The account number doesn't exist | Check the account number |
| `NAME_MISMATCH` | The account holder isn't the KYC name | Add an account in their own name |
| `ACCOUNT_TYPE_NRE` | NRE accounts aren't supported | Use a resident savings or current account |
| `PENNY_DROP_FAILED` | The test deposit didn't go through | Try another account |
| `BANK_RISK_CHECK_FAILED` | The account failed risk checks | Use another account. Contact support if this repeats |

In sandbox, set the result yourself with [Mock Bank Verification](/api-reference-exchange/endpoint/bank/mock-bank-verification). It doesn't send a `BANK` webhook, so read the account afterwards.

### List, fetch and delete

| Task | Endpoint |
| - | - |
| List a customer's accounts (paginated) | [`GET /bank/list/{customer_id}`](/api-reference-exchange/endpoint/bank/list-\{customer_id}) |
| Fetch one account | [`GET /bank/{customer_id}/{bank_id}`](/api-reference-exchange/endpoint/bank/\{customer_id}-\{bank_id}) |
| Delete an account | [`DELETE /bank/{customer_id}/{bank_id}/delete`](/api-reference-exchange/endpoint/bank/delete) |

## Wallets

Save a customer's crypto address with [`POST /customer/wallet/add`](/api-reference-exchange/endpoint/wallet/add). If the address already exists for that customer, Zapyd updates it.

```json theme={null}
{
  "customer_id": "c2cf861b-342b-4318-a90e-85cd0312e82f",
  "wallet_address": "0x1234567890123456789012345678901234567890",
  "network": "Polygon",
  "label": "Main wallet"
}
```

* `network` is case-insensitive: `Ethereum`, `Polygon`, `Arbitrum`, `Bsc`, `Solana`, `Tron`, or `Sepolia` in sandbox.
* Zapyd checks the address format against the network: `0x…` for EVM networks, `T…` for Tron, base58 for Solana.
* Only wallets with a `label` appear in [List Wallets](/api-reference-exchange/endpoint/wallet/list), which works as the customer's address book.

To fetch or delete a wallet, use [`GET /customer/wallet/{customer_id}/{wallet_id}`](/api-reference-exchange/endpoint/wallet/\{customer_id}-\{wallet_id}) or [Delete Wallet](/api-reference-exchange/endpoint/wallet/delete).

<Info>
  Payin crypto goes to your organization's wallet for that asset and network, which is set up during onboarding. It doesn't go to a saved customer wallet. See [Payins](/guides/payments/payins#where-the-crypto-goes).
</Info>
