|
| 1 | +--- |
| 2 | +name: didit-aml-screening |
| 3 | +description: > |
| 4 | + Integrate Didit AML Screening standalone API to screen individuals or companies against |
| 5 | + global watchlists. Use when the user wants to perform AML checks, screen against sanctions |
| 6 | + lists, check PEP status, detect adverse media, implement KYC/AML compliance, screen against |
| 7 | + OFAC/UN/EU watchlists, calculate risk scores, or perform anti-money laundering screening |
| 8 | + using Didit. Supports 1300+ databases, fuzzy name matching, configurable scoring weights, |
| 9 | + and continuous monitoring. |
| 10 | +version: 1.1.0 |
| 11 | +metadata: |
| 12 | + openclaw: |
| 13 | + requires: |
| 14 | + env: |
| 15 | + - DIDIT_API_KEY |
| 16 | + primaryEnv: DIDIT_API_KEY |
| 17 | + emoji: '🛡️' |
| 18 | + homepage: https://docs.didit.me |
| 19 | +--- |
| 20 | + |
| 21 | +# Didit AML Screening API |
| 22 | + |
| 23 | +## Overview |
| 24 | + |
| 25 | +Screens individuals or companies against 1,300+ global watchlists and high-risk databases in real-time. Uses a two-score system: **Match Score** (identity confidence) and **Risk Score** (threat level). |
| 26 | + |
| 27 | +**Key constraints:** |
| 28 | + |
| 29 | +- `full_name` is the only **required** field |
| 30 | +- Supports `entity_type`: `"person"` (default) or `"company"` |
| 31 | +- Document number acts as a "Golden Key" for definitive matching |
| 32 | +- All weight parameters must sum to 100 |
| 33 | + |
| 34 | +**Coverage:** OFAC SDN, UN, EU, HM Treasury, Interpol, FBI, 170+ national sanction lists, PEP Levels 1-4, 50,000+ adverse media sources, financial crime databases. |
| 35 | + |
| 36 | +**Scoring system:** |
| 37 | + |
| 38 | +1. **Match Score** (0-100): Is this the same person? → classifies hits as False Positive or Unreviewed |
| 39 | +2. **Risk Score** (0-100): How risky is this entity? → determines final AML status |
| 40 | + |
| 41 | +**API Reference:** https://docs.didit.me/standalone-apis/aml-screening |
| 42 | +**Feature Guide:** https://docs.didit.me/core-technology/aml-screening/overview |
| 43 | +**Risk Scoring:** https://docs.didit.me/core-technology/aml-screening/aml-risk-score |
| 44 | + |
| 45 | +--- |
| 46 | + |
| 47 | +## Authentication |
| 48 | + |
| 49 | +All requests require `x-api-key` header. Get your key from [Didit Business Console](https://business.didit.me) → API & Webhooks, or via programmatic registration (see below). |
| 50 | + |
| 51 | +## Getting Started (No Account Yet?) |
| 52 | + |
| 53 | +If you don't have a Didit API key, create one in 2 API calls: |
| 54 | + |
| 55 | +1. **Register:** `POST https://apx.didit.me/auth/v2/programmatic/register/` with `{"email": "you@gmail.com", "password": "MyStr0ng!Pass"}` |
| 56 | +2. **Check email** for a 6-character OTP code |
| 57 | +3. **Verify:** `POST https://apx.didit.me/auth/v2/programmatic/verify-email/` with `{"email": "you@gmail.com", "code": "A3K9F2"}` → response includes `api_key` |
| 58 | + |
| 59 | +**To add credits:** `GET /v3/billing/balance/` to check, `POST /v3/billing/top-up/` with `{"amount_in_dollars": 50}` for a Stripe checkout link. |
| 60 | + |
| 61 | +See the **didit-verification-management** skill for full platform management (workflows, sessions, users, billing). |
| 62 | + |
| 63 | +--- |
| 64 | + |
| 65 | +## Endpoint |
| 66 | + |
| 67 | +``` |
| 68 | +POST https://verification.didit.me/v3/aml/ |
| 69 | +``` |
| 70 | + |
| 71 | +### Headers |
| 72 | + |
| 73 | +| Header | Value | Required | |
| 74 | +| -------------- | ------------------ | -------- | |
| 75 | +| `x-api-key` | Your API key | **Yes** | |
| 76 | +| `Content-Type` | `application/json` | **Yes** | |
| 77 | + |
| 78 | +### Body (JSON) |
| 79 | + |
| 80 | +| Parameter | Type | Required | Default | Description | |
| 81 | +| --------------------------- | ------- | -------- | ---------- | --------------------------------------------- | |
| 82 | +| `full_name` | string | **Yes** | — | Full name of person or entity | |
| 83 | +| `date_of_birth` | string | No | — | DOB in `YYYY-MM-DD` format | |
| 84 | +| `nationality` | string | No | — | ISO country code (alpha-2 or alpha-3) | |
| 85 | +| `document_number` | string | No | — | ID document number ("Golden Key") | |
| 86 | +| `entity_type` | string | No | `"person"` | `"person"` or `"company"` | |
| 87 | +| `aml_name_weight` | integer | No | `60` | Name weight in match score (0-100) | |
| 88 | +| `aml_dob_weight` | integer | No | `25` | DOB weight in match score (0-100) | |
| 89 | +| `aml_country_weight` | integer | No | `15` | Country weight in match score (0-100) | |
| 90 | +| `aml_match_score_threshold` | integer | No | `93` | Below = False Positive, at/above = Unreviewed | |
| 91 | +| `save_api_request` | boolean | No | `true` | Save in Business Console | |
| 92 | +| `vendor_data` | string | No | — | Your identifier for session tracking | |
| 93 | + |
| 94 | +### Example |
| 95 | + |
| 96 | +```python |
| 97 | +import requests |
| 98 | + |
| 99 | +response = requests.post( |
| 100 | + "https://verification.didit.me/v3/aml/", |
| 101 | + headers={"x-api-key": "YOUR_API_KEY", "Content-Type": "application/json"}, |
| 102 | + json={ |
| 103 | + "full_name": "John Smith", |
| 104 | + "date_of_birth": "1985-03-15", |
| 105 | + "nationality": "US", |
| 106 | + "document_number": "AB1234567", |
| 107 | + "entity_type": "person", |
| 108 | + }, |
| 109 | +) |
| 110 | +print(response.json()) |
| 111 | +``` |
| 112 | + |
| 113 | +```typescript |
| 114 | +const response = await fetch('https://verification.didit.me/v3/aml/', { |
| 115 | + method: 'POST', |
| 116 | + headers: { 'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json' }, |
| 117 | + body: JSON.stringify({ |
| 118 | + full_name: 'John Smith', |
| 119 | + date_of_birth: '1985-03-15', |
| 120 | + nationality: 'US', |
| 121 | + }), |
| 122 | +}); |
| 123 | +``` |
| 124 | + |
| 125 | +### Response (200 OK) |
| 126 | + |
| 127 | +```json |
| 128 | +{ |
| 129 | + "request_id": "a1b2c3d4-...", |
| 130 | + "aml": { |
| 131 | + "status": "Approved", |
| 132 | + "total_hits": 2, |
| 133 | + "score": 45.5, |
| 134 | + "hits": [ |
| 135 | + { |
| 136 | + "id": "hit-uuid", |
| 137 | + "caption": "John Smith", |
| 138 | + "match_score": 85, |
| 139 | + "risk_score": 45.5, |
| 140 | + "review_status": "False Positive", |
| 141 | + "datasets": ["PEP"], |
| 142 | + "properties": { "name": ["John Smith"], "country": ["US"] }, |
| 143 | + "score_breakdown": { |
| 144 | + "name_score": 95, |
| 145 | + "name_weight": 60, |
| 146 | + "dob_score": 100, |
| 147 | + "dob_weight": 25, |
| 148 | + "country_score": 100, |
| 149 | + "country_weight": 15 |
| 150 | + }, |
| 151 | + "risk_view": { |
| 152 | + "categories": { "score": 55, "risk_level": "High" }, |
| 153 | + "countries": { "score": 23, "risk_level": "Low" }, |
| 154 | + "crimes": { "score": 0, "risk_level": "Low" } |
| 155 | + } |
| 156 | + } |
| 157 | + ], |
| 158 | + "screened_data": { |
| 159 | + "full_name": "John Smith", |
| 160 | + "date_of_birth": "1985-03-15", |
| 161 | + "nationality": "US", |
| 162 | + "document_number": "AB1234567" |
| 163 | + }, |
| 164 | + "warnings": [] |
| 165 | + } |
| 166 | +} |
| 167 | +``` |
| 168 | + |
| 169 | +--- |
| 170 | + |
| 171 | +## Match Score System |
| 172 | + |
| 173 | +**Formula:** `(Name × W1) + (DOB × W2) + (Country × W3)` |
| 174 | + |
| 175 | +| Component | Default Weight | Algorithm | |
| 176 | +| --------- | -------------- | -------------------------------------------------------------------- | |
| 177 | +| Name | 60% | RapidFuzz WRatio — handles typos, word order, middle name variations | |
| 178 | +| DOB | 25% | Exact=100%, Year-only=100%, Same year diff date=50%, Mismatch=-100% | |
| 179 | +| Country | 15% | Exact=100%, Mismatch=-50%, Missing=0%. Auto-converts ISO codes | |
| 180 | + |
| 181 | +**Document Number "Golden Key":** |
| 182 | + |
| 183 | +| Scenario | Effect | |
| 184 | +| ----------------------------- | ------------------------- | |
| 185 | +| Same type, same value | Override score to **100** | |
| 186 | +| Different type or one missing | Keep base score (neutral) | |
| 187 | +| Same type, different value | **-50 point penalty** | |
| 188 | + |
| 189 | +**Classification:** Score < threshold (default 93) → **False Positive**. Score >= threshold → **Unreviewed**. |
| 190 | + |
| 191 | +> When data is missing, remaining weights are re-normalized. E.g., name-only → name weight becomes 100%. |
| 192 | +
|
| 193 | +--- |
| 194 | + |
| 195 | +## Risk Score System |
| 196 | + |
| 197 | +**Formula:** `(Country × 0.30) + (Category × 0.50) + (Criminal × 0.20)` |
| 198 | + |
| 199 | +**Final AML Status (from highest risk score among non-FP hits):** |
| 200 | + |
| 201 | +| Highest Risk Score | Status | |
| 202 | +| ------------------- | ------------- | |
| 203 | +| Below 80 (default) | **Approved** | |
| 204 | +| Between 80-100 | **In Review** | |
| 205 | +| Above 100 | **Declined** | |
| 206 | +| All False Positives | **Approved** | |
| 207 | + |
| 208 | +**Category scores (50% weight):** |
| 209 | + |
| 210 | +| Category | Score | |
| 211 | +| ---------------------------- | ----- | |
| 212 | +| Sanctions / PEP Level 1 | 100 | |
| 213 | +| Warnings & Regulatory | 95 | |
| 214 | +| PEP Level 2 / Insolvency | 80 | |
| 215 | +| Adverse Media | 60 | |
| 216 | +| PEP Level 4 / Businessperson | 55 | |
| 217 | + |
| 218 | +--- |
| 219 | + |
| 220 | +## Status Values & Handling |
| 221 | + |
| 222 | +| Status | Meaning | Action | |
| 223 | +| --------------- | --------------------------------------------- | --------------------------------- | |
| 224 | +| `"Approved"` | No significant matches or all False Positives | Safe to proceed | |
| 225 | +| `"In Review"` | Matches found with moderate risk | Manual compliance review needed | |
| 226 | +| `"Rejected"` | High-risk matches confirmed | Block or escalate per your policy | |
| 227 | +| `"Not Started"` | Screening not yet performed | Check for missing data | |
| 228 | + |
| 229 | +### Error Responses |
| 230 | + |
| 231 | +| Code | Meaning | Action | |
| 232 | +| ----- | -------------------- | --------------------------------------- | |
| 233 | +| `400` | Invalid request body | Check `full_name` and parameter formats | |
| 234 | +| `401` | Invalid API key | Verify `x-api-key` header | |
| 235 | +| `403` | Insufficient credits | Check credits in Business Console | |
| 236 | + |
| 237 | +--- |
| 238 | + |
| 239 | +## Warning Tags |
| 240 | + |
| 241 | +| Tag | Description | |
| 242 | +| --------------------------------- | ---------------------------------------------------------------------- | |
| 243 | +| `POSSIBLE_MATCH_FOUND` | Potential watchlist matches requiring review | |
| 244 | +| `COULD_NOT_PERFORM_AML_SCREENING` | Missing KYC data. Provide full name, DOB, nationality, document number | |
| 245 | + |
| 246 | +--- |
| 247 | + |
| 248 | +## Response Field Reference |
| 249 | + |
| 250 | +### Hit Object |
| 251 | + |
| 252 | +| Field | Type | Description | |
| 253 | +| -------------------------- | ------- | ------------------------------------------------------------------------- | |
| 254 | +| `match_score` | integer | 0-100 identity confidence score | |
| 255 | +| `risk_score` | float | 0-100 threat level score | |
| 256 | +| `review_status` | string | `"False Positive"`, `"Unreviewed"`, `"Confirmed Match"`, `"Inconclusive"` | |
| 257 | +| `datasets` | array | e.g. `["Sanctions"]`, `["PEP"]`, `["Adverse Media"]` | |
| 258 | +| `pep_matches` | array | PEP match details | |
| 259 | +| `sanction_matches` | array | Sanction match details | |
| 260 | +| `adverse_media_matches` | array | `{headline, summary, source_url, sentiment_score, adverse_keywords}` | |
| 261 | +| `linked_entities` | array | Related persons/entities | |
| 262 | +| `first_seen` / `last_seen` | string | ISO 8601 timestamps | |
| 263 | + |
| 264 | +**Adverse media sentiment:** `-1` = slightly negative, `-2` = moderately, `-3` = highly negative. |
| 265 | + |
| 266 | +--- |
| 267 | + |
| 268 | +## Continuous Monitoring |
| 269 | + |
| 270 | +Available on **Pro plan**. Automatically included for all AML-screened sessions. |
| 271 | + |
| 272 | +- **Daily automated re-screening** against updated watchlists |
| 273 | +- New hits → session status updated to "In Review" or "Declined" based on thresholds |
| 274 | +- **Real-time webhook notifications** on status changes |
| 275 | +- Zero additional integration — uses same thresholds from workflow config |
| 276 | + |
| 277 | +--- |
| 278 | + |
| 279 | +## Common Workflows |
| 280 | + |
| 281 | +### Basic AML Check |
| 282 | + |
| 283 | +``` |
| 284 | +1. POST /v3/aml/ → {"full_name": "John Smith", "nationality": "US"} |
| 285 | +2. If "Approved" → no significant watchlist matches |
| 286 | + If "In Review" → review hits[].datasets, hits[].risk_view for details |
| 287 | + If "Rejected" → block user, check hits for sanctions/PEP details |
| 288 | +``` |
| 289 | + |
| 290 | +### Comprehensive KYC + AML |
| 291 | + |
| 292 | +``` |
| 293 | +1. POST /v3/id-verification/ → extract name, DOB, nationality, document number |
| 294 | +2. POST /v3/aml/ → screen extracted data with all fields populated |
| 295 | +3. More data = higher match accuracy = fewer false positives |
| 296 | +``` |
| 297 | + |
| 298 | +--- |
| 299 | + |
| 300 | +## Utility Scripts |
| 301 | + |
| 302 | +**screen_aml.py**: Screen against AML watchlists from the command line. |
| 303 | + |
| 304 | +```bash |
| 305 | +# Requires: pip install requests |
| 306 | +export DIDIT_API_KEY="your_api_key" |
| 307 | +python scripts/screen_aml.py --name "John Smith" |
| 308 | +python scripts/screen_aml.py --name "John Smith" --dob 1985-03-15 --nationality US |
| 309 | +python scripts/screen_aml.py --name "Acme Corp" --entity-type company |
| 310 | +``` |
0 commit comments