# EUR recipient verification

When you create an EUR recipient via the Wise API, we automatically run a series of [validations (format checks) and verifications (Confirmation of Payee, or CoP)](/guides/product/send-money/recipient-verification) before returning the response.

This guide provides details specific to the verification that occurs when creating EUR recipients.

Availability
EUR recipient verification is currently only available for Embedded finance and Enterprise partners when making payments to a SEPA account.

Support for Correspondent partners is coming later in 2026.

## Supported recipient types

EUR supports one recipient type only:

| Recipient type | API value  | Description |
|  --- | --- | --- |
| IBAN (SEPA-reachable) | `iban` | IBAN identifying a EUR payment account reachable via SEPA SCT/SCT Inst. |


### Verification provider

Verification is performed by EU Verification of Payee (VoP) via receiving PSP (through EPC Directory/RVM). Note the following about this method:

- In the EU, VoP must be performed for EUR credit transfers within SEPA. Unlike some markets, the VoP check can apply beyond first-time recipient creation. This guide focuses on the API behavior at recipient creation.
- VoP is not a one-time check for EUR. Under EU Regulation 2024/886, verification must occur before the authorisation of every payment, not only at recipient creation. If a recipient was created with a non-SUCCESS outcome and later used for a new transfer, the check runs again and requires fresh acceptance. This means the confirmation flow described in this guide applies both at recipient creation AND on subsequent transfers.


## Step 1: Field-level validation

Field-level validation checks the format and correctness of input fields. If any fail, the recipient is not created and the API returns a validation error.

### IBAN

The following validations occur when the recipient type is `iban`:

| Field | Rules | Error if invalid  |
|  --- | --- | --- |
| IBAN | 4–34 alphanumeric. Must pass IBAN checksum and country-specific structure. | `NOT_VALID` |
| Name | At least first name + last name (two words separated by a space). Max length 140 characters. | `NOT_VALID` |


### Sample field validation errors

Invalid name (single word)
```JSON
{
 "errors": [
   {
     "code": "NOT_VALID",
     "path": "name/fullName",
     "message": "The name provided is not valid."
   }
 ]
}
```

Invalid IBAN
```JSON
{
 "errors": [
   {
     "code": "NOT_VALID",
     "path": "details/iban",
     "message": "The IBAN provided is invalid."
   }
 ]
}
```

## Step 2: Verification

The VoP check verifies both account existence (IBAN is reachable at the receiving PSP) and name matching, returning one of four outcomes. Account existence and name matching are handled by the receiving PSP response routed via the EU VoP scheme.

This step runs after all field validations pass and is non-blocking, so the recipient resource is always created.

### How it works

1. Wise calls the receiving institution’s VoP endpoint with the recipient’s IBAN and name.
2. The institution confirms IBAN is reachable and returns a name match assessment.
3. Wise maps the provider’s result to API outcomes and includes it in the `confirmations` object.


**Possible outcomes**:

| What it means | Outcome  | Requires acceptance? |
|  --- | --- | --- |
| Names match | `SUCCESS` | No. Proceed with transfer. |
| Close/similar match | `PARTIAL_FAILURE` | Yes. Show result to user and call `PATCH` to accept. |
| Names do not match | `FAILURE` | Yes. Show result to user and call `PATCH` to accept. |
| Could not check (timeout/error/not reachable) | `COULD_NOT_CHECK` | Yes. Inform user and call `PATCH` if proceeding. |


### Fields checked by recipient type

| Recipient type | Verification provider | Fields checked (V1) | Fields checked (V2) |
|  --- | --- | --- | --- |
| IBAN (SEPA) | Receiving PSP via EU VoP | `accountHolderName`, `details.iban` | `name.fullName`, `details.iban` |


Individual vs Business name matching
- `PRIVATE` entities have stricter word-by-word comparison.
- `BUSINESS` entities have more lenient standards. Common legal abbreviations are recognized (for example, “GmbH”, “S.A.”).


### Account–quote compatibility check

In addition to VoP at recipient creation, a [compatibility endpoint](/api-reference/recipient/recipientcompatibilitycheck) is available to check whether a previously created recipient account is compatible with a given quote for the purposes of VoP.

This is useful when:

- A recipient was created earlier (possibly with a non-SUCCESS outcome) and is being reused for a new transfer.
- You want to verify VoP status before initiating the transfer flow.


This endpoint allows you to pre-check whether the account+quote combination will require a fresh VoP cycle or if an existing acceptance is still valid.

These return the same error format as field validation errors (see above sample field validation errors section).

## Possible outcomes for EUR

| Outcome | Blocking? | When it happens |
|  --- | --- | --- |
| `SUCCESS` | No | Name matches |
| `PARTIAL_FAILURE` | No | Name partially matches |
| `FAILURE` | No | Names do not match |
| `COULD_NOT_CHECK` | No | Receiving PSP reports IBAN not found/reachable |


### What to do

The action you take depends on your regulatory scope.

- EEA partners are those within the European Economic Area subject to EU Regulation 2024/886.
- Non-EEA partners are not subject to VoP risk acceptance requirements, and the `requiresCustomerAcceptance` field will return `false` for all non-SUCCESS outcomes.


| Outcome  | Recipient created? | EEA partners | Non-EEA partners |
|  --- | --- | --- | --- |
| `SUCCESS` | Yes | Proceed with the transfer. No additional action needed. | Proceed with the transfer. No additional action needed. |
| `PARTIAL_FAILURE` | Yes | Show the verification result to the end user. If they accept, call `PATCH /v1/accounts/{id}/confirmations` (or V2 equivalent).

The PATCH is mandatory. Transfers will be blocked until acceptance is recorded. | `requiresCustomerAcceptance` will be `false`. No `PATCH` call is needed. The recipient can be used for transfers without the confirmation gate. |
| `FAILURE` | Yes | Show warning to the end user. If they choose to proceed anyway, call `PATCH` to accept the risk. The `PATCH` is mandatory. Transfers will be blocked until acceptance is recorded. | `requiresCustomerAcceptance` will be `false`. No `PATCH` call is needed. The recipient can be used for transfers without the confirmation gate. |
| `COULD_NOT_CHECK` | Yes | Inform the user that verification wasn't possible. If they accept, make `PATCH` request to proceed. The `PATCH` is mandatory. Transfers will be blocked until acceptance is recorded. | `requiresCustomerAcceptance` will be `false`. No `PATCH` call is needed. The recipient can be used for transfers without the confirmation gate. |


## V1 vs V2 endpoints

Both V1 and V2 endpoints run the same validations and verifications. The difference is the field naming and how the confirmation result is returned.

| Aspect | `/v1/accounts/` | `/v2/accounts/` |
|  --- | --- | --- |
| Confirmations in response | Always included in the recipient response (if available) | Only included if you request them via `responseConfiguration.additionalInformation` |
| Name field path in confirmations | `accountHolderName` | `name/fullName` |
| Recommended update path | `accountHolderName` | `name/fullName` |
| Accept confirmation | `PATCH /v1/accounts/{id}/confirmations` | `PATCH /v2/accounts/{id}/confirmations` |


### V2 request confirmations

When creating a recipient with V2, include the following in your request to receive the `confirmations` field:

```json
{
  "type": "iban",
  "details": { "iban": "IBANvalue1234" },
  "responseConfiguration": {
    "additionalInformation": ["CONFIRMATION_RESULT"]
  }
}
```

Without this, V2 will create the recipient and run all checks, but **will not include** the `confirmations` field in the response. You would then need to fetch it separately via `GET /v2/accounts/{id}`.

## Sample payloads

All payloads below show the `confirmations` field as it appears in the response after creating a recipient.

In production, the `resolvedName` and `providedName` fields are `null`. Null fields may be omitted entirely from the actual API response. They are shown here as `null` for documentation completeness.

### SUCCESS (name matches)

V1 response
```JSON
{
  "id": 123456,
  "type": "iban",
  "accountHolderName": "Maria Rossi",
  "details": {
    "iban": "IT49S0200802004000103890690"
  },
  "confirmations": {
    "acceptedOutcomes": false,
    "acceptedAt": null,
    "quoteId": null,
    "outcomes": [
      {
        "type": "NAME_MATCHING",
        "outcome": "SUCCESS",
        "timestamp": "2026-03-07T10:30:00Z",
        "requiresCustomerAcceptance": false,
        "fieldsChecked": ["accountHolderName", "details/iban"],
        "message": null,
        "resolvedName": "Maria Rossi",
        "providedName": null,
        "recommendedUpdates": []
      }
    ]
  }
}
```

V2 response
```JSON
{
  "id": 123456,
  "type": "iban",
  "name": { "fullName": "Maria Rossi" },
  "details": {
    "iban": "IT49S0200802004000103890690"
  },
  "confirmations": {
    "acceptedOutcomes": false,
    "acceptedAt": null,
    "quoteId": null,
    "outcomes": [
      {
        "type": "NAME_MATCHING",
        "outcome": "SUCCESS",
        "timestamp": "2026-03-07T10:30:00Z",
        "requiresCustomerAcceptance": false,
        "fieldsChecked": ["name/fullName", "details/iban"],
        "message": null,
        "resolvedName": "Maria Rossi",
        "providedName": null,
        "recommendedUpdates": []
      }
    ]
  }
}
```

### PARTIAL_FAILURE (close name match)

V1 response
```JSON
{
  "id": 123456,
  "type": "iban",
  "accountHolderName": "Maria Ros",
  "details": {
    "iban": "DE44500105175407324931"
  },
  "confirmations": {
    "acceptedOutcomes": false,
    "acceptedAt": null,
    "quoteId": null,
    "outcomes": [
      {
        "type": "NAME_MATCHING",
        "outcome": "PARTIAL_FAILURE",
        "timestamp": "2026-03-07T10:30:00Z",
        "requiresCustomerAcceptance": true,
        "fieldsChecked": ["accountHolderName", "details/iban"],
        "message": "The name you entered doesn't exactly match the account holder's name.",
        "resolvedName": "Maria Rossi",
        "providedName": null,
        "recommendedUpdates": []
      }
    ]
  }
}
```

V2 response
```JSON
{
  "id": 123456,
  "type": "iban",
  "name": { "fullName": "Maria Ross" },
  "details": {
    "iban": "DE44500105175407324931"
  },
  "confirmations": {
    "acceptedOutcomes": false,
    "acceptedAt": null,
    "quoteId": null,
    "outcomes": [
      {
        "type": "NAME_MATCHING",
        "outcome": "PARTIAL_FAILURE",
        "timestamp": "2026-03-07T10:30:00Z",
        "requiresCustomerAcceptance": true,
        "fieldsChecked": ["name/fullName", "details/iban"],
        "message": "The name you entered doesn't exactly match the account holder's name.",
        "resolvedName": "Maria Rossi",
        "providedName": null,
        "recommendedUpdates": []
      }
    ]
  }
}
```

### FAILURE — Name doesn't match (NO_MATCH_NAME)

V1 response
```JSON
{
  "id": 123456,
  "type": "iban",
  "accountHolderName": "Steve Smith",
  "details": {
    "iban": "ES9121000418450200051332"
  },
  "confirmations": {
    "acceptedOutcomes": false,
    "acceptedAt": null,
    "quoteId": null,
    "outcomes": [
      {
        "type": "NAME_MATCHING",
        "outcome": "FAILURE",
        "timestamp": "2026-03-07T10:30:00Z",
        "requiresCustomerAcceptance": true,
        "fieldsChecked": ["accountHolderName", "details/iban"],
        "message": "The name you entered doesn't match the account holder's name. Please check and try again.",
        "resolvedName": "Maria Rossi",
        "providedName": null,
        "recommendedUpdates": []
      }
    ]
  }
}
```

V2 response
```JSON
{
  "id": 123456,
  "type": "iban",
  "name": { "fullName": "Steve Smith" },
  "details": {
    "iban": "ES9121000418450200051332"
  },
  "confirmations": {
    "acceptedOutcomes": false,
    "acceptedAt": null,
    "quoteId": null,
    "outcomes": [
      {
        "type": "NAME_MATCHING",
        "outcome": "FAILURE",
        "timestamp": "2026-03-07T10:30:00Z",
        "requiresCustomerAcceptance": true,
        "fieldsChecked": ["name/fullName", "details/iban"],
        "message": "The name you entered doesn't match the account holder's name. Please check and try again.",
        "resolvedName": "Maria Rossi",
        "providedName": null,
        "recommendedUpdates": []
      }
    ]
  }
}
```

### DOES_NOT_EXIST — IBAN not reachable/recognized

**V1 response** (V2 uses `name/fullName` in `fieldsChecked`):

```JSON
{
  "id": 123456,
  "type": "indonesian",
  "accountHolderName": "Budi Santoso",
  "details": {
    "bankCode": "mandiri",
    "accountNumber": "1234567890"
  },
  "confirmations": {
    "acceptedOutcomes": false,
    "acceptedAt": null,
    "quoteId": null,
    "outcomes": [
      {
        "type": "NAME_MATCHING",
        "outcome": "COULD_NOT_CHECK",
        "timestamp": "2026-03-08T10:30:00Z",
        "requiresCustomerAcceptance": true,
        "fieldsChecked": ["accountHolderName", "details/bankCode", "details/accountNumber"],
        "message": "We couldn't verify the account holder's name right now. You can still proceed.",
        "resolvedName": null,
        "providedName": null,
        "recommendedUpdates": []
      }
    ]
  }
}
```

### COULD_NOT_CHECK (service error or timeout)

Returned when Wise encounters certain timeout or internal errors. The payload is the same regardless of which specific error happened.

**V1 response** (V2 uses `name/fullName` in `fieldsChecked`):

```JSON
{
  "id": 123456,
  "type": "indonesian",
  "accountHolderName": "Budi Santoso",
  "details": {
    "bankCode": "mandiri",
    "accountNumber": "1234567890"
  },
  "confirmations": {
    "acceptedOutcomes": false,
    "acceptedAt": null,
    "quoteId": null,
    "outcomes": [
      {
        "type": "NAME_MATCHING",
        "outcome": "COULD_NOT_CHECK",
        "timestamp": "2026-03-08T10:30:00Z",
        "requiresCustomerAcceptance": true,
        "fieldsChecked": ["accountHolderName", "details/bankCode", "details/accountNumber"],
        "message": "We couldn't verify the account holder's name right now. You can still proceed.",
        "resolvedName": null,
        "providedName": null,
        "recommendedUpdates": []
      }
    ]
  }
}
```

### Blocking errors

Invalid IBAN at receiving PSP
```json
{
  "errors": [
    {
      "code": "NOT_VALID",
      "path": "details/iban",
      "message": "The IBAN provided is invalid."
    }
  ]
}
```

Account does not exist
```json
{
  "errors": [
    {
      "code": "NOT_VALID",
      "path": "details/iban",
      "message": "The account does not exist."
    }
  ]
}
```

## Accept confirmation outcomes

When the `outcome` value is `PARTIAL_FAILURE`, `FAILURE` or `COULD_NOT_CHECK`, the `requiresCustomerAcceptance` field indicates whether you must obtain explicit end-user acceptance before proceeding.

- This field is `true` only for partners that are in scope for risk acceptance (that is, where it is legally required). When `requiresCustomerAcceptance` is `true`, you must call the `PATCH` endpoint to confirm the end user has acknowledged the result before using the recipient for a transfer.
- For partners not in scope, the field returns `false` and no `PATCH` call is needed.


### Transfer blocking

EUR transfers will be blocked if the recipient has a non-SUCCESS confirmation outcome and the `PATCH` acceptance has not been performed. However, this only applies when both of the following conditions are met:

1. Recipient validations are enabled for your integration.
2. You are within the EEA regulatory scope for EUR payments.


Unlike some currencies where transfers may proceed with a warning, EUR enforces this as a hard gate under EU VoP regulations for in-scope partners. Partners who sit outside of the EEA are not subject to this blocking requirement. For these partners, EUR transfers will proceed without the VoP confirmation gate.

Always ensure the `PATCH` call is completed before attempting to create a transfer if your integration is in scope.

### Request

**Endpoint**: `PATCH /v1/accounts/{accountId}/confirmations` or `PATCH /v2/accounts/{accountId}/confirmations`

**Body**:

```JSON
{
  "acceptedOutcomes": true
}
```

### Response after acceptance

The response mirrors the original `confirmations` object, but now `acceptedOutcomes` is `true` and `acceptedAt` contains a timestamp:

```JSON
{
  "confirmations": {
    "acceptedOutcomes": true,
    "acceptedAt": "2026-03-07T10:35:00Z",
    "quoteId": null,
    "outcomes": [
      {
        "type": "NAME_MATCHING",
        "outcome": "FAILURE",
        "timestamp": "2026-03-07T10:30:00Z",
        "requiresCustomerAcceptance": true,
        "fieldsChecked": ["accountHolderName", "details/iban"],
        "message": "The name you entered doesn't match the account holder's name.",
        "resolvedName": null,
        "providedName": null,
        "recommendedUpdates": []
      }
    ]
  }
}
```

Liability implications
When an end user accepts a `FAILURE` or `PARTIAL_FAILURE` outcome, this constitutes an explicit risk acknowledgement.

Wise records this decision, which shifts liability for any resulting loss of funds to the payer (or partner, in the case of platform integrations) under the EU VoP mandate.

Ensure your UX clearly communicates the mismatch to the end user before calling the `PATCH` endpoint, as the acceptance is treated as informed consent.

## Test in the sandbox

1. **Create a recipient** via `POST /v1/accounts/` (or V2) with valid details.
2. **Inspect the response**. Check the `confirmations.outcomes.outcome` field.
3. **If outcome requires acceptance** (`requiresCustomerAcceptance: true`), call `PATCH /v1/accounts/{id}/confirmations` with `{"acceptedOutcomes": true}`.
4. **Create a transfer** using the accepted recipient.


### How to trigger each verification outcome

The verification result depends on the relationship between the name you provide when creating the recipient and the registered name on the actual bank account.

See the following examples to illustrate testing scenarios.

**For `PRIVATE` entity types**:

| Registered name (at bank) | Name you provide | Expected outcome |
|  --- | --- | --- |
| Maria Rossi | Maria Rossi | `SUCCESS` - exact match |
| Maria Teresa Rossi | Maria Rossi | `PARTIAL_FAILURE` - partial name match |
| Maria Rossi | John Smith | `FAILURE` - no match |


**For `BUSINESS` entity types**:

| Registered name (at bank) | Name you provide | Expected outcome |
|  --- | --- | --- |
| ACME Technologies GmbH | ACME Technologies GmbH | `SUCCESS` |
| ACME Technologies Gesellschaft mit beschränkter Haftung | ACME Technologies GmbH | `SUCCESS` - abbreviation understood |
| ACME Technologies GmbH | ACME Tech | `PARTIAL_FAILURE` - missing key words |
| ACME Technologies GmbH | Different Company SA | `FAILURE` |


You don't control the registered name. It comes from the receiving PSP. To trigger `PARTIAL_FAILURE`, you need to know the actual registered name and provide a partial match. An exact match results in `SUCCESS`. A completely different name results in `FAILURE`.

**Other scenarios**:

| Test scenario | Expected outcome |
|  --- | --- |
| Valid IBAN, matching name | `SUCCESS` |
| Valid IBAN, close name | `PARTIAL_FAILURE` |
| Valid IBAN, completely wrong name | `FAILURE` (NO_MATCH_NAME) |
| Non-existent IBAN at provider | `FAILURE` (DOES_NOT_EXISt) |
| Invalid IBAN format | Blocking error: `NOT_VALID` on `details/iban` |
| Single-word name (e.g., "Maria") | Blocking error: `NOT_VALID` on `name/fullName` |
| Provider timeout/error | `COULD_NOT_CHECK` |