Overview
The FT AML API lets your systems talk to the AML Platform directly: push customer or transaction data in, and get screening results, alerts, and case updates back — either by polling or by subscribing to webhooks.
- All requests and responses use JSON over HTTPS.
- Every request requires a bearer token (see Authentication below).
- Endpoints are grouped by the same modules used in the platform itself: Name Screening, Transaction Monitoring, Case Management, and Data Ingestion.
Base URL
https://api.ftaml.example/v1
Format
JSON request & response, UTF-8
Authentication
Every request must include a bearer token issued to your organization. Contact your account team to generate one.
Authorization: Bearer <api_token> Content-Type: application/json
Tokens are scoped per environment — use a sandbox token while integrating, and switch to a production token only once your integration is verified.
Core concepts
A handful of ideas show up throughout the API, carried over directly from how the platform itself works:
Variable
A named, reusable calculation over your data — e.g. count_sender_txns_1_week. Rules reference variables instead of repeating raw logic.
Typology
The category of suspicious behavior a rule detects (e.g. Smurfing). Every alert is tagged with the typology that triggered it.
Alert
Raised when a screening match or typology crosses its configured severity threshold.
Case
An investigation opened from one or more alerts, ending in a disposition (cleared, or escalated to an STR).
Relationship flow, end to end:
Customer / Transaction -> Screening or Variable/Typology Evaluation -> Alert -> Case (investigation) -> Disposition -> (optional) STR
Endpoints reference
These paths and payload shapes are a proposed structure based on the platform's feature set. No confirmed API specification was available while drafting this page — check every path, field name, and status code against the real implementation before this goes live.
Name screening
| Method & path | Purpose |
|---|---|
| POST/screening/manual | Submit a single individual or entity for manual screening |
| POST/screening/batch | Submit a batch of records for screening |
| GET/screening/jobs/{jobId} | Check the status of a batch or delta screening job |
Transaction monitoring
| Method & path | Purpose |
|---|---|
| GET/variables | List available variables |
| POST/variables | Create a new variable |
| GET/typologies | List transaction monitoring typologies (rules) |
| POST/typologies | Create or update a typology |
Case management
| Method & path | Purpose |
|---|---|
| GET/alerts | Query alerts by severity, status, or date |
| PATCH/cases/{caseId}/disposition | Record an investigation outcome on a case |
Data ingestion
| Method & path | Purpose |
|---|---|
| GET/ingestion/watchlist/jobs | List watchlist ingestion job history |
| GET/ingestion/tms/jobs | List transaction data ingestion job history |
Example — create a variable & typology
POST /variables { "name": "Cash_Deposit_Count_7D", "type": "Int", "computation": { "aggregation": "count", "filter": { "channel": "cash_deposit" }, "window": "7d", "group_by": "account_id" } } POST /typologies { "name": "Structuring - Frequent Sub-Threshold Cash Deposits", "conditions": { "all": [ { "variable": "VAR-1042", "operator": ">=", "value": 4 }, { "variable": "VAR-1055", "operator": "<", "value": 10000 } ]}, "risk_score": 78, "severity_hint": "High", "status": "active" }
Example — query alerts & record a disposition
GET /alerts?severity=High&status=open 200 OK { "results": [ { "alert_id": "ALT-55012", "typology_id": "TYP-2031", "severity": "High", "status": "open" } ], "page": 1, "page_size": 50, "total": 1 } PATCH /cases/CASE-2026-0000087/disposition { "disposition": "escalate_str", "notes": "Confirmed structuring pattern, filing STR." }
Example — manual screening request
POST /screening/manual Authorization: Bearer <api_token> Content-Type: application/json { "name": "John Doe", "entity_type": "individual", "dob": "1985-04-12", "citizenship": "GBR" }
200 OK { "screening_id": "SCR-88213", "status": "completed", "match_score": 91, "matches": [ { "watchlist_id": "WL-3391", "name": "Jon Doe", "score": 91, "category": "PEP" } ] }
Webhooks
Subscribe to receive alerts as they're generated, instead of polling. Configure your callback URL in Account > Integrations.
Example — alert created
POST <your_webhook_url> Content-Type: application/json { "event": "alert.created", "alert_id": "ALT-55012", "typology_id": "TYP-2031", "account_id": "ACC-77410", "risk_score": 78, "severity": "High", "sla_deadline": "2026-07-13T09:00:00Z" }
Retry policy, signing secret, and delivery guarantees: to be confirmed with engineering.
Errors
Failed requests return a JSON error body alongside a standard HTTP status code:
{
"error": {
"code": "invalid_request",
"message": "citizenship must be a valid ISO-3166 country code"
}
}
| Status | Meaning |
|---|---|
| 400 | Request was malformed or failed validation |
| 401 | Missing or invalid bearer token |
| 404 | Resource not found (e.g. unknown job or case ID) |
| 429 | Rate limit exceeded — see below |
| 500 | Unexpected server error — retry with backoff |
Rate limits
Default limit: 100 requests/minute per API key. Batch endpoints (/screening/batch, ingestion) are subject to separate, higher size limits per job rather than per request.
429 Too Many Requests { "error": { "code": "rate_limited", "retry_after": 12 } }
Pagination
List endpoints return a page of results with a total count. Always check for a next page rather than assuming a single response contains the full result set.
GET /alerts?page=2&page_size=50
Idempotency
For POST endpoints that create records (/screening/manual, /typologies), pass an Idempotency-Key header so a retried request — after a timeout, for example — doesn't create a duplicate record.
Idempotency-Key: 3f29a1e2-9c3b-4e11-9e2a-6b1d4a0f9c11
Exact rate limit thresholds and the pagination default page size are illustrative and not yet confirmed with engineering.
Code samples
The same manual screening call from Overview, in a couple of common languages.
cURL
curl -X POST https://api.ftaml.example/v1/screening/manual \ -H "Authorization: Bearer <API_KEY>" \ -H "Content-Type: application/json" \ -d '{"name": "John Doe", "entity_type": "individual", "dob": "1985-04-12", "citizenship": "GBR"}'
Python
import requests API_KEY = "..." BASE = "https://api.ftaml.example/v1" resp = requests.post( f"{BASE}/screening/manual", headers={"Authorization": f"Bearer {API_KEY}"}, json={"name": "John Doe", "entity_type": "individual", "dob": "1985-04-12", "citizenship": "GBR"}, ) print(resp.json())
Node.js
const res = await fetch("https://api.ftaml.example/v1/screening/manual", { method: "POST", headers: { "Authorization": `Bearer ${process.env.FTAML_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "John Doe", entity_type: "individual", dob: "1985-04-12", citizenship: "GBR", }), }); const data = await res.json();
Changelog & versioning
- The API is versioned in the URL path (
/v1/...); breaking changes ship under a new version prefix (/v2/...). - Non-breaking additions — new optional fields, new endpoints — may appear within a version without notice.
- Deprecated endpoints are marked in this reference and supported for a minimum notice period before removal.
This versioning policy is a proposed default, not a documented commitment yet.
Support
- In-app: Homepage > Help & Support
- Email: developers@ftaml.example
- Account-level issues (API key provisioning, permissions) — contact your organization's Admin
If this reference doesn't answer your question, that's a gap we want to close.