Case Status State Machine
Every compliance case follows a deterministic state machine managed by the Temporal workflow. The workflow enforces valid transitions and logs every state change to the audit trail.
State Diagram
State Definitions
| Status | Description | Triggered By |
|---|---|---|
CREATED | Case record exists in PostgreSQL, Temporal workflow starting | POST /api/cases |
PRE_INVESTIGATION | OSINT investigation runs before document collection -- registry agents fetch official documents (KBO, NBB, INPI, ARES), OSINT sources are queried, evidence is gathered and cross-referenced | Workflow initialization |
DOCUMENT_GAP_ANALYSIS | System computes which documents the customer actually needs to provide by analyzing what was auto-retrieved from registries, what OSINT found, and what gaps remain against the template requirements. Risk-adaptive automation tier is computed. | After pre-investigation completes |
REQUIREMENTS_REVIEW | Officer reviews computed document requirements before the portal opens. Behavior depends on the automation tier (see Requirements Review Gate below). | After gap analysis completes |
AWAITING_DOCUMENTS | Waiting for customer to upload documents via portal. Portal shows only the documents that could not be auto-retrieved. | Requirements approved (or auto-advanced), or follow-up loop |
DOCUMENTS_RECEIVED | Customer submitted documents, processing about to begin | documents_submitted signal from portal |
PROCESSING | Active processing -- Docling conversion or post-upload OSINT investigation running | Internal workflow transition |
VALIDATING_DOCUMENTS | AI agent validating uploaded documents against requirements | After Docling conversion |
GENERATING_TASKS | AI agent analyzing investigation results to suggest follow-up tasks | After OSINT + MCC classification |
REVIEW_PENDING | All automated processing complete, waiting for officer decision | After task generation |
FOLLOW_UP_REQUIRED | Officer requested additional information from customer | Officer follow_up decision |
APPROVED | Case approved by compliance officer (terminal) | Officer approve decision |
APPROVED_WITH_RESTRICTIONS | Case approved with merchant restrictions (volume caps, secondary review flag, restriction rationale) attached (terminal) | Officer approve_with_restrictions decision |
REJECTED | Case rejected by compliance officer (terminal) | Officer reject decision |
ESCALATED | Case escalated for senior review (terminal) | Officer escalate decision |
FAILED | Case failed due to timeout or max iterations (terminal) | Timeline exceeded or iteration limit |
Terminal States
Five states are terminal -- the workflow completes and no further transitions occur:
- APPROVED -- Compliance requirements met, customer cleared
- APPROVED_WITH_RESTRICTIONS -- Customer cleared but with conditions (e.g. monthly volume cap, mandatory periodic review). The
MerchantRestrictionspayload and itsrestriction_reason(EU AI Act Art. 13) are persisted with the decision. - REJECTED -- Compliance requirements not met, customer denied
- ESCALATED -- Case requires senior compliance officer review
- FAILED -- Administrative failure (timeout or iteration limit reached)
Pre-Investigation Phase
The pre-investigation phase is the key architectural change introduced in ADR-0018 (Dynamic Document Requirements). Instead of opening the portal with a static list of required documents, the system first runs an OSINT investigation to determine what it can already retrieve automatically from government registries and public sources.
CREATED -> PRE_INVESTIGATION -> DOCUMENT_GAP_ANALYSIS -> REQUIREMENTS_REVIEW -> AWAITING_DOCUMENTS
PRE_INVESTIGATION
During this state, the system:
- Runs all applicable registry agents (KBO, NBB, INPI, ARES, etc.) based on the company's country
- Downloads official documents (PDFs, financial statements) directly from registries and stores them in MinIO
- Runs OSINT data collection (sanctions, PEP, adverse media, web presence)
- Cross-references all gathered data points (addresses, directors, legal form, capital) to detect discrepancies
DOCUMENT_GAP_ANALYSIS
After evidence collection, the system:
- Compares auto-retrieved evidence against the template's document requirements
- Identifies which requirements are already satisfied by registry data
- Computes which documents the customer must still provide (typically just Director ID)
- Classifies discrepancies by severity (LOW, MEDIUM, HIGH)
- Computes the automation tier for the requirements review gate
Requirements Review Gate
The REQUIREMENTS_REVIEW state implements a risk-adaptive gate controlled by the automation tier. The tier is computed per-case based on risk signals detected during pre-investigation:
| Condition | Tier | Gate Behavior |
|---|---|---|
| No HIGH discrepancies, EBA risk LOW/MEDIUM, no PEP, no adverse events | AUTONOMOUS | Portal opens immediately -- no officer wait |
| Minor discrepancies OR EBA risk MEDIUM-HIGH | ASSISTED | Portal opens after 15-minute auto-release if officer does not act |
| ANY: PEP detected, adverse events, complex ownership, EBA HIGH/VERY_HIGH | FULL_REVIEW | Workflow waits indefinitely for officer signal |
| Tenant-level override set to FULL_REVIEW | FULL_REVIEW | Always -- for regulated clients |
The tier is computed upward only: a per-case computation can escalate above the tenant default but never relax below it. If risk assessment or gap analysis fails, the system defaults to FULL_REVIEW (fail-safe).
At the FULL_REVIEW gate, the officer sees:
- All pre-gathered evidence and registry documents
- Cross-reference results with discrepancy highlights
- The proposed document list for the customer
- Risk assessment summary
The officer can add, remove, or modify requirements and add custom notes for the customer before the portal opens.
The Iteration Loop
The core compliance loop operates between AWAITING_DOCUMENTS and REVIEW_PENDING:
AWAITING_DOCUMENTS -> DOCUMENTS_RECEIVED -> PROCESSING -> VALIDATING_DOCUMENTS
-> PROCESSING -> GENERATING_TASKS -> REVIEW_PENDING -> [decision]
If the officer selects "Follow-up", the workflow transitions through FOLLOW_UP_REQUIRED back to AWAITING_DOCUMENTS with a new set of tasks for the customer. This loop can repeat up to max_iterations times (default: 5).
Validation Bounce-Back
A special sub-loop exists within the iteration: if document validation fails (e.g., the customer uploaded a bank statement instead of a certificate of incorporation), the workflow automatically generates re-upload tasks and returns to AWAITING_DOCUMENTS without reaching the officer. This is tracked in the audit log as a validation_bounce_back event.
Guards and Timeouts
| Guard | Default | Behavior |
|---|---|---|
max_iterations | 5 | Workflow transitions to FAILED after 5 complete iterations |
max_timeline_days | 60 | If no documents submitted within the timeline, workflow fails |
| Document validation | per-template | Required documents must pass AI validation to proceed |
| Requirements review | per-case tier | AUTONOMOUS: immediate, ASSISTED: 15min auto-release, FULL_REVIEW: indefinite wait |
Audit Trail
Every state transition generates an audit event with:
{
"event_type": "status_changed",
"details": { "to": "PRE_INVESTIGATION" },
"timestamp": "2026-04-02T10:30:00Z"
}
Additional audit events are logged (via self._log_audit(...) into in-memory state, plus some via the persist_audit_event activity). The full set emitted by ComplianceCaseWorkflow includes:
case_created-- Initial case creationrequirements_computed-- Document requirements computed with covered/required/escalated counts and automation tierrequirements_auto_approved-- Gate auto-advanced (reason:autonomous_tierorassisted_timeout)requirements_approved-- Requirements gate cleared (officer signal for FULL_REVIEW, or auto for AUTONOMOUS/ASSISTED)documents_received-- Customer submitted documentsdocuments_skipped-- Officer used "Skip & Continue" to bypass the portalmcc_classified-- MCC code assigned (andmcc_reclassifiedwhen an officer changes it)nace_mcc_reconciliation-- Declared NACE vs inferred MCC divergence checkquality_scored(plusinvestigation_quality_low/investigation_quality_critical) -- LLM-as-judge quality scoringsynthesis_refreshed-- Investigation summary refreshed after document uploadtasks_generated-- Follow-up tasks createdofficer_decision-- Officer action with decision type and reasonrestrictions_applied-- Merchant restrictions attached onapprove_with_restrictionsvalidation_bounce_back-- Documents failed validation, auto-loopingfollow_up_tasks_completed-- Customer satisfied outstanding follow-up taskstrust_capsule_assembled-- Customs Shield Trust Capsule built on a terminal decisiontimeline_exceeded-- Case timed outmax_iterations_reached-- Iteration limit hitfollow_up_requested-- Officer sent case back to customer
The persist_audit_event activity additionally writes investigation_completed to PostgreSQL when each OSINT/KYC investigation finishes.
Implementation
The state machine is implemented directly in the Temporal workflow class (ComplianceCaseWorkflow). All workflow state lives in a single WorkflowState dataclass (app/models/workflow_state.py) held as self._state; the current status is the string enum value self._state.status. The workflow uses Temporal's wait_condition for blocking waits (document submission, officer decision, requirements review) and standard control flow (while, if/elif) for state transitions.
The pre-investigation phase is orchestrated directly inside the workflow's run method, which dispatches these activities in order:
registry_and_documents-- runs registry agents (KBO, NBB, INPI, ARES, etc.) and downloads official documents (defined inapp/workflows/registry_activity.py)run_osint_investigation-- full OSINT collection and synthesis (via the_run_kyb_investigationhelper)collect_registry_documents-- fallback document collection whenregistry_and_documentsreturned no downloadscross_reference_evidence-- builds the corroboration matrix and flags discrepanciesanalyze_document_gaps-- computes coverage, customer-required documents, and the automation tier
The tier-based requirements gate is not a separate activity. It is implemented inline in run() using workflow.wait_condition(lambda: self._state.requirements_approved): AUTONOMOUS sets the flag immediately, ASSISTED waits with a 15-minute timeout before auto-approving, and FULL_REVIEW waits indefinitely for the signal_requirements_approved signal.
See ADR-0018 for the architectural rationale and Temporal Workflows for implementation details.