Overview
A case is a compliance investigation anchored to exactly one subject: one user or one business. Sessions, business sessions and transactions of that same subject can be linked to it. Your team (or your integration) works the case from OPEN to RESOLVED with a full audit trail, and can file a Financial Intelligence Unit (FIU) report from it.
Key facts:
- Lifecycle has three statuses:
OPEN,AWAITING_USER,RESOLVED. Resolution isFALSE_POSITIVE,VALID_THREAT,INCONCLUSIVE,REQUIREMENTS_NOT_METorREQUIREMENTS_SATISFIED; onlyVALID_THREATfeeds the shared fraud index. Priority isLOW,MEDIUM(default) orHIGH. - One subject per case. Linking a session, business session or transaction that belongs to another user or business returns
400. - A blueprint (called a review policy in the console) decides assignment, escalation, transfer, 4-eyes approval, a required resolution note, the checklist and which sections the case page shows.
- Cases are created by four sources:
MANUAL(console or API),AML_SCREENING(ongoing-monitoring hits),TRANSACTION_RULE(a rule with the Create case effect) andWORKFLOW_BUILDER(a Create case node). API-created cases are alwaysMANUAL. - Every endpoint is scoped to one organization and one application:
https://verification.didit.me/v3/organization/{organization_id}/application/{application_id}/... - Actions taken with an API key have no person attached:
actor_emailandactor_nameon events and notes, andresolved_by_email, arenull.
Pricing: No list price is published for these endpoints; check didit.me/pricing.
API Reference: https://docs.didit.me/management-api/cases/list Feature guides: https://docs.didit.me/console/case-management/cases | https://docs.didit.me/console/case-management/blueprints | https://docs.didit.me/console/case-management/four-eyes | https://docs.didit.me/console/case-management/fiu-reports
Authentication
Every request needs the x-api-key header. Use your application's API key from the Didit Business Console under Developers > API keys, or create an account and key programmatically (see Getting Started). Case endpoints need the application's primary key or a named key with full access: a named key with custom access gets 403 "This API key has no access to this endpoint."
The key only acts on its own application: {application_id} must be the application the key belongs to, otherwise 403 "You do not have permission to perform this action."
| Path ID | Where to find it |
|---|---|
{organization_id} |
organization.uuid in the verify-email response; Business Console > Organization Settings > Organization ID; or GET https://apx.didit.me/auth/v2/organizations/me/ with Authorization: Bearer <access_token> |
{application_id} |
application.uuid in the verify-email response; or GET https://apx.didit.me/auth/v2/organizations/me/{org_id}/applications/ with a Bearer token (each entry's uuid) |
Getting Started (No Account Yet?)
If you don't have a Didit API key, create one in 2 API calls:
- Register:
POST https://apx.didit.me/auth/v2/programmatic/register/with{"email": "you@yourdomain.com", "password": "MyStr0ng!Pass"} - Check email for a 6-character verification code
- Verify:
POST https://apx.didit.me/auth/v2/programmatic/verify-email/with{"email": "you@yourdomain.com", "code": "A3K9F2"}. The key is in the response atapplication.api_key.
To add credits: GET /v3/billing/balance/ to check, POST /v3/billing/top-up/ with {"amount_in_dollars": 50} for a Stripe checkout link.
See the didit-verification-management skill for full platform management (workflows, sessions, users, billing).
The same verify-email response carries organization.uuid and application.uuid, the two path IDs every case endpoint needs (see Authentication).
Endpoints
All paths below are relative to the base URL:
https://verification.didit.me/v3/organization/{organization_id}/application/{application_id}/
| Method | Path | Purpose |
|---|---|---|
GET / POST |
cases/ |
List and search cases / create a case |
GET / PATCH / DELETE |
cases/{case_uuid}/ |
Case detail / update title, priority, due date, tags / delete (204) |
POST |
cases/{case_uuid}/assign/ |
Assign or unassign |
POST |
cases/{case_uuid}/resolve/ |
Resolve, or stage for 4-eyes approval |
POST |
cases/{case_uuid}/approve/ |
Approve a staged resolution (4-eyes) |
POST |
cases/{case_uuid}/reject-approval/ |
Reject a staged resolution (4-eyes) |
POST |
cases/{case_uuid}/reopen/ |
AWAITING_USER or RESOLVED back to OPEN |
POST |
cases/{case_uuid}/request-info/ |
OPEN to AWAITING_USER |
POST |
cases/{case_uuid}/escalate/ |
Move to the blueprint's escalation target |
POST |
cases/{case_uuid}/transfer/ |
Move to another active blueprint |
GET / POST |
cases/{case_uuid}/notes/ |
List / add notes |
PATCH / DELETE |
cases/{case_uuid}/notes/{note_uuid}/ |
Pin or unpin / delete a note |
POST / GET |
cases/{case_uuid}/notes/presign/ |
Upload an attachment / mint a fresh download URL |
GET / POST |
cases/{case_uuid}/links/ |
List / attach a session, business session or transaction |
DELETE |
cases/{case_uuid}/links/{link_uuid}/ |
Detach a link |
GET |
cases/{case_uuid}/events/ |
Audit timeline |
GET |
cases/{case_uuid}/checklist/ |
Checklist items |
PATCH |
cases/{case_uuid}/checklist/{item_uuid}/ |
Tick or untick an item |
GET |
cases/{case_uuid}/aml/ |
AML screening results of the linked sessions |
GET |
cases/{case_uuid}/print/ |
Full-case PDF (presigned URL) |
POST |
cases/{case_uuid}/entity-actions/ |
Act on the subject (status, tags, note, blocklist) |
POST |
cases/duplicates/ |
Find existing cases for a subject before creating one |
GET |
cases/statistics/ |
Analytics: trends, breakdowns, SLA, queue depth |
POST |
cases/bulk/status/ |
Reopen, request info or resolve many cases |
POST |
cases/bulk/assign/ |
Assign or unassign many cases |
GET |
cases/reviewers/ |
Members who can be assigned cases |
* |
cases/blueprints/..., cases/teams/..., cases/routing-rules/... |
Configuration (see Blueprints and Assignment) |
* |
report-templates/..., regulatory-reports/..., cases/{case_uuid}/regulatory-reports/ |
FIU reports (see FIU Reports) |
List Cases
GET .../cases/
| Query param | Description |
|---|---|
status__in |
Comma-separated: OPEN,AWAITING_USER,RESOLVED |
priority |
Comma-separated: LOW,MEDIUM,HIGH |
source__in |
Comma-separated: MANUAL,AML_SCREENING,TRANSACTION_RULE,WORKFLOW_BUILDER |
blueprint |
Blueprint UUID |
assigned_to |
Assignee user UUID |
unassigned |
true = unassigned only, false = assigned only |
tag |
Exact tag |
search |
Matches title, case_number, tag, subject name, subject UUID, subject external ID (vendor_data), linked session UUID, linked transaction UUID |
date_from, date_to |
YYYY-MM-DD, inclusive range on created_at |
due_before |
ISO 8601 datetime, upper bound on due_at |
overdue |
true: only OPEN/AWAITING_USER cases whose due_at has passed |
ordering |
created_at, -created_at (default), due_at, -due_at (nulls last) |
limit, offset |
Page size (default 50) and offset |
Response: {"count", "next", "previous", "results": [CaseListItem]}. count is exact up to 100 and capped at 100 beyond that: page until next is null, never rely on count.
Create Case
POST .../cases/ returns 201 with the full case.
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | Yes | Max 255 chars. The console prefills Manual - <subject name> |
subject |
object | One of subject / links |
{"type": "user" or "business", "uuid": "<didit_internal_id>"}: the didit_internal_id from GET /v3/users/{vendor_data}/ or GET /v3/businesses/{vendor_data}/ |
links |
array | One of subject / links |
[{"entity_type": "session" or "business_session" or "transaction", "entity_uuid": "<uuid>"}]. Without subject, the subject is derived from the first link's owner |
blueprint |
UUID | No | Active blueprint. Seeds the checklist and drives routing, content and resolution rules |
priority |
string | No | LOW, MEDIUM (default), HIGH |
due_at |
datetime | No | SLA deadline. Without it, the blueprint's default_deadline_hours applies if set |
tags |
string[] | No | Free-form tags |
assigned_to |
UUID | No | Member user UUID from GET .../cases/reviewers/ |
review_duplicates |
boolean | No | Default false. When true, creation returns 409 if the subject already has cases, unless separate_incident_reason is set |
separate_incident_reason |
string | No | Max 1000 chars. Why this is a separate incident; saved on the case |
Link IDs: a session's session_id, a business session's session_id, or a transaction's Didit uuid (from POST /v3/transactions/).
import os
import requests
BASE = (
"https://verification.didit.me/v3/organization/"
f"{os.environ['DIDIT_ORG_ID']}/application/{os.environ['DIDIT_APP_ID']}"
)
HEADERS = {"x-api-key": os.environ["DIDIT_API_KEY"], "Content-Type": "application/json"}
response = requests.post(
f"{BASE}/cases/",
headers=HEADERS,
json={
"title": "Manual - Jane Doe",
"priority": "HIGH",
"subject": {"type": "user", "uuid": "8c0e5a3e-1f7b-4d55-9a0e-6b2f0c7d9e11"},
"links": [{"entity_type": "transaction", "entity_uuid": "3b9d2c1a-7e4f-4a6b-8c5d-0e1f2a3b4c5d"}],
"tags": ["review"],
},
timeout=30,
)
response.raise_for_status()
case = response.json()
print(case["uuid"], case["case_number"], case["status"])
const BASE = `https://verification.didit.me/v3/organization/${orgId}/application/${appId}`;
const response = await fetch(`${BASE}/cases/`, {
method: "POST",
headers: { "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({
title: "Manual - Jane Doe",
priority: "HIGH",
subject: { type: "user", uuid: "8c0e5a3e-1f7b-4d55-9a0e-6b2f0c7d9e11" },
}),
});
const caseDetail = await response.json();
Response (201 Created)
{
"uuid": "6f1c2a9e-0b7d-4c3e-9a51-2d8e4f7b1c03",
"case_number": "CS-0042",
"title": "Manual - Jane Doe",
"status": "OPEN",
"resolution": null,
"resolution_notes": null,
"priority": "HIGH",
"source": "MANUAL",
"tags": ["review"],
"subject": {"type": "user", "uuid": "8c0e5a3e-1f7b-4d55-9a0e-6b2f0c7d9e11", "name": "Jane Doe", "external_id": "user-42"},
"assigned_to": null,
"assigned_at": null,
"due_at": null,
"resolved_at": null,
"resolved_by_email": null,
"pending_resolution": null,
"metadata": {},
"blueprint": null,
"notes": [],
"events": [
{
"uuid": "0d4e7c2b-9a1f-4b3c-8e5d-6f7a8b9c0d1e",
"event_type": "CREATED",
"actor_email": null,
"actor_name": null,
"previous_status": null,
"new_status": "OPEN",
"metadata": {},
"created_at": "2026-10-12T09:30:00Z"
}
],
"links": [
{
"uuid": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
"linked_entity_type": "transaction",
"linked_entity_uuid": "3b9d2c1a-7e4f-4a6b-8c5d-0e1f2a3b4c5d",
"linked_entity_summary": {"uuid": "3b9d2c1a-7e4f-4a6b-8c5d-0e1f2a3b4c5d", "txn_id": "tx-0001", "status": "IN_REVIEW"},
"created_at": "2026-10-12T09:30:00Z"
}
],
"links_count": 1,
"notes_count": 0,
"checklist_items": [],
"created_at": "2026-10-12T09:30:00Z",
"updated_at": "2026-10-12T09:30:00Z"
}
Duplicate review before creating
POST .../cases/duplicates/ with {"subject": {"type": "user", "uuid": "..."}} or {"links": [...]} (optional offset) returns {"count", "next_offset", "results": [{uuid, case_number, title, status, priority, created_at}]}, open and resolved cases, at most 20 per page. Then create with review_duplicates: true (and separate_incident_reason when you still want a new case).
Get, Update, Delete
GET .../cases/{case_uuid}/: full case (CaseDetail), with the newest 50 notes, newest 100 events, all links and checklist items.PATCH .../cases/{case_uuid}/: any oftitle,priority,due_at(nullable),tags. LogsPRIORITY_CHANGED,DUE_DATE_CHANGED,TAG_ADDED,TAG_REMOVED.DELETE .../cases/{case_uuid}/: deletes the case, returns204. There is no undelete.
Case Actions
Every action returns the updated CaseDetail (200) unless noted.
| Action | Body | Rules |
|---|---|---|
assign/ |
{"assignee_user_id": "<uuid>"} (or null to unassign) |
The assignee must be a member with case access to the application: list them with GET .../cases/reviewers/ |
resolve/ |
{"resolution": "<resolution>", "note": "..."} (any value from Status Values) |
400 if a non-optional checklist item is incomplete (lists the labels) or the blueprint requires a note and none is given. On a 4-eyes blueprint it stages instead of closing (see below) |
reopen/ |
none | Only from AWAITING_USER or RESOLVED, else 400. Clears resolution, resolution_notes, resolved_at, resolved_by_email |
request-info/ |
none | Only from OPEN and with no resolution pending approval, else 400 |
escalate/ |
none | 400 unless the blueprint has escalation_enabled and an active escalation_blueprint |
transfer/ |
{"target_blueprint": "<uuid>"} |
400 unless the current blueprint has transfer_enabled and the target is a different, active blueprint of the same application. Auto-assigns if the target uses AUTOMATIC assignment |
approve/ |
none | 4-eyes only. 409 if nothing is pending or the linked evidence changed after submission; 403 for the officer who submitted it |
reject-approval/ |
{"notes": "..."} (optional) |
4-eyes only. Clears the staged resolution and returns the case to the maker blueprint. Same 409 / 403 rules |
Resolve and 4-eyes review
r = requests.post(f"{BASE}/cases/{case_uuid}/resolve/", headers=HEADERS,
json={"resolution": "FALSE_POSITIVE", "note": "Documents confirm legitimate activity."},
timeout=30)
case = r.json()
if case["pending_resolution"]:
print("Staged for 4-eyes approval; status is still", case["status"])
else:
print("Resolved:", case["resolution"])
When the case's blueprint sets four_eyes_checker_blueprint, resolve/ does not close the case: status stays OPEN, pending_resolution holds {resolution, resolution_notes, maker_email, maker_name, maker_blueprint_id, staged_at}, the case moves to the checker blueprint's queue and a SUBMITTED_FOR_APPROVAL event is logged. A different officer then calls approve/ (case becomes RESOLVED, credited to the maker) or reject-approval/.
- Changing a checklist answer or adding or removing a link clears the pending decision (
APPROVAL_INVALIDATED); submit a new one. request-info/is blocked while a resolution is pending.- With an API key: a resolution staged by an API key has no recorded maker, so
approve/andreject-approval/both return403for everyone. On 4-eyes blueprints, let a console user submit the resolution (or submit it again), then a second console user approves.
Notes, Attachments, Links, Checklist
Notes: POST .../cases/{case_uuid}/notes/ returns 201.
| Field | Type | Description |
|---|---|---|
comment |
string | Required, plain text, max 10000 chars |
mentioned_emails |
email[] | Must be members with access to the application, else 400 |
is_internal |
boolean | Default false |
attachments |
object[] | Each needs the s3_key returned by the upload endpoint; the note stores what you send, so also pass filename and file_size |
tags |
string[] | Note tags |
reply_to |
UUID | A live note in the same case |
client_reference |
UUID | Idempotency key: an unchanged retry returns the existing note (200); reuse with different content, or after the note was deleted, returns 409 |
GET .../notes/(limitdefault 50,offset): pinned notes first, then newest first.PATCH .../notes/{note_uuid}/with{"is_pinned": true};DELETE .../notes/{note_uuid}/permanently deletes it (204).- Upload:
POST .../notes/presign/asmultipart/form-datawithfile(PDF, JPEG, PNG, WebP, plain text or CSV, max 5 MB) returns201{"s3_key", "url", "filename", "file_size"}. Thes3_keystarts withcase-notes/{case_uuid}/; a note whose attachments3_keybelongs to another case is rejected with400. - Download later:
GET .../notes/presign/?s3_key=<key>returns a fresh{"s3_key", "url", "filename"}(404if the key is not on this case).
Links: GET .../links/ (array), POST .../links/ with {"entity_type", "entity_uuid"} (201; 400 if it belongs to a different subject), DELETE .../links/{link_uuid}/ (204).
Checklist: GET .../checklist/ lists items copied from the blueprint when the case was created; PATCH .../checklist/{item_uuid}/ with {"is_completed": true}.
Other reads: GET .../events/ (paginated, newest first), GET .../aml/ (array of the AML screening results of the sessions and business sessions linked to the case, newest first, each with its status and hits), GET .../print/ (renders a new PDF of the whole case and returns {"url": "<presigned URL>"}; download promptly).
Subject Actions
POST .../cases/{case_uuid}/entity-actions/ acts on the case subject (the user or business):
action |
Extra fields | Effect |
|---|---|---|
set_status |
status: ACTIVE, FLAGGED or BLOCKED |
Changes the user or business status |
add_tags |
tags |
Adds tags to the subject |
add_note |
note |
Adds an internal case note |
add_to_blocklist |
none | Adds the subject to the blocklist |
Send a unique client_reference (UUID) to get an idempotent receipt: the response then includes receipt with status APPLIED or FAILED (a 200 can carry a FAILED receipt, meaning nothing was applied). Reusing a reference with a different payload returns 409. Retry a failed action with retry: true, or POST .../action-receipts/{receipt_uuid}/retry/; list receipts with GET .../action-receipts/ (20 per page, next_offset).
Customer Messages
Ask the verified user for information from a case (fixed email templates only):
| Step | Call |
|---|---|
| Options | GET .../cases/{case_uuid}/requests/options/ returns verified contacts, allowed purposes, languages |
| Preview | POST .../cases/{case_uuid}/requests/preview/ with {"contact_id", "purpose", "language"} returns request_id, expires_at, preview_token and the rendered email |
| Send | POST .../cases/{case_uuid}/requests/ with the same contact_id, purpose, language plus request_id, expires_at, preview_token, within 15 minutes (201) |
| History / cancel | GET .../cases/{case_uuid}/requests/, POST .../requests/{request_uuid}/cancel/ |
purpose: identity_document, proof_of_address, business_document, confirm_details, review_in_progress. language: en (default), es, pt. An actionable request moves the case to AWAITING_USER; review_in_progress leaves the status unchanged. Guide: https://docs.didit.me/console/case-management/customer-messages
Bulk Operations
POST .../cases/bulk/status/with{"case_uuids": [...], "status": "OPEN" | "AWAITING_USER" | "RESOLVED", "resolution": "...", "note": "..."}.OPENreopens,AWAITING_USERrequests info,RESOLVEDresolves (resolutionrequired, else400).POST .../cases/bulk/assign/with{"case_uuids": [...], "assignee_user_id": "<uuid>" or null}. All cases must share one blueprint.
Both return {"updated": 3, "failures": [{"case_uuid": "...", "detail": "..."}]}: each case is evaluated on its own, so one blocked case never rolls back the rest.
Analytics
GET .../cases/statistics/
| Query param | Description |
|---|---|
date_from, date_to |
YYYY-MM-DD, send both. Default: the last 30 days. Max range 731 days |
blueprint, assigned_to |
UUID filters (current values) |
priority, source, status |
One value each |
Response: series ([{date, created, reviewed}]), tables (officer, blueprint, source, status: rows of {key, created, assigned, resolved, threat_rate, false_positive_rate, unresolved, reassignment_rate, escalation_rate, avg_assign_hours, avg_resolve_hours, avg_handling_hours}), totals (same metrics) and queue ({open, overdue}, a live snapshot independent of the date range). 400 for a bad date, date_from after date_to, or a range over 731 days.
Status Values
Case status |
Meaning | Next |
|---|---|---|
OPEN |
Under investigation (also while a 4-eyes resolution is pending) | resolve/, request-info/, escalate/, transfer/ |
AWAITING_USER |
Paused while information is collected from the user | reopen/, resolve/ |
RESOLVED |
Closed with a resolution |
reopen/ |
resolution |
Meaning |
|---|---|
FALSE_POSITIVE |
No concern found: activity was legitimate |
VALID_THREAT |
Concern confirmed: suspicious or fraudulent activity. The only value that feeds the shared fraud index and counts toward threat_rate |
INCONCLUSIVE |
The evidence does not support either verdict |
REQUIREMENTS_NOT_MET |
The subject did not meet the review's requirements (not a fraud verdict) |
REQUIREMENTS_SATISFIED |
The subject met the review's requirements |
source |
Created by |
|---|---|
MANUAL |
Console or API |
AML_SCREENING |
An ongoing-monitoring AML hit, when Create cases from ongoing monitoring hits is on in the workflow's AML settings. A later hit on a subject with an open case attaches to it |
TRANSACTION_RULE |
A transaction rule with the Create case effect (grouping by applicant, or by rule and applicant) |
WORKFLOW_BUILDER |
A session reaching a Create case node |
Event types (events[].event_type): CREATED, STATUS_CHANGED, ASSIGNED, UNASSIGNED, RESOLVED, REOPENED, LINK_ADDED, LINK_REMOVED, NOTE_ADDED, PRIORITY_CHANGED, DUE_DATE_CHANGED, TAG_ADDED, TAG_REMOVED, ESCALATED, TRANSFERRED, CHECKLIST_COMPLETED, REPORT_CREATED, REPORT_FINALIZED, SUBMITTED_FOR_APPROVAL, APPROVAL_GRANTED, APPROVAL_REJECTED, ENTITY_ACTION, APPROVAL_INVALIDATED.
Blueprints (Review Policies)
| Method | Path | Purpose |
|---|---|---|
GET / POST |
cases/blueprints/ |
List (limit default 50, offset) / create (201) |
GET / PATCH / DELETE |
cases/blueprints/{blueprint_uuid}/ |
Detail (adds content_config, creation_sources, receives_escalation_from) / update / delete (204) |
GET |
cases/blueprints/presets/ |
{"results": [{preset_key, name, description, content_config, installed}]} |
POST |
cases/blueprints/presets/install/ |
{"preset_keys": [...]} (omit to install all). 201 {"created": [...]}, or 200 when nothing new |
GET / PUT / DELETE |
cases/blueprints/{blueprint_uuid}/draft/ |
Read / save / discard an unpublished draft |
POST |
cases/blueprints/{blueprint_uuid}/publish/ |
Publish the draft: {"expected_version", "expected_revision"} |
GET |
cases/blueprints/{blueprint_uuid}/history/ |
Published versions |
Create fields:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Max 24 chars |
assignee_pool |
UUID[] | Yes | At least one member user UUID (GET .../cases/reviewers/), else 400 "Pick at least one assignee" |
description |
string | No | Free text |
assignment_method |
string | No | MANUAL (default) or AUTOMATIC (least-loaded authorized reviewer, up to capacity) |
max_active_cases |
integer | No | Capacity per reviewer for automatic assignment (min 1) |
escalation_enabled / escalation_blueprint |
boolean / UUID | No | Both are needed before escalate/ works |
transfer_enabled |
boolean | No | Allows transfer/ |
four_eyes_checker_blueprint |
UUID | No | Setting it turns on 4-eyes review |
require_resolution_note |
boolean | No | Makes note mandatory on resolve |
default_deadline_hours |
integer | No | 1-8760; due_at for new cases |
content_config |
object | No | Case page sections, validated strictly (unknown keys 400) |
is_active |
boolean | No | Default true; only active blueprints accept new cases and transfers |
publish |
boolean | No | Default true. false saves an unpublished draft (version 0) that must be published through .../publish/ |
A blueprint cannot point at itself as escalation target or checker, and cross-referenced blueprints must belong to the same application. PATCH publishes immediately; send expected_version to get 409 instead of overwriting a newer version. Deleting a blueprint does not touch its cases: they keep their blueprint and its settings.
content_config sections: checklist (enabled, optional, items[{label, order}]), aml_control (enabled, financial, identity), identity (enabled, personal_information, applicant_tags, contact_information, risk_overview, poi_documents, poa_documents), verifications (enabled, summary), reporting (enabled, fiu_reports), financial (enabled, payment_methods, transactions). With checklist.optional false, every item must be completed before resolving.
Presets: screening_hit, aml_investigation, suspicious_activity, kyc. Installing copies an editable blueprint (idempotent per key). Presets set no assignment, escalation, transfer or 4-eyes: configure those after installing.
Assignment: Reviewers, Teams, Routing Rules
| Method | Path | Purpose |
|---|---|---|
GET |
cases/reviewers/ |
{"results": [{user_id, email, name}]}: members eligible for cases (503 if access could not be verified; retry) |
GET / POST |
cases/teams/ |
Review teams: {name, description, members[{user_id, availability, capacity, skills, available_from, available_until}], is_active} |
GET / PATCH |
cases/teams/{record_uuid}/ |
Team detail / update |
GET / POST |
cases/routing-rules/ |
Rules: {name, priority, conditions[{field, operator, value}], blueprint, team, required_skills, is_active, publish} |
GET / PATCH |
cases/routing-rules/{record_uuid}/ |
Rule detail / update (expected_version for 409 on stale edits) |
POST |
cases/routing-rules/preview/ |
Test routing for {source, priority, tags} without saving or reserving capacity |
* |
cases/routing-rules/{record_uuid}/draft/, .../publish/, .../history/ |
Same draft lifecycle as blueprints |
Conditions: field is source, priority or tags; operator is equals or in (source, priority) or contains (one tag). Rules are evaluated in priority order on case creation; the first matching active rule picks the blueprint and team. An explicit assigned_to always wins. Guide: https://docs.didit.me/console/case-management/assignment
FIU Reports (Regulatory Reports)
Draft a goAML filing from a case, validate it, finalize it (irreversible), then download the goAML XML for your FIU portal and a PDF for your records.
| Method | Path | Purpose |
|---|---|---|
GET / POST |
report-templates/ |
List / create a template (201) |
GET / PATCH / DELETE |
report-templates/{template_uuid}/ |
Detail / update / delete (reports already created stay) |
GET / POST |
cases/{case_uuid}/regulatory-reports/ |
Reports of a case / create a draft from the case (201) |
GET / POST |
regulatory-reports/ |
All reports (status, report_type, case filters) / standalone create (case and template required) |
GET / PATCH / DELETE |
regulatory-reports/{report_uuid}/ |
Detail / edit / delete. 409 once FINALIZED |
POST |
regulatory-reports/{report_uuid}/validate/ |
{"status", "is_valid", "errors": [{field, message}]}; sets status to VALIDATED or back to DRAFT |
POST |
regulatory-reports/{report_uuid}/finalize/ |
Re-validates, renders XML + PDF, sets FINALIZED. 400 with errors on failure, 409 if not finalizable |
GET |
regulatory-reports/{report_uuid}/download-url/?file=pdf |
{"download_url", "file"}; file is pdf (default) or xml. 409 until finalized |
Template (POST .../report-templates/): name, jurisdiction (ISO 3166-1 alpha-2), report_type, field_config. field_config must contain rentity_id (reporting entity ID from your FIU), submission_code (E electronic or M manual) and currency_code_local (3 letters), else 400 {"errors": [...]} listing the missing ones. Optional: enabled_fields, reporting_person, location, reason, action.
Report from a case (POST .../cases/{case_uuid}/regulatory-reports/): template, or report_type + jurisdiction when no template. Optional transactions (UUIDs; default: the case's linked transactions) and header fields (rentity_id, rentity_branch, submission_code, entity_reference, fiu_ref_number, currency_code_local, submission_date, reporting_person, location, reason, action, activity, narrative {who, what, when, where, why, how}, report_indicators, included_fields). The draft is filled from the template, the case subject, the linked transactions and a five-Ws narrative (SAR, STR, TFR). Validation also requires action, submission_date, at least one transaction and a 2-letter jurisdiction.
report_type |
Report |
|---|---|
SAR |
Suspicious Activity Report |
STR |
Suspicious Transaction Report |
CTR |
Cash Transaction Report |
TTR |
Threshold Transaction Report |
UTR |
Unusual Transaction Report |
IFT |
International Funds Transfer Report |
CBR |
Cross-Border Report |
TFR |
Terrorism Financing Report |
Report status: DRAFT (editable), VALIDATED (passed validation), FINALIZED (files generated; no more edits or deletes). Finalizing a SAR, STR or TFR moves the case's transaction alerts from Pending SAR to SAR filed.
Error Responses
| Code | Meaning | Action |
|---|---|---|
400 |
Invalid request | Read the field errors in the response body |
401 |
The API key has expired | Rotate it or create a new key in Developers > API keys |
403 |
Missing, wrong or revoked API key; a scoped key without access to this endpoint; a request from outside the key's IP allowlist; or not enough credits | Read the message in detail or error: fix the x-api-key header or the key's access, or top up |
403 |
"This API key has no access to this endpoint." |
Named key with custom access: use the primary key or a full-access key |
403 |
"You do not have permission to perform this action." |
{application_id} is not the key's application |
403 |
On approve/ / reject-approval/ |
The maker cannot approve their own resolution; a key-staged resolution has no recorded maker |
404 |
Case, blueprint, note, link, report, template or attachment not found in this application | Check the UUIDs and {application_id} |
409 |
Duplicate review required; nothing pending approval or evidence changed; stale expected_version; note client_reference reused; report already finalized or not finalized yet |
Read detail |
429 |
Rate limit: 300 POST/PATCH/DELETE and 600 GET requests a minute per key | Wait the Retry-After seconds, then retry |
Response Field Reference
CaseListItem / CaseDetail
| Field | Type | Description |
|---|---|---|
uuid |
UUID | Case ID used in every case path |
case_number |
string | Human-readable, e.g. CS-0001 |
title |
string | Case title |
status / resolution / priority / source |
string | See Status Values |
tags |
string[] | Case tags |
subject |
object | {type, uuid, name, external_id}; external_id is the subject's vendor_data |
assigned_to |
object | {uuid, full_name, email} or null |
assigned_at / due_at / resolved_at |
datetime | Timestamps (nullable) |
blueprint |
object | List: {uuid, name}. Detail: the blueprint with content_config |
links_count / notes_count |
integer | Live links and notes |
created_by_email / created_by_name |
string | List only |
application_id / application_name / organization_id / organization_name |
string | List only |
resolution_notes / resolved_by_email |
string | Detail only |
pending_resolution |
object | Detail only. Staged 4-eyes resolution or null |
metadata |
object | Detail only. For example separate_incident_reason |
notes / events / links / checklist_items |
array | Detail only. Newest 50 notes, newest 100 events |
Note, Link, Checklist item
| Object | Fields |
|---|---|
| Note | uuid, comment, actor_email, actor_name, mentioned_emails, is_internal, attachments, tags, created_at, reply_to, is_pinned, client_reference |
| Link | uuid, linked_entity_type, linked_entity_uuid, linked_entity_summary (session_id, session_number, status for sessions; uuid, txn_id, status for transactions), created_at |
| Checklist item | uuid, label, is_completed, completed_by, completed_at, order |
Common Workflows
Work the open queue
1. GET .../cases/?status__in=OPEN,AWAITING_USER&ordering=due_at -> most urgent first
2. GET .../cases/{case_uuid}/ -> subject, links, checklist
3. GET .../cases/{case_uuid}/aml/ -> AML results of linked sessions
4. PATCH .../cases/{case_uuid}/checklist/{item_uuid}/ {"is_completed": true}
5. POST .../cases/{case_uuid}/notes/ {"comment": "Reviewed source of funds."}
6. POST .../cases/{case_uuid}/resolve/ {"resolution": "FALSE_POSITIVE", "note": "..."}
If pending_resolution is set -> a second console user approves (4-eyes)
Open a case for a flagged transaction
1. POST /v3/transactions/ returns uuid and status IN_REVIEW
2. POST .../cases/duplicates/ {"links": [{"entity_type": "transaction", "entity_uuid": "<uuid>"}]}
3. Existing open case -> POST .../cases/{case_uuid}/links/ with the transaction
No case -> POST .../cases/ {"title": "...", "links": [...], "review_duplicates": true}
File a SAR from a case
1. POST .../report-templates/ {"name": "US SAR", "jurisdiction": "US", "report_type": "SAR",
"field_config": {"rentity_id": "FIU-US-00123", "submission_code": "E", "currency_code_local": "USD"}}
2. POST .../cases/{case_uuid}/regulatory-reports/ {"template": "<template_uuid>"} -> DRAFT
3. PATCH .../regulatory-reports/{report_uuid}/ {"action": "...", "submission_date": "...", "narrative": {...}}
4. POST .../regulatory-reports/{report_uuid}/validate/ -> fix every errors[].field
5. POST .../regulatory-reports/{report_uuid}/finalize/ -> FINALIZED (irreversible)
6. GET .../regulatory-reports/{report_uuid}/download-url/?file=xml -> upload to the FIU portal
Bootstrap case management
1. GET .../cases/reviewers/ -> member user_id values
2. POST .../cases/blueprints/presets/install/ {"preset_keys": ["aml_investigation", "kyc"]}
3. PATCH .../cases/blueprints/{uuid}/ {"assignee_pool": ["<user_id>"], "assignment_method": "AUTOMATIC"}
4. Turn on case creation in the AML settings, transaction rules or workflow builder (Business Console)
Utility Scripts
manage_cases.py: list, inspect, create, resolve, annotate cases and pull analytics from the command line.
# Requires: pip install requests
export DIDIT_API_KEY="your_api_key"
export DIDIT_ORG_ID="your_organization_uuid"
export DIDIT_APP_ID="your_application_uuid"
python scripts/manage_cases.py list --status OPEN,AWAITING_USER --ordering due_at
python scripts/manage_cases.py get 6f1c2a9e-0b7d-4c3e-9a51-2d8e4f7b1c03
python scripts/manage_cases.py create --title "Manual - Jane Doe" --user 8c0e5a3e-1f7b-4d55-9a0e-6b2f0c7d9e11 --priority HIGH
python scripts/manage_cases.py create --title "Flagged payout" --transaction 3b9d2c1a-7e4f-4a6b-8c5d-0e1f2a3b4c5d
python scripts/manage_cases.py resolve 6f1c2a9e-0b7d-4c3e-9a51-2d8e4f7b1c03 --resolution FALSE_POSITIVE --note "Verified"
python scripts/manage_cases.py note 6f1c2a9e-0b7d-4c3e-9a51-2d8e4f7b1c03 --comment "Called the customer"
python scripts/manage_cases.py stats --date-from 2026-09-01 --date-to 2026-09-30