# Create and manage identities

Supported corridors
For supported payment corridors, see [Payout network](/products/payments-direct-2/introduction/payout-network).

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

- Create a new identity
- Retrieve an identity by ID (and by version)
- List identities with filters and pagination
- Update identity details
- Deactivate an identity


It builds on the model described in [Payment identities](/products/payments-direct-2/introduction/concepts/payment-identities) and focuses on the v3 identity schema.

internalId
A key behavior in v3 is that **at most one ACTIVE identity** in your environment can use a given `internalId`. Attempts to create or update an identity so that its `internalId` collides with another ACTIVE identity result in **409 Conflict**.

## Before you begin

To follow these examples, you need:

- Base URL for the Identity Management (for example, `https://{base-url}`)
- An access token with the following scopes (or equivalent):
  - `participants:create `for creating identities
  - `participants:read` for reading and listing identities
  - `participants:update` for updating and deactivating identities
- The v3 identity model from **Payment identities** (INDIVIDUAL vs BUSINESS and ORIGINATOR vs BENEFICIARY)


In all examples below, replace:

- `{base-url}` with your actual PII base URL
- `<access_token>` with a valid OAuth2 access token
- `{identity-id} `with a real `identityId` returned by the API


## Create an identity

Use `POST /v3/identities` to create a new identity.

At minimum, you must provide:

- `identityType` – INDIVIDUAL or BUSINESS
- `paymentRole` – ORIGINATOR or BENEFICIARY
- Either individual or business details, depending on `identityType`
- `internalId` for ORIGINATOR identities


For **BENEFICIARY** identities, `internalId` is optional, but recommended if you want to link beneficiaries to your own customer records.

Deduplication via internalId
Identity Management v3 enforces that at most one **ACTIVE** identity per organization can use a given `internalId`.

- If you try to create an identity and the provided `internalId` is already used by another **ACTIVE** identity, the request fails with **409 Conflict**.
- You can handle this error in your integration to surface “duplicate customer” conditions or guide users to reuse the existing identity.


Originator account number for USD payments to China
Some corridors require the originator's own account number. Send it in `originatorAccountNumber`, not in `internalId`. On an originator identity validated against `CN_CFXPS` (USD payments to China), omitting `originatorAccountNumber` fails with **400 Bad Request** (`USR_111`), and a randomly generated UUID or other arbitrary reference causes the payout to be rejected.

See [The originator's account number](/products/payments-direct-2/introduction/concepts/payment-identities#the-originators-account-number).

Identity documents in the Brazil (BR) jurisdiction
If your organization is configured for the Brazil (BR) jurisdiction, `identityDocuments` is required on every ORIGINATOR identity, on every corridor, including corridors that do not otherwise require it. Omitting it fails with **400 Bad Request** (`USR_111`).

This applies to updates as well as to creates. An update validates the body you send, not the record already stored, so an update that omits `identityDocuments` fails even when you are changing something unrelated.

See [Jurisdiction-based requirements](/products/payments-direct-2/introduction/concepts/payment-identities#jurisdiction-based-requirements).

Optionally, you can also:

- Set a friendly `nickName` and `tags`
- Add `validatePayoutRails` to validate the identity against specific payment rails (for example, `US_ACH`, `BR_PIX`, `MX_SPEI`)


**Example: Create an INDIVIDUAL ORIGINATOR**

```bash
curl -X POST "https://{base-url}/v3/identities" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "identityType": "INDIVIDUAL",
    "paymentRole": "ORIGINATOR",
    "internalId": "customer-12345-uuid",
    "nickName": "Alice Sender USD",
    "tags": ["sender", "priority"],
    "validatePayoutRails": ["US_ACH"],
    "individual": {
      "firstName": "Alice",
      "lastName": "Chen",
      "dateOfBirth": "1990-05-14",
      "citizenship": "US",
      "address": {
        "streetAddress": ["123 Main Street"],
        "city": "San Francisco",
        "stateOrProvince": "CA",
        "postalCode": "94105",
        "country": "US"
      }
    }
  }'
```

**Response (201 Created)**

```json
{
  "identityId": "99254c4f-f207-4792-a846-06928825018c",
  "version": "1"
}
```

Deduplication via internalId
If you send another `POST /v3/identities` with the same `internalId` while this identity is still ACTIVE, the service returns:

- **409 Conflict** – indicating that another **ACTIVE** identity already uses that `internalId`.


Handle 409 as a signal that you should look up and reuse the existing identity instead of creating a duplicate.

**Example: Create a BUSINESS BENEFICIARY**

```bash
curl -X POST "https://{base-url}/v3/identities" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "identityType": "BUSINESS",
    "paymentRole": "BENEFICIARY",
    "internalId": "counterparty-7890",
    "nickName": "Widgets Org MX",
    "tags": ["beneficiary", "mx"],
    "validatePayoutRails": ["MX_SPEI"],
    "business": {
      "businessName": "Widgets Org",
      "incorporationCountry": "US",
      "registration": [
        {
          "number": "123ABC",
          "type": "INCORPORATION_CERTIFICATE"
        }
      ],
      "email": "fake@example.com",
      "phone": "+1234567890",
      "address": {
        "streetAddress": ["123 Example MA"],
        "city": "Boston",
        "stateOrProvince": "MS",
        "postalCode": "12345",
        "country": "US"
      }
    }
  }'
```

**Response (201 Created)**

```json
{
  "identityId": "0116bacc-ffbf-4fa2-a29c-ecd9ea346806",
  "version": "1"
}
```

Missing or invalid PII
If any required PII is missing or invalid for the rails in `validatePayoutRails`, the API returns a 400 error describing which fields failed validation.

**Example: Validation failure for missing required fields**

If you try to create an identity with `validatePayoutRails: ["US_ACH"]` but omit required fields like `identityDocuments`, you'll receive:

**Response (400 Bad Request)**

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

To fix this, add the missing fields and retry the request.

**Example: Validation failure for an unsupported document type**

Some corridors accept only a subset of the `idType` (individual) or `registration.type` (business) enum, and the API rejects unsupported types with a 400 error. For example, `KR_KFTC` originators accept only `PASSPORT`, so submitting `NATIONAL_ID_NUMBER` fails:

**Response (400 Bad Request)**

```json
{
  "code": "VALIDATION_ERROR",
  "title": "Identity validation failed",
  "description": "The identity uses a document type that is not supported for the specified payment rails.",
  "details": [
    {
      "field": "individual.identityDocuments.idType",
      "message": "Document type NATIONAL_ID_NUMBER is not accepted for KR_KFTC originator identities. Accepted values: PASSPORT."
    }
  ]
}
```

For the accepted document types per corridor and role, see [Accepted document types by corridor](/products/payments-direct-2/introduction/concepts/payment-identities#accepted-document-types-by-corridor).

## Handling `internalId` conflicts (409 Conflict)

When you create or update an identity with an `internalId` that's already used by another **ACTIVE** identity, the API returns **409 Conflict**.

This is a **deduplication feature** that prevents you from accidentally creating multiple identities for the same customer.

### Example: Duplicate `internalId` on create

**Request:**

```bash
curl -X POST "https://{base-url}/v3/identities" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "identityType": "INDIVIDUAL",
    "paymentRole": "BENEFICIARY",
    "internalId": "customer-12345-uuid",
    "individual": {
      "firstName": "Bob",
      "lastName": "Smith",
      "address": {
        "streetAddress": ["456 Oak Ave"],
        "city": "Austin",
        "stateOrProvince": "TX",
        "postalCode": "78701",
        "country": "US"
      }
    }
  }'
```

**Response (409 Conflict)**

```json
{
  "code": "CONFLICT",
  "title": "Identity conflict",
  "description": "An identity with the provided internalId already exists.",
  "details": [
    {
      "field": "internalId",
      "message": "An ACTIVE identity with internalId 'customer-12345-uuid' already exists (identityId: 99254c4f-f207-4792-a846-06928825018c)"
    }
  ]
}
```

### How to handle 409 Conflict

When you receive a 409 Conflict:

1. **Look up the existing identity** using the `identityId` from the error message
2. **Reuse the existing identity** instead of creating a new one
3. **Update the existing identity** if needed (for example, to add new PII or change `validatePayoutRails`)
4. **Investigate your upstream systems** if you're unexpectedly getting conflicts (this may indicate duplicate customer records in your CRM or database)


**Example: Look up the existing identity**

```bash
curl -X GET "https://{base-url}/v3/identities/99254c4f-f207-4792-a846-06928825018c" \
  -H "Authorization: Bearer <access_token>"
```

If the existing identity is correct, use it for your payment or financial instrument creation. If it needs updates, use `PUT /v3/identities/{identity-id}` to modify it.

### Example: Duplicate `internalId` on update

**Request:**

```bash
curl -X PUT "https://{base-url}/v3/identities/1d927c62-45fa-4f42-b9f8-8210e1a111bb" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "internalId": "customer-99999"
  }'
```

If `customer-99999` is already used by a different ACTIVE identity:

**Response (409 Conflict)**

```json
{
  "code": "CONFLICT",
  "title": "Identity conflict",
  "description": "The provided internalId already exists on a different identity.",
  "details": [
    {
      "field": "internalId",
      "message": "An ACTIVE identity with internalId 'customer-99999' already exists (identityId: 7ea3399c-1234-5678-8d8f-d320ea406630)"
    }
  ]
}
```

### Best practices for `internalId` conflicts

- **Use stable upstream keys** – Use your CRM customer ID, core banking customer ID, or KYC master record ID as `internalId`
- **Keep it your own key** – You do not need to adapt `internalId` for any corridor. Corridors that need the originator's account number read `originatorAccountNumber` instead. See [The originator's account number](/products/payments-direct-2/introduction/concepts/payment-identities#the-originators-account-number)
- **Implement idempotent create logic** – When creating identities, catch 409 errors and look up the existing identity instead of failing
- **Avoid PII in `internalId`** – Don't use emails, phone numbers, or national IDs directly; use opaque identifiers
- **Monitor conflicts** – Track 409 errors in your logs to identify data quality issues in upstream systems


## Get an identity by ID (and version)

Use `GET /v3/identities/{identity-id}` to retrieve a specific identity.

- If you omit the version query parameter, the latest version is returned.
- To retrieve a specific version, include `?version={version-number}`.


**Example: Get the latest version**

```bash
curl -X GET "https://{base-url}/v3/identities/9d839f58-7fd3-4913-a27e-48d31973d3f9" \
  -H "Authorization: Bearer <access_token>"
```

**Response (200 OK)**

```json
{
  "identityId": "9d839f58-7fd3-4913-a27e-48d31973d3f9",
  "identityState": "ACTIVE",
  "nickName": "nickName",
  "tags": ["tag1"],
  "version": "2",
  "schemaVersion": "1.0.0",
  "createdAt": "2025-10-01T18:46:41.833Z",
  "updatedAt": "2025-10-01T18:46:47.430Z",
  "identityType": "BUSINESS",
  "paymentRole": "BENEFICIARY",
  "internalId": "counterparty-7890",
  "business": {
    "businessName": "Widgets Org",
    "address": {
      "streetAddress": ["123 Example MA"],
      "country": "US",
      "city": "Boston",
      "stateOrProvince": "MS",
      "postalCode": "12345"
    },
    "email": "fake@example.com",
    "phone": "+1234567890",
    "registration": [
      {
        "number": "123ABC",
        "type": "INCORPORATION_CERTIFICATE"
      }
    ],
    "incorporationCountry": "US"
  }
}
```

**Example: Get a specific version**

```bash
curl -X GET "https://{base-url}/v3/identities/9d839f58-7fd3-4913-a27e-48d31973d3f9?version=1" \
  -H "Authorization: Bearer <access_token>"
```

Versioned lookups
Use versioned lookups when you need to:

- Compare changes over time for audit
- Reconcile payments that used an earlier version of an identity


If the identity or version does not exist, the service returns **404 Not Found**.

## Get an identity by internal ID

Use `GET /v3/identities/by-internal-id/{internal-id}` to look up an identity directly by your client-provided `internalId`, without needing to know the server-generated `identityId` first.

This endpoint is especially useful for:

- Resolving the `identityId` of an existing customer identity when you only have your own reference (for example, a CRM customer ID).
- Implementing idempotent create-or-fetch workflows without storing `identityId` separately.
- Handling 409 Conflict responses by immediately looking up the conflicting identity.


**Example: Look up an identity by internal ID**

```bash
curl -X GET "https://{base-url}/v3/identities/by-internal-id/customer-12345-uuid" \
  -H "Authorization: Bearer <access_token>"
```

**Response (200 OK)**

```json
{
  "identityId": "99254c4f-f207-4792-a846-06928825018c",
  "identityState": "ACTIVE",
  "identityType": "INDIVIDUAL",
  "paymentRole": "ORIGINATOR",
  "internalId": "customer-12345-uuid",
  "nickName": "Alice Sender USD",
  "tags": ["sender", "priority"],
  "version": "1",
  "schemaVersion": "1.0.0",
  "createdAt": "2025-10-01T18:46:41.833Z",
  "updatedAt": "2025-10-01T18:46:47.430Z",
  "individual": {
    "firstName": "Alice",
    "lastName": "Chen",
    "dateOfBirth": "1990-05-14",
    "citizenship": "US",
    "address": {
      "streetAddress": ["123 Main Street"],
      "city": "San Francisco",
      "stateOrProvince": "CA",
      "postalCode": "94105",
      "country": "US"
    }
  }
}
```

Only ACTIVE identities
This endpoint returns the identity only if it is currently **ACTIVE**. If the identity exists but has been deactivated or blocked, the service returns **404 Not Found**.

The lookup is scoped to your organization. `internalId` values from other organizations do not conflict.

Error responses:

- **404 Not Found** – no ACTIVE identity with the specified `internalId` exists in your organization


## List identities

Use `GET /v3/identities` to retrieve identities for your organization with optional filters and pagination.

Supported query parameters:

- `payment-role` – filter by payment role (ORIGINATOR or BENEFICIARY)
- `nick-name` – filter by nickname (exact match)
- `limit` – maximum number of identities to return (1–100, default 10)
- `next-token` – opaque token returned from a previous response, used to fetch the next page


**Example: List identities by role**

```bash
curl -X GET "https://{base-url}/v3/identities?payment-role=BENEFICIARY&limit=2" \
  -H "Authorization: Bearer <access_token>"
```

**Response (200 OK)**

```json
{
  "data": [
    {
      "identityId": "0116bacc-ffbf-4fa2-a29c-ecd9ea346806",
      "identityType": "BUSINESS",
      "paymentRole": "BENEFICIARY",
      "createdAt": "2025-10-01T16:14:13.200Z",
      "updatedAt": "2025-10-01T16:14:15.763Z",
      "identityState": "ACTIVE",
      "nickName": "testNickName",
      "tags": ["tag1"],
      "version": 2,
      "schemaVersion": "1.0.0",
      "internalId": "counterparty-7890"
    },
    {
      "identityId": "1d927c62-45fa-4f42-b9f8-8210e1a111bb",
      "identityType": "INDIVIDUAL",
      "paymentRole": "ORIGINATOR",
      "createdAt": "2025-10-01T17:20:05.111Z",
      "updatedAt": "2025-10-01T17:20:06.500Z",
      "identityState": "ACTIVE",
      "nickName": "Alice Originator",
      "tags": ["sender", "priority"],
      "version": 1,
      "schemaVersion": "1.0.0",
      "internalId": "customer-12345-uuid"
    }
  ],
  "nextToken": "eyJrZXkxIjoidmFsdWUifQ=="
}
```

If you receive a `nextToken`, pass it back as `next-token` to fetch the next page:

```bash
curl -X GET "https://{base-url}/v3/identities?payment-role=BENEFICIARY&limit=2&next-token=eyJrZXkxIjoidmFsdWUifQ==" \
  -H "Authorization: Bearer <access_token>"
```

No identities found
If no identities match your filters, the service returns **404 Not Found**.

## Update an identity

Use `PUT /v3/identities/{identity-id}` to update an existing identity.

Each successful update creates a **new identity version**. The `identityId` never changes, and previous versions remain available for retrieval and audit. The request body supports **partial updates**. Only include the fields you want to change.

You can update:

- Metadata: `nickName`, `tags`
- PII: any field within `individual` or `business`
- `internalId` (must remain unique across all ACTIVE identities)
- `validatePayoutRails` (re-validates PII against the specified rails)


`identityType` and `paymentRole` cannot be changed after creation.

For complete examples, error handling, and guidance on updating BLOCKED identities, see [Update an identity](/products/payments-direct-2/api-docs/developer-guides/update-an-identity).

## Deactivate an identity

Use `DELETE /v3/identities/{identity-id}` to deactivate an identity.

Deactivation:

- Sets the identity’s state to **DEACTIVATED**
- Deactivates the identity’s associated **financial instruments**
- Prevents any **new payments** from using this identity
- Keeps historical versions available for audit


Permanent
Deactivation is **permanent**; you cannot reactivate a deactivated identity.

**Example: Deactivate an identity**

```bash
curl -X DELETE "https://{base-url}/v3/identities/146f3c51-c313-47ce-b6f2-691c5a238b3e" \
  -H "Authorization: Bearer <access_token>"
```

**Response (204 No Content) – the identity is successfully deactivated.**

Error responses:

- **400 Bad Request** – malformed request or invalid `identityId` format
- **404 Not Found** – identity does not exist
- **422 Unprocessable Entity** – identity is already deactivated or cannot be deactivated (for example, due to internal constraints)


## Error handling and validation

All identity endpoints use a standard error response schema with:

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


Common cases:

- **400** – missing or invalid fields in the request, or identity failing corridor-specific validation rules
- **404** – identity not found (for a specific ID, version, or query)
- **409** – `internalId` conflict (another ACTIVE identity already uses the same `internalId`)
- **422** – invalid lifecycle transition (for example, attempting to deactivate an already deactivated identity)
- **500** – internal processing error; retry or contact support if persistent


Refer to the general [Error handling](/products/payments-direct-2/api-docs/error-handling/api-errors) section of the API docs for the full list of error codes.

## Next steps

- For a deeper explanation of the identity model, see [Payment identities](/products/payments-direct-2/introduction/concepts/payment-identities).
- For full update examples and guidance on immutable versioning, payment rail validation, and BLOCKED identity reactivation, see [Update an identity](/products/payments-direct-2/api-docs/developer-guides/update-an-identity).
- To learn how to attach payout account details to identities (bank accounts, wallets, local payment rails), continue with [Create and manage financial instruments](/products/payments-direct-2/api-docs/developer-guides/create-and-manage-financial-instruments).