Concepts
The mental model
Five nouns and one chain. Once these click, the rest of the docs are reference.
The flow
agent calls a gated tool
│
▼
POST /approvals ──► policy evaluation (JSONLogic, no LLM)
│ │
│ auto-approve / auto-reject / notify
▼ ▼
pending ────────► human decides (Slack / web inbox)
│ │
▼ ▼
agent unblocks ◄──── approved / edited / rejected
│
▼
tool executes ──► POST /:id/execution ──► append-only hash chainAgent
Your AI process. Each agent has its own API key (pli_live_…) — revocable individually without rotating the rest. Agents are soft-deleted on revoke (forensics preserved), never hard-deleted.
Tool
A function that can touch the real world (pay, delete, send). Registered with its declared risk. A tool flagged requires_approval=false short-circuits to auto-approve with an honest provenance flag.
Policy
A declarative rule (JSONLogic, ~12 whitelisted operators, no LLM in the critical path) deciding what happens when an agent wants a tool: auto-approve, auto-reject, or notify → a human in the matched approver_group decides. Policies are versioned (immutable snapshots) and can run in shadow mode to test in production without affecting traffic.
Approval
One gated call. It carries the tool, args, originator and context. A human can approve, edit & approve (the edited args are what execute — never the originals), or reject with a reason. Authorization is enforced identically across REST, web inbox, and Slack: only an admin or a member of the policy's approver group (or an active delegate) can decide; auditors are read-only.
The hash chain
Every event lands in an append-only table, each row chained by SHA-256 over the previous event's canonical payload, per tenant. Anti-UPDATE/DELETE/TRUNCATE triggers enforce immutability at the database. The args that were approved are anchored by hash, so rewriting them after the fact breaks the chain.