Docs

One call before the irreversible one

Interlok holds an action your agent cannot undo, a named human approves it in Slack, and you get a signed record your auditor can verify without us. Four HTTP calls, no model in the decision path.

Step 1

Mint a runtime key

In the console, open Keys and mint a runtime key. It is shown once and stored as a hash. Keys are scoped to one workspace and can be revoked instantly.

shell
export INTERLOK_KEY=ilk_live_...

Step 2

Write the policy

YAML, first match wins, evaluated deterministically. Anything unclassified fails closed to the highest tier — an unknown action is treated as irreversible until you say otherwise.

policy.yaml
version: 1
default: gate

rules:
  - name: reads are free
    actors: ["agent:*"]
    actions: ["read.*", "list.*", "search.*"]
    decision: allow

  - name: money movement needs two humans
    targets: ["stripe/*"]
    actions: ["refund.*", "payout.*"]
    decision: gate
    approvals_required: 2

  - name: never drop production data
    targets: ["db/prod/*"]
    actions: ["drop.*", "truncate.*"]
    decision: deny

Step 3

Call the gate before you act

Same call from any runtime. If the verdict is hold, do nothing until a human determines it.

typescript
import { Interlok } from "@interlok/sdk";

const interlok = new Interlok({ apiKey: process.env.INTERLOK_KEY! });

const verdict = await interlok.gate({
  actor: "agent:billing-copilot",
  target: "stripe/live",
  action: "refund.create",
  payload: { charge: "ch_123", amount_cents: 24000 },
  idempotency_key: task.id,
});

if (verdict.decision !== "allow") {
  // hold -> a named human decides in Slack; deny -> stop.
  return verdict;
}

await stripe.refunds.create({ charge: "ch_123" });
await interlok.report({ hold_id: verdict.hold_id, outcome: "applied" });
curl
curl -sS https://guard-rails-signed.lovable.app/api/public/v1/gate \
  -H "authorization: Bearer $INTERLOK_KEY" \
  -H "content-type: application/json" \
  -d '{
    "actor": "agent:billing-copilot",
    "target": "stripe/live",
    "action": "refund.create",
    "payload": { "charge": "ch_123", "amount_cents": 24000 },
    "idempotency_key": "task_9f2"
  }'

Using MCP? Wrap the server once and every tool call is gated without touching agent code.

mcp.json
{
  "mcpServers": {
    "stripe": {
      "command": "npx",
      "args": ["-y", "@interlok/mcp", "--", "npx", "-y", "@stripe/mcp"],
      "env": { "INTERLOK_KEY": "ilk_live_..." }
    }
  }
}

Step 4

Let a human determine it, then verify the record

Held actions land in Slack with the actor, target, action and the exact payload fingerprint. An agent may never approve an agent: the determination is signed by a human-held key that never leaves their browser. Every entry is hash-chained, so you can verify the record yourself.

curl
curl -sS https://guard-rails-signed.lovable.app/api/public/v1/verify/led_01H... \
  -H "authorization: Bearer $INTERLOK_KEY"

API surface

Four documented endpoints. That is the whole integration.

POST/api/public/v1/gateAsk before acting. Returns allow, deny or hold with a hold id.
GET/api/public/v1/hold/:idPoll a held action until a human determines it, or it expires.
POST/api/public/v1/reportTell Interlok the action was applied, so the ledger entry is complete.
GET/api/public/v1/verify/:entryVerify a ledger entry's hash chain and human signature without us.

When Interlok is unreachable

Each workspace picks a stance. fail_closed means an unreachable gate blocks the action — the default for anything irreversible. fail_open means it proceeds and is recorded as ungated. You can start in observe mode, where nothing is blocked and every decision is still recorded, then switch on enforcement once the log looks right.