# Identity matching (v2)

The `POST /v2/identities` [operation](/products/payments-direct-2/v2025.11/api-docs/payments-direct-api/payments-direct-2-api/identitiesv2/createidentityv2) does not always create a new identity. Before it creates one, it looks for an existing identity in your account that carries the same party ID. If it finds one, it writes a **new version of that existing identity** instead of creating a second one, built from your request body in full. `piiData` is replaced rather than merged, so the new version carries the bank account details in your request and not the ones the previous version held. `nickName` is also taken from the request rather than carried forward.

Both outcomes return `201`. The difference is visible only in the response body.

A match overwrites bank account details
If you submit two identities that carry the same party ID, the second submission replaces the bank account details of the first. A payment records the identity version that was current when you created it, so payments created after the second submission carry the second account.

## How the match key is built

The operation builds a match key from three things:

| Component | Value |
|  --- | --- |
| Your account | The tenant your access token belongs to. Matching never crosses accounts. |
| `identityType` | `BENEFICIARY`, `ORIGINATOR`, or `SENDER`. An identity only matches another of the same type. |
| A party ID from `piiData` | Which field supplies it depends on `useCaseType`. |


| `useCaseType` | Field read from `piiData` |
|  --- | --- |
| `BUSINESS` | `OrgId.Othr.Id` |
| `INDIVIDUAL` | `PrvtId.Othr.Id` |


The field is read wherever it appears in `piiData`. For a beneficiary identity that is `Cdtr.Id.OrgId.Othr.Id` or `Cdtr.Id.PrvtId.Othr.Id`. For an originator identity it is `Dbtr.Id.OrgId.Othr.Id` or `Dbtr.Id.PrvtId.Othr.Id`.

The document type is not part of the key
Only the ID number is read. The accompanying document type field (`Othr.SchmeNm.Cd`) is not part of the match key. Two identities that carry the same number under different document types still match each other.

### Values are normalized before they are compared

The ID value is normalized before it is compared, so values that look different can still match. Normalization removes ASCII whitespace and ASCII punctuation, then converts the result to uppercase.

| Submitted value | Normalizes to |
|  --- | --- |
| `201708102R` | `201708102R` |
| `2017-0810 2r` | `201708102R` |
| `2017/0810.2R` | `201708102R` |
| `X-1` | `X1` |
| `abc_123` | `ABC123` |


Characters outside ASCII are left alone. An en dash or a non-breaking space survives normalization, so `ABC–123` (en dash) and `ABC123` do **not** match.

### When `piiData` carries no party ID

If `piiData` contains no value at the path that `useCaseType` selects, no match key is recorded and the operation always creates a new identity.

Whether the field is present at all depends on the data requirements for the corridor, currency, use case, and payout method you are sending on. This is why repeated submissions behave differently from one corridor to the next. Use the [Payment Data Requirements](/products/payments-direct-2/v2025.11/api-docs/integration-resources/payment-data-requirements-form) utility to check your own corridor.

## Reading the response

```json
{
  "identityId": "439bfec9-069a-4645-8699-c35316668523",
  "version": 2
}
```

| Field | What it tells you |
|  --- | --- |
| `identityId` | On a match this is the **existing** identity's ID, not a new one. |
| `version` | `1` means a new identity was created. Any value greater than `1` means an existing identity was matched and a new version was written. |


Check `version` on every create. Treat a value greater than `1` as a signal that you sent data for an identity your account already holds, and confirm that replacing it was what you intended before you use the `identityId` in a payment.

To see what the previous version held, call the `GET /v2/identities/{identity-id}` [operation](/products/payments-direct-2/v2025.11/api-docs/payments-direct-api/payments-direct-2-api/identitiesv2/getidentitybyidv2) with the `version` query parameter. Earlier versions remain readable for audit.

## Holding two bank accounts for one payee

A single v2 identity holds one set of bank account details, and the match key is the payee's ID rather than the account. There is no request field that turns matching off.

To create a second identity for the same payee, supply a **different ID document that the payee genuinely holds**, along with its matching document type. A business might be identified by its certificate of incorporation number on one identity and its tax identification number on the other. Both values are real, both are truthful in the compliance field, and because the numbers differ, the two identities do not match each other.

Three things constrain this approach:

1. **The value must be a real document the payee holds.** These are compliance fields. Do not vary the value to force a new identity.
2. **The number must differ.** Changing only the document type changes nothing, because the type is not part of the match key.
3. **The corridor caps how many identities you can create this way.** You can only use document types the corridor accepts for that party and use case. Where a corridor accepts one type, this approach is not available.


As an illustration, the India INR NEFT corridor accepts these document types:

| Party and use case | ID document types accepted |
|  --- | --- |
| Business beneficiary (B2B, B2B2B, C2B2B) | `CINC`, `TXID` |
| Individual beneficiary (B2C, B2B2C, C2B2C) | `ARNU`, `CUST`, `DRLC`, `EMPL`, `NIDN`, `CCPT`, `SOS`, `TXID` |
| Business originator (B2B, B2B2B, B2B2C, B2C) | `CINC`, `TXID` |
| Individual originator (C2B2B, C2B2C) | `CCPT` |


The number of accepted types is the ceiling, not a guarantee. You reach it only where the payee holds that many documents and their numbers differ once normalized. A business beneficiary on this corridor tops out at two identities, and an individual originator has no second type to fall back on.

Check the accepted document types for your own corridor before you rely on this. Other corridors accept different sets.

Two further options:

* **Keep one account active at a time.** Call the `DELETE /v2/identities/{identity-id}` [operation](/products/payments-direct-2/v2025.11/api-docs/payments-direct-api/payments-direct-2-api/identitiesv2/deactivateidentityv2) to deactivate the existing identity, which releases its match key, then create the identity for the second account. Deactivation is permanent and the deactivated identity cannot be used in later payments.
* **Move to Identity Management v3.** v3 does not match on `OrgId.Othr.Id` or `PrvtId.Othr.Id`. It keys identities on an `internalId` that you supply, and rejects a create whose `internalId` already belongs to an active identity with a `409`, so two accounts for one party are created under distinct `internalId` values. Contact your Ripple representative about migrating.


## Two payees that share an ID number

The match key is the ID number alone. Validation on create checks the fields in your `piiData` against the corridor's data requirements. It does not check that an ID number belongs to only one payee. If two unrelated payees are submitted with the same ID number, under the same `identityType` in the same account, they match each other and the second replaces the first's bank account details, exactly as two accounts for one payee would.

This is unlikely but possible, for example through a data entry error, a placeholder value, or a number reused across related entities.

Check for an existing identity before you create one
Track the party IDs you have already submitted and check for a match on your side before calling `POST /v2/identities`. The `GET /v2/identities` [operation](/products/payments-direct-2/v2025.11/api-docs/payments-direct-api/payments-direct-2-api/identitiesv2/getidentitiesv2) filters only on `identityType` and `nickName`, so there is no way to ask the API whether an identity already holds a given party ID. Checking on your side is what catches a collision before it replaces an existing identity's bank account details, rather than after.