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

# AML Monitoring Migration Guide

> Migrate your AML Monitoring integration from v2 to v3, with breaking changes, endpoint mapping, and step-by-step upgrade guidance.

## Overview

iDenfy introduced AML Monitoring v3 with an upgraded screening engine. **Existing v2 integrations continue to work** — the API automatically handles backward compatibility and your webhooks will look exactly as they always have.

<Warning>
  There is no announced shutdown date for v2 compatibility. However, the mapping layer exists for continuity, not as a permanent solution. We recommend planning migration to v3 to ensure long-term stability and access to the full capabilities of the screening engine.
</Warning>

This guide covers:

* What changed between v2 and v3
* Request body field changes with examples
* The complete webhook payload reference
* The recommended migration path and a complete migration checklist

***

## What Changed

| Event                      | Status transition               |
| -------------------------- | ------------------------------- |
| Initial screening complete | `PENDING` → `ACTIVE` or `ALERT` |
| New match found            | → `ALERT`                       |
| Match resolved             | → `ACTIVE`                      |

### Status Field Names

v3 introduced a new `status` field with updated values. The existing `alertStatus` field continues to return v2 values for all integrations.

| v2 (`alertStatus`) | v3 (`status`) |
| ------------------ | ------------- |
| `ACCEPTED`         | `ACTIVE`      |
| `ALERT`            | `ALERT`       |
| `DECLINED`         | `STOPPED`     |
| `PENDING`          | `PENDING`     |

Both fields are present in all webhook payloads. If you are on v2, use `alertStatus` and ignore `status`.

### Request Body Fields

When migrating to v3 endpoints, the request body structure changes. v3 uses a nested structure — a top-level object wrapping an `amlCheck` object with separate `input` (who to screen) and `filter` (what to screen for) sub-objects.

**Person monitoring:**

| Area          | v2 field                    | v3 field                     | Notes                                |
| ------------- | --------------------------- | ---------------------------- | ------------------------------------ |
| First name    | `name`                      | —                            | Combined into `fullName`             |
| Last name     | `surname`                   | —                            | Combined into `fullName`             |
| Full name     | —                           | `amlCheck.input.fullName`    | Single combined field                |
| Subject type  | `type: "PERSON"`            | `userType: "PERSON"`         | Renamed                              |
| Date of birth | `dateOfBirth`               | `amlCheck.input.dateOfBirth` | Moved into nested input              |
| Nationality   | `nationality`               | `amlCheck.input.nationality` | Moved into nested input              |
| Gender        | —                           | `amlCheck.input.sex`         | New optional field: `M`, `F`, or `O` |
| Adverse media | `monitorAdverseMedia: true` | —                            | Legacy feature, not supported in v3  |
| Auto-renewal  | `autoExpirationExtension`   | `isSubscribed`               | Renamed, same boolean behaviour      |

**Company monitoring:**

| Area         | v2 field          | v3 field                     | Notes              |
| ------------ | ----------------- | ---------------------------- | ------------------ |
| Company name | `name`            | `amlCheck.input.companyName` | Renamed and nested |
| Subject type | `type: "COMPANY"` | `userType: "COMPANY"`        | Renamed            |
| Country      | `nationality`     | `amlCheck.input.country`     | Renamed and nested |

<Warning>
  The `name` + `surname` → `fullName` change is the most common cause of silent failures. If you send `name` and `surname` separately to a v3 endpoint, the fields are ignored and the check runs with no name.
</Warning>

**Example v3 request body (person):**

```json theme={"system"}
{
  "userType": "PERSON",
  "isSubscribed": true,
  "tags": [],
  "amlCheck": {
    "input": {
      "fullName": "John Doe",
      "nationality": "GBR",
      "dateOfBirth": "1980-01-15",
      "sex": "M"
    },
    "filter": {
      "datasets": ["PEP", "SAN"]
    }
  }
}
```

### Datasets

v3 lets you control which screening databases to check via the `amlCheck.filter.datasets` array. This replaces the implicit fixed set from v2, where adverse media was a separate boolean toggle.

| Dataset       | Description                                                           |
| ------------- | --------------------------------------------------------------------- |
| `PEP`         | All PEP tiers — shorthand that expands to current, former, and linked |
| `PEP-CURRENT` | Active politically exposed persons                                    |
| `PEP-FORMER`  | Former PEPs                                                           |
| `PEP-LINKED`  | Associates and family members of PEPs                                 |
| `SAN`         | All sanctions — shorthand for current and former                      |
| `SAN-CURRENT` | Active sanctions                                                      |
| `SAN-FORMER`  | Expired or lifted sanctions                                           |

<Info>
  `datasets` is required. Omitting it will result in a validation error. Specify at least one dataset value from the table above.
</Info>

### False Positive Handling

v2 and v3 handle false positives differently — there is no direct 1:1 equivalent endpoint.

* **v2** (`POST /api/v2/add-whitelist`) marked an entire monitoring subject as whitelisted using their `monitoringId`.
* **v3** (`DELETE /aml/monitorings/{id}/profiles/{profileId}/`) removes a specific matched profile from the results, leaving the monitoring record and any other matches intact.

The v3 approach is more granular — you dismiss individual matches rather than the whole subject. The `profileId` is returned inside each match entry in the monitoring response and webhook payload. Store or pass through this ID in your integration to support false positive dismissal.

***

## Webhook Reference

Both v2 and v3 send the same webhook payload structure. v3 adds the `status` field and an optional `amlCheck` object — both can be safely ignored if you have not yet migrated.

### Headers

| Header              | Value                                                               |
| ------------------- | ------------------------------------------------------------------- |
| `Idenfy-Event-Type` | `AML_MONITORING`                                                    |
| `Content-Type`      | `application/json; charset=utf-8`                                   |
| `User-Agent`        | `iDenfy/1.0`                                                        |
| `Idenfy-Signature`  | HMAC-SHA256 signature — only present if a signing key is configured |

### Payload Fields

<Warning>
  If you are on v3, use the `amlCheck` object from the webhook payload for all screening results. The top-level fields below exist only for backward compatibility with v2 integrations and should not be relied on in new or migrated integrations.
</Warning>

**Top-level (v2 compatibility fields):**

| Field                | Type           | Description                                                |
| -------------------- | -------------- | ---------------------------------------------------------- |
| `monitoringId`       | string (UUID)  | Unique identifier for this monitoring record               |
| `name` / `surname`   | string         | Subject's name                                             |
| `nationality`        | string         | ISO country code                                           |
| `dob`                | string \| null | Date of birth                                              |
| `isActive`           | boolean        | Whether monitoring is still running                        |
| `expiration`         | string         | Monitoring expiry date                                     |
| `alertStatus`        | string         | v2 status: `ACCEPTED`, `ALERT`, `DECLINED`, `PENDING`      |
| `status`             | string         | v3 status: `ACTIVE`, `ALERT`, `STOPPED`, `PENDING`         |
| `pepsStatus`         | string         | `NOT_CHECKED`, `FLAGS_FOUND`, `FALSE_POSITIVE`, or `CLEAR` |
| `sanctionsStatus`    | string         | Same values as `pepsStatus`                                |
| `adverseMediaStatus` | string         | Same values as `pepsStatus`                                |
| `results[]`          | array          | Per-category check results (see below)                     |

**`amlCheck` — v3 screening results (use this on v3):**

The `amlCheck` object contains the full World-Check screening output broken down by dataset, with per-finding `profileId` values used for false positive dismissal. Refer to the [API Reference](/api-reference/aml-checks/create-aml-check) for the complete schema.

**`results[]` — v2 compatibility, per-category check:**

| Field                  | Type   | Description                              |
| ---------------------- | ------ | ---------------------------------------- |
| `serviceName`          | string | Name of the check that ran               |
| `serviceGroupType`     | string | Category, e.g. `AML_NAMES_CHECK`         |
| `status.overallStatus` | string | `SUSPECTED`, `NOT_SUSPECTED`, or `ERROR` |
| `data[]`               | array  | Matched profiles (see below)             |

**`data[]` — matched profile:**

| Field              | Type           | Description                                      |
| ------------------ | -------------- | ------------------------------------------------ |
| `listName`         | string         | `PEPS` or `SANCTION`                             |
| `name` / `surname` | string         | Matched entity's name                            |
| `nationality`      | string         | Matched entity's nationality                     |
| `dob`              | string \| null | Matched entity's date of birth                   |
| `score`            | integer        | Match confidence (0–100)                         |
| `pepLevel`         | integer        | PEP tier 1–4 (present for PEP matches only)      |
| `category`         | string         | e.g. `Head of Government`                        |
| `reason`           | string         | Why this entity is listed                        |
| `whitelisted`      | boolean        | Whether you have marked this as a false positive |
| `isActive`         | boolean        | Whether the entity is still active on the list   |

***

## Migrating to v3

### AML Single Check Flow

Use `POST /aml/checks/` to screen a subject once without ongoing monitoring.

<Steps>
  <Step title="Create an AML Check">
    Submit a person or company for screening. The response includes a check ID and any matched profiles.

    <Card title="Create AML Check" icon="plus" href="/aml/create-profile">
      `POST /aml/checks/` — Screen a subject against sanctions, PEPs, and adverse media.
    </Card>

    The response body includes a `profiles` array. Each entry contains a `profileId` you can use in the next step.
  </Step>

  <Step title="Retrieve a Matched Profile">
    Use the check ID and profile ID from the previous response to fetch the complete profile record — all sanctions entries, PEP tiers, and adverse media sources, regardless of the filters applied during the check.

    <Card title="Retrieve AML Profile" icon="download" href="/aml/retrieve-profile">
      `GET /aml/checks/{id}/profiles/{profileId}/` — Fetch full profile detail for a specific match.
    </Card>

    <Warning>
      Profile data is not filtered by the check's configured datasets. Retrieve the full profile to see all available information for a matched entity.
    </Warning>
  </Step>
</Steps>

***

### AML Monitoring Flow

Monitoring is continuous screening — the system re-screens the entity daily and sends webhooks whenever status changes.

<Steps>
  <Step title="Create a Monitoring Record">
    Register a person or company for ongoing daily screening. The response returns a `monitoringId` used in all subsequent calls.

    <Card title="Create AML Monitoring" icon="eye" href="/aml/monitoring-create">
      `POST /aml/monitorings/` — Start ongoing screening with automatic status change alerts.
    </Card>

    Use `PATCH /aml/monitorings/{id}/` to extend the monitoring period or attach organizational tags.
  </Step>

  <Step title="Receive Webhook Alerts">
    The system fires an `AML_MONITORING` webhook each time the screening status changes. See the [Webhook Reference](#webhook-reference) above for the full payload and delivery details.
  </Step>

  <Step title="Retrieve Monitoring Status">
    Poll the monitoring record at any time to get the current status and latest results — useful for reconciling state if a webhook was missed.

    <CardGroup cols={2}>
      <Card title="Retrieve Monitoring" icon="download" href="/aml/monitoring-retrieve">
        `GET /aml/monitorings/{id}/` — Fetch a single monitoring record.
      </Card>

      <Card title="List All Monitored Entities" icon="list" href="/aml/monitoring-retrieve">
        `GET /aml/monitorings/` — List all monitoring records.
      </Card>
    </CardGroup>
  </Step>

  <Step title="Manage the Monitoring Lifecycle">
    Pause, resume, or remove monitoring records as your compliance requirements change.

    <CardGroup cols={2}>
      <Card title="Start / Stop Monitoring" icon="circle-pause" href="/aml/monitoring-manage">
        `POST /aml/monitorings/{id}/stop/` or `/start/` — Pause or resume without deleting.
      </Card>

      <Card title="Delete Monitoring" icon="trash" href="/aml/monitoring-manage">
        `DELETE /aml/monitorings/{id}/` — Permanently remove a monitoring record.
      </Card>
    </CardGroup>

    To dismiss a specific false positive match without stopping monitoring, use `DELETE /aml/monitorings/{id}/profiles/{profileId}/`. See [False Positive Handling](#false-positive-handling) above for details.
  </Step>

  <Step title="Generate a Compliance PDF (Optional)">
    Export the full screening history for a monitoring record as a PDF for audit trails and compliance records.

    <Card title="Generate Monitoring PDF" icon="file-pdf" href="/aml/monitoring-pdf">
      `POST /aml/monitorings/{id}/pdf/` — Download a full-history PDF report.
    </Card>
  </Step>
</Steps>

***

## Complete Migration Checklist

Use this as a final review before cutting over to v3.

**API endpoints:**

| v2 endpoint                            | v3 endpoint                                                                    |
| -------------------------------------- | ------------------------------------------------------------------------------ |
| `POST /api/v2/add-aml-user`            | `POST /aml/monitorings/`                                                       |
| `POST /api/v2/get-monitoring-callback` | `GET /aml/monitorings/{id}/`                                                   |
| `POST /api/v2/get-aml-users`           | `GET /aml/monitorings/`                                                        |
| `POST /api/v2/delete-monitoring-user/` | `DELETE /aml/monitorings/{id}/`                                                |
| `POST /api/v2/generate-pdf-aml`        | `POST /aml/monitorings/{id}/pdf/`                                              |
| `POST /api/v2/add-whitelist`           | No direct equivalent — see [False Positive Handling](#false-positive-handling) |

**Request body — persons:**

* [ ] Combine `name` + `surname` → `amlCheck.input.fullName`
* [ ] Rename `type: "PERSON"` → `userType: "PERSON"`
* [ ] Move `nationality` → `amlCheck.input.nationality`
* [ ] Move `dateOfBirth` → `amlCheck.input.dateOfBirth`
* [ ] Remove `monitorAdverseMedia` — adverse media is not supported in v3
* [ ] Rename `autoExpirationExtension` → `isSubscribed`

**Request body — companies:**

* [ ] Rename `name` → `amlCheck.input.companyName`
* [ ] Rename `type: "COMPANY"` → `userType: "COMPANY"`
* [ ] Rename `nationality` → `amlCheck.input.country`

**Webhook handler:**

* [ ] Read `status` instead of (or alongside) `alertStatus`
* [ ] Update status value comparisons: `ACCEPTED` → `ACTIVE`, `DECLINED` → `STOPPED`
* [ ] Store `profileId` from match entries if you support false positive dismissal

<Info>
  Existing monitoring records created via v2 remain accessible. v2 and v3 records coexist in the system — your historical data is not affected by migration.
</Info>
