Adapters and the API

Run standalone today, embed in any app tomorrow.


The engine is domain-blind. It approves a reference (targetType + targetId) and never contains another app’s business logic. Two modes share one submission endpoint.

Standalone mode (today)

Form submissions use the built-in standalone.request target: the form’s values become the payload, the payload feeds conditional rules, and the decision is recorded on the target.

POST /requests
{ "policyId": "…", "title": "Field team laptops",
  "payload": { "amount": 9200, "category": "it-equipment" } }

Adapter mode (the suite contract)

A host app registers one adapter and hands off its own objects:

interface ApprovalTargetAdapter {
  targetType: string; // e.g. "document.transmittal"
  loadContext(orgId, targetId): Promise<{ code; title; href; snapshot }>;
  applyDecision(orgId, targetId, finalAction, comment?): Promise<void>;
}

loadContext gives the engine display fields and the rule snapshot; applyDecision is called exactly once on the terminal outcome. This is the entire integration surface: the host app keeps its own workflow, and delegates only the act of approval.

Key endpoints

Method and pathPurpose
GET /policies · POST /policiesList and define policies
POST /requests · GET /requestsSubmit and list requests
GET /requests/:idDetail: stage map, payload, timeline
GET /inbox · POST /assignments/:id/decideYour queue and your decision
POST /assignments/bulk-decideGuardrailed bulk approve
POST /external/decideMagic-link + code decisions (public)
GET /activity · GET /analytics/summaryAudit feed and dashboard rollup

Realtime clients join org and user rooms over Socket.IO and receive inbox.changed, request.changed, and notifications.changed signals.