# Create and manage financial instruments

Identity Management v3
This topic describes the v3 identity model. **Identity Management v3** is recommended for all new integrations.

The supported payment corridors are:

- US • USD
- EU • EUR
- MX • MXN
- BR • BRL
- CO • COP
- CA • CAD
- GB • GBP
- NG • NGN


This tutorial shows how to use the **Identity Management v3** financial instrument endpoints to:

- Create a financial instrument for an identity
- List an identity’s financial instruments
- Retrieve a financial instrument by ID
- Update financial instrument details
- Deactivate a financial instrument


It assumes you’ve read [Financial instruments](/products/payments-direct-2/v2026.03/api-docs/concepts/financial-instruments) for payments and [Payment identities](/products/payments-direct-2/v2026.03/api-docs/concepts/payment-identities).

Multiple instruments per identity
In Identity Management v3, an identity can have **one or more financial instruments**, allowing a single party (identity) to hold multiple bank accounts or payout rails.

## Before you begin

To follow these examples, you need:

- Base URL for the Ripple Payments Direct 2.0 API (for example,` https://{base-url}`)
- A valid OAuth2 access token with scopes:
  - `participants:create` – create financial instruments
  - `participants:read` – list and get financial instruments
  - `participants:update` – update and deactivate financial instruments
- An existing `identityId` (created via the identities endpoints)


In all examples, replace:

- `{base-url}` with your PII base URL
- `<access_token>` with a valid access token
- `{identity-id} `and `{financial-instrument-id}` with real IDs from your environment


## Create a financial instrument

Use `POST /v3/identities/{identity-id}/financial-instruments` to create a financial instrument for an existing identity.

You must provide:

- `financialInstrumentType` – the rail, such as `US_ACH`, `MX_SPEI`,` BR_PIX`
- `currency` – ISO 4217 currency code (for example, `USD`, `MXN`, `BRL`)
- Exactly one rail-specific object (for example, `usAch`, `mxSpei`, `brPix`) with the required account details
- Optional metadata such as `label`


### Example: Create a US ACH bank account


```bash
curl -X POST "https://{base-url}/v3/identities/{identity-id}/financial-instruments" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "financialInstrumentType": "US_ACH",
    "currency": "USD",
    "label": "US bank account",
    "usAch": {
      "bankName": "Bank of Example",
      "bankRoutingNumber": "266231608",
      "accountNumber": "60480",
      "accountType": "CHECKING"
    }
  }'
```

### Response (201 Created)


```json
{
  "financialInstrumentId": "2f4ac57f-c5ba-4051-b51f-b3565778717b"
}
```

You’ll use this `financialInstrumentId` when:

- Getting the instrument by ID
- Updating or deactivating the instrument
- Referencing it in payment creation flows


### Example: Create an MX SPEI bank account for the same identity


```bash
curl -X POST "https://{base-url}/v3/identities/{identity-id}/financial-instruments" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "financialInstrumentType": "MX_SPEI",
    "currency": "MXN",
    "label": "MXN SPEI account",
    "mxSpei": {
      "bankName": "Banco Ejemplo",
      "clabe": "032180000118359719"
    }
  }'
```

Now the same identity holds **two financial instruments** (one US ACH, one MX SPEI).

### Example: Create a Nigeria bank payout financial instrument


```bash
curl -X POST "https://{base-url}/v3/identities/{identity-id}/financial-instruments" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "financialInstrumentType": "NG_BANK_PAYOUT",
    "currency": "NGN",
    "label": "NGN bank account",
    "ngBankPayout": {
      "bankName": "Guaranty Trust Bank PLC",
      "bankCode": "RPL:NG:GTBINGLA:BNK",
      "accountNumber": "0123456789",
      "country": "NG"
    }
  }'
```

Ripple Bank Codes
The `bankCode` field requires a Ripple Bank Code (RBC) — not the bank's own internal code. Use the [Ripple Bank Codes lookup](/products/payments-direct-2/v2026.03/api-docs/integration-resources/ripple-bank-codes) to find the correct RBC for the destination bank before creating the instrument.

Missing or invalid fields
If required fields are missing or invalid (for example, CLABE length or routing code format), the API returns **400 Bad Request** with details about the failing fields.

If the `identity-id` does not exist, the API returns **404 Not Found**.

### Identity validation and `validatePayoutRails`

When you create a financial instrument, the associated identity must have all required PII for the specified `financialInstrumentType`.

**If the identity was created with `validatePayoutRails`:**

- The identity's PII was already validated against the specified payout rails at identity creation time.
- Creating a financial instrument for a rail listed in `validatePayoutRails` succeeds immediately (assuming the identity has the required PII).
- Creating a financial instrument for a rail **not** listed in `validatePayoutRails` triggers validation at instrument creation time. If required PII is missing, the request fails with **400 Bad Request**.


**If the identity was created without `validatePayoutRails`:**

- The identity's PII is validated when you create the financial instrument.
- If required fields are missing for the specified `financialInstrumentType`, the request fails with **400 Bad Request** and details about which fields are missing.


**Example: Validation failure due to missing PII**

If you try to create a `US_ACH` financial instrument for an identity that's missing required fields like `dateOfBirth` or `identityDocuments`, you'll receive:


```json
{
  "code": "VALIDATION_ERROR",
  "title": "Identity validation failed",
  "description": "The identity is missing required fields for the specified financial instrument type.",
  "details": [
    {
      "field": "individual.dateOfBirth",
      "message": "Date of birth is required for US_ACH beneficiary identities"
    },
    {
      "field": "individual.identityDocuments",
      "message": "At least one identity document (SSN or Tax ID) is required for US_ACH beneficiary identities"
    }
  ]
}
```

To fix this, update the identity with the missing PII fields using `PUT /v3/identities/{identity-id}`, then retry creating the financial instrument.

Upfront validation
Using `validatePayoutRails` when creating identities helps catch PII issues early, before you attempt to create financial instruments. For details, see [Validating identities for specific payout rails](/products/payments-direct-2/v2026.03/api-docs/concepts/payment-identities#validating-identities-for-specific-payout-rails).

## List financial instruments for an identity

Use `GET /v3/identities/{identity-id}/financial-instruments` to list financial instruments for an identity.

Query parameters:

- `version` – optional identity version. If omitted, the latest identity version is used.
- `limit` – maximum number of instruments to return (1–100, default 10).
- `next-token` – pagination token from a previous response.


### Example: List instruments for an identity


```bash
curl -X GET "https://{base-url}/v3/identities/{identity-id}/financial-instruments?limit=10" \
  -H "Authorization: Bearer <access_token>"
```

### Response (200 OK)


```json
{
  "data": [
    {
      "financialInstrumentId": "2f4ac57f-c5ba-4051-b51f-b3565778717b",
      "financialInstrumentType": "US_ACH",
      "currency": "USD",
      "label": "US bank account",
      "country": "US",
      "createdAt": "2025-10-01T18:46:47.430Z",
      "updatedAt": "2025-10-01T18:46:47.430Z"
    },
    {
      "financialInstrumentId": "9e267ec3-0f75-4e8b-93b2-bf7e2f2a9e0d",
      "financialInstrumentType": "MX_SPEI",
      "currency": "MXN",
      "label": "MXN SPEI account",
      "country": "MX",
      "createdAt": "2025-10-01T18:50:12.100Z",
      "updatedAt": "2025-10-01T18:50:12.100Z"
    }
  ],
  "nextToken": "eyJrZXkxIjoidmFsdWVfMSIsImtleTIiOiJ2YWx1ZTIifQ=="
}
```

- The data array can contain **multiple** instruments for the same identity.
- Only metadata is returned; rail-specific objects (such as `usAch` or `mxSpei`) are not included.
- If `nextToken` is present, pass it as `next-token` to fetch the next page:



```bash
curl -X GET "https://{base-url}/v3/identities/{identity-id}/financial-instruments?limit=10&next-token=eyJrZXkxIjoidmFsdWVfMSIsImtleTIiOiJ2YWx1ZTIifQ==" \
  -H "Authorization: Bearer <access_token>"
```

If the identity or instruments are not found, the API returns **404 Not Found**.

## Get a financial instrument by ID

Use `GET /v3/identities/{identity-id}/financial-instruments/{financial-instrument-id}` to retrieve full details for a specific financial instrument, including the rail-specific object.

###Example: Get a US ACH financial instrument


```bash
curl -X GET "https://{base-url}/v3/identities/{identity-id}/financial-instruments/{financial-instrument-id}" \
  -H "Authorization: Bearer <access_token>"
```

### Response (200 OK)


```json
{
  "financialInstrument": {
    "country": "US",
    "financialInstrumentId": "7f2bac05-42a3-4b26-89fd-333396fdba70",
    "createdAt": "2025-10-01T18:46:47.430Z",
    "updatedAt": "2025-10-01T18:46:47.430Z",
    "usAch": {
      "bankName": "Bank of Example",
      "bankRoutingNumber": "266231608",
      "accountNumber": "60480",
      "accountType": "CHECKING"
    },
    "currency": "USD",
    "label": "US bank account",
    "financialInstrumentType": "US_ACH"
  }
}
```

Errors
If either the `identity-id` or `financial-instrument-id` is invalid or does not exist, the API returns **400** or **404** respectively.

## Update a financial instrument

Use `PUT /v3/identities/{identity-id}/financial-instruments/{financial-instrument-id}` to update editable fields of an existing financial instrument.

**You can:**

- Update metadata such as label.
- Update rail-specific details, such as account number or PIX key.


**You cannot change:**

- `financialInstrumentType`
- `currency`


### Partial updates

The request supports partial updates:

- Fields you include are **overwritten**.
- Fields you omit remain **unchanged**.


### Example: Update only the label


```bash
curl -X PUT "https://{base-url}/v3/identities/{identity-id}/financial-instruments/{financial-instrument-id}" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "US bank account (primary)"
  }'
```

### Response (200 OK)


```json
{
  "financialInstrumentId": "7f2bac05-42a3-4b26-89fd-333396fdba70",
  "financialInstrumentType": "US_ACH",
  "currency": "USD",
  "label": "US bank account (primary)",
  "country": "US",
  "createdAt": "2025-10-01T18:46:47.430Z",
  "updatedAt": "2025-10-02T09:15:10.000Z"
}
```

Response
The update response returns the **instrument entry** (metadata + timestamps) but does **not** include the rail-specific object (such as `usAch`).

### Example: Update rail-specific details (US ACH)


```bash
curl -X PUT "https://{base-url}/v3/identities/{identity-id}/financial-instruments/{financial-instrument-id}" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "usAch": {
      "bankName": "Bank of Example",
      "bankRoutingNumber": "266231608",
      "accountNumber": "987654321",
      "accountType": "CHECKING"
    }'
```

Errors
If the update payload is invalid (for example, unsupported account type or invalid routing format), the API returns **400 Bad Request** with error details.

If the identity or instrument is not found, the API returns **404 Not Found**.

If there is a conflicting resource state (for example, concurrent update or other internal constraint), the API may return **409 Conflict**.

## Deactivate a financial instrument

Use `DELETE /v3/identities/{identity-id}/financial-instruments/{financial-instrument-id}` to deactivate a financial instrument.

**Deactivation:**

- Is **permanent** for that instrument.
- Prevents the instrument from being used for **new payments**.
- Keeps historical usage and data for audit and reconciliation.


Identity deactivation
Deactivating an identity (via the identities endpoints) also deactivates its financial instrument.

### Example: Deactivate a financial instrument


```bash
curl -X DELETE "https://{base-url}/v3/identities/{identity-id}/financial-instruments/{financial-instrument-id}" \
  -H "Authorization: Bearer <access_token>"
```

### Response (204 No Content)

The following errors may occur:

- **400 Bad Request** – malformed identity or instrument ID.
- **404 Not Found** – the identity or instrument does not exist.
- **409 Conflict** – the instrument is already deactivated or cannot be deactivated due to its current state.
- **500 Internal Server Error** – unexpected server-side error.


## Error handling

All financial instrument endpoints use the standard error response schema:

- **code** – machine-readable error code
- **title** – short description
- **description** – detailed explanation and remediation hints


Common scenarios:

- **400** – structural validation errors (missing required fields, pattern violations, invalid enum values).
- **404** – identity or financial instrument not found.
- **409** – conflicting state (for example, update or deactivate not allowed).
- **500** – internal processing error; if persistent, contact Ripple support.


Refer to the [Error handling reference](/products/payments-direct-2/v2026.03/api-docs/error-codes/api-errors) in the API docs for the complete list of error codes.

## Next steps

- To understand how instruments fit into the overall payout setup, see [Financial instruments](/products/payments-direct-2/v2026.03/api-docs/concepts/financial-instruments) for payments.
- To review the KYC / PII model that instruments attach to, see [Payment identities](/products/payments-direct-2/v2026.03/api-docs/concepts/payment-identities).
- To see how identities and financial instruments are used together when sending money, continue with the [Create a payment](/products/payments-direct-2/v2026.03/api-docs/tutorials/create-a-payment) tutorial.