Skip to content

Agent Rail

Deterministic, policy-governed operational agents for KYC routing, compliance triage, reconciliation break handling, and evidence pack assembly.

The Agent Rail provides a control plane for deterministic operational agents that automate case routing, alert triage, reconciliation handling, and evidence assembly. Unlike LLM-powered agents, these agents are fully deterministic — every input produces a predictable, auditable output governed by explicit rules and state machines.

Agents operate within a multi-tenant context and emit structured domain events that integrate with the platform’s event-driven architecture. All agent decisions are recorded for audit trail compliance (SOC2, AAOIFI).

/api/v1/agents
AgentCodePurpose
KYC Case Routerkyc_routerRoutes KYC/KYB cases to the correct review queue
Compliance Alert Triagecompliance_triageNormalizes and triages AML/sanctions/PEP alerts
Reconciliation Break Handlerrecon_breakClassifies and routes reconciliation breaks
Evidence Pack Builderevidence_packAssembles evidence packs for audit and compliance

These agents wrap pure-function financial statement generators with tenant isolation, audit trail events, and correlation tracking. Same inputs always produce identical output.

AgentCodePurposeStandard
Trial Balance Generatortrial_balance_generatorGenerates Trial Balance from ledger snapshotAAOIFI FAS-1
Balance Sheet Generatorbalance_sheet_generatorGenerates Balance Sheet (Assets = Liabilities + Equity)AAOIFI FAS-1
Income Statement Generatorincome_statement_generatorGenerates Income Statement (Revenue - Expenses)AAOIFI FAS-3/4
Zakat Calculation Generatorzakat_calculation_generatorCalculates Zakat (2.5% of zakatable wealth above nisab)AAOIFI SS-9

Non-Deterministic Agents (Advisory, AI-Assisted)

Section titled “Non-Deterministic Agents (Advisory, AI-Assisted)”

These agents produce advisory content that requires human review before use. They never compute financial numbers — all figures are sourced from deterministic report generators.

AgentCodePurpose
Report Commentaryreport_commentaryGenerates narrative commentary for financial reports
Anomaly Detectoranomaly_detectorStatistical anomaly detection with z-score analysis
Report Explainerreport_explainerPlain-language report explanations for stakeholders
POST /api/v1/agents/execute

Execute any agent by type. The agent_type field selects the agent.

Request Body:

{
"tenant_id": "tenant_001",
"workspace_id": "ws_001",
"agent_type": "kyc_router",
"correlation_id": "corr_abc123",
"input": {
"case_id": "case_001",
"case_state": "open",
"case_type": "kyc_review",
"kyc_review": {
"completeness_status": "complete",
"risk_band": "high",
"jurisdiction_code": "SA",
"missing_requirements": []
}
}
}

Response:

{
"success": false,
"outcome": "approval_required",
"output_summary": "Routed to kyc_manual_review. Approval required: high risk band",
"events": [
{
"event_type": "case.routed",
"entity_type": "case",
"entity_id": "case_001",
"payload": {
"assignee_queue": "kyc_manual_review",
"new_state": "in_review",
"reason": "high_risk"
}
},
{
"event_type": "approval.requested",
"entity_type": "approval_request",
"entity_id": "",
"payload": {
"approval_type": "case_transition",
"case_id": "case_001",
"reason": "High risk KYC case requires manual review"
}
}
],
"policy_decision": null
}
POST /api/v1/agents/kyc-router

Route a KYC case based on completeness, risk, and jurisdiction.

Routing Rules:

RuleConditionQueueState
1Incomplete applicationkyc_missing_infowaiting_for_info
2High risk or EDD jurisdiction (IR, KP, SY, AF, MM, YE)kyc_manual_reviewin_review + approval
3Medium riskkyc_standard_reviewin_review
4Low risk + completekyc_fast_track_reviewin_review

Request Body:

{
"tenant_id": "tenant_001",
"workspace_id": "ws_001",
"input": {
"case_id": "case_001",
"case_state": "open",
"case_type": "kyc_review",
"kyc_review": {
"completeness_status": "complete",
"risk_band": "low",
"jurisdiction_code": "SA",
"missing_requirements": []
}
}
}
POST /api/v1/agents/compliance-triage

Triage a compliance alert (sanctions, PEP, adverse media).

Triage Rules:

RuleConditionOutcome
1Strong sanctions hitEscalated, case created, approval required
2PEP moderate/strong hitRouted to manual review
3Adverse media below thresholdTriaged
4Matching cleared fingerprintMarked false positive
5Critical severityRequires approval before closure

Request Body:

{
"tenant_id": "tenant_001",
"workspace_id": "ws_001",
"input": {
"alert_id": "alert_001",
"alert_state": "new",
"alert_type": "screening",
"severity": "high",
"screening_type": "sanctions",
"hit_strength": "strong",
"hit_count": 1,
"jurisdiction_code": "SA"
}
}
POST /api/v1/agents/recon-break

Classify and route a reconciliation break.

Classification Rules:

RuleConditionAction
1Timing difference detectedQueue: recon_timing_queue
2Duplicate transactionQueue: recon_duplicate_queue
3Missing legQueue: recon_missing_leg_queue
4Above materiality thresholdEscalate
5Timing difference within auto-close ageAuto-close

Request Body:

{
"tenant_id": "tenant_001",
"workspace_id": "ws_001",
"input": {
"break_id": "break_001",
"break_type": "amount_mismatch",
"source_amount": 10000,
"target_amount": 10050,
"source_date": "2025-01-15",
"target_date": "2025-01-16",
"currency": "SAR"
}
}
POST /api/v1/agents/evidence-pack

Assemble an evidence pack for audit or compliance review.

Pack Types: kyc, compliance, reconciliation, audit, shariah_review

Request Body:

{
"tenant_id": "tenant_001",
"workspace_id": "ws_001",
"input": {
"pack_type": "kyc",
"entity_id": "case_001",
"entity_type": "case",
"items": [
{ "type": "document", "ref": "doc_passport_001" },
{ "type": "screening_result", "ref": "screen_001" }
]
}
}

Response:

{
"success": true,
"outcome": "success",
"output_summary": "Evidence pack assembled: 2/3 mandatory items (67% complete)",
"events": [
{
"event_type": "evidence_pack.assembled",
"entity_type": "evidence_pack",
"entity_id": "ep_001",
"payload": {
"new_state": "incomplete",
"completeness_score": 67,
"missing_items": ["proof_of_address"]
}
}
],
"policy_decision": null
}
POST /api/v1/agents/validate-transition

Validate whether a state machine transition is allowed.

Request Body:

{
"from_state": "open",
"to_state": "in_review",
"entity_type": "case"
}

Response:

{
"allowed": true,
"reason": "Transition allowed",
"from_state": "open",
"to_state": "in_review"
}
POST /api/v1/agents/evaluate-policy

Evaluate a Cerbos policy decision for a subject-resource-action tuple.

Request Body:

{
"subject": { "id": "user_001", "roles": ["compliance_officer"] },
"resource": { "id": "case_001", "type": "case", "tenant_id": "tenant_001" },
"action": "approve"
}

Response:

{
"id": "pd_001",
"decision": "ALLOW",
"pdp_type": "SPDP",
"policy_scope": "agent_rail",
"matched_rules": ["compliance_officer_can_approve_cases"],
"obligations": ["log_approval_decision"]
}

All agent endpoints require a tenant context:

FieldTypeRequiredDescription
tenant_idstringYesTenant identifier
workspace_idstringYesWorkspace identifier
agent_idstringNoAgent identifier (auto-assigned)
correlation_idstringNoCorrelation ID for tracing (auto-generated if omitted)

All agents return a standard result structure:

FieldTypeDescription
successbooleanWhether the outcome was successful
outcomestringsuccess, approval_required, error
output_summarystringHuman-readable summary
eventsAgentEvent[]Domain events emitted
policy_decisionPolicyDecisionPolicy evaluation result (if applicable)

Cases and alerts follow explicit state machines. Use the /agents/validate-transition endpoint to check whether a transition is valid before attempting it.

Case States: open -> in_review -> approved | rejected | waiting_for_info -> closed

Alert States: new -> normalized -> triaged | under_review | escalated | false_positive -> closed

EventDescription
case.routedCase assigned to a queue
case.transitionedCase state changed
alert.normalizedAlert normalized from raw
alert.triagedAlert triage completed
alert.escalatedAlert escalated
approval.requestedApproval workflow initiated
task.createdFollow-up task created
evidence_pack.assembledEvidence pack built

Agent rail resources are protected by Cerbos ABAC policies:

ResourcePolicy File
casepolicies/agent-rail/resource_policies/case.yaml
alertpolicies/agent-rail/resource_policies/alert.yaml
evidence-packpolicies/agent-rail/resource_policies/evidence-pack.yaml

The agent rail uses 33 dedicated tables prefixed with ar_. Key tables:

TablePurpose
ar_agentsAgent registry
ar_casesCase management
ar_alertsAlert tracking
ar_evidence_packsEvidence assembly
ar_reconciliation_breaksBreak tracking
ar_policy_decisionsPolicy audit trail
ar_timeline_eventsFull event audit
import IOF from "@iof/sdk";
const client = new IOF({ apiKey: process.env.IOF_API_KEY });
const result = await client.agents.routeKYC(
{ tenantId: "tenant_001", workspaceId: "ws_001" },
{
case_id: "case_001",
case_state: "open",
case_type: "kyc_review",
kyc_review: {
completeness_status: "complete",
risk_band: "low",
jurisdiction_code: "SA",
missing_requirements: [],
},
},
);
from iof_sdk import IOF
client = IOF(api_key=os.environ["IOF_API_KEY"])
result = client.agents.route_kyc(
tenant_id="tenant_001",
workspace_id="ws_001",
input={
"case_id": "case_001",
"case_state": "open",
"case_type": "kyc_review",
"kyc_review": {
"completeness_status": "complete",
"risk_band": "low",
"jurisdiction_code": "SA",
"missing_requirements": [],
},
},
)
client := iof.NewClient(os.Getenv("IOF_API_KEY"))
ctx := iof.AgentContext{
TenantID: "tenant_001",
WorkspaceID: "ws_001",
}
result, err := client.Agents.RouteKYC(ctx, map[string]interface{}{
"case_id": "case_001",
"case_state": "open",
"case_type": "kyc_review",
"kyc_review": map[string]interface{}{
"completeness_status": "complete",
"risk_band": "low",
"jurisdiction_code": "SA",
},
})