Quickstart
Gate a risky agent action behind a human approval — run it locally with no account, or wire the SDK to the hosted API and approve in Slack.
Seal puts a human in the loop for the actions your agent should not take alone. A policy decides whether a human is needed, the call pauses until someone answers, and every decision is written to a tamper-evident audit chain.
There are two ways in. Pick the one that matches where you are.
Start with no account
If your agent reaches its tools over MCP, you do not have to sign up for anything. The proxy runs the policy engine, the approval prompt and the audit log on your own machine:
npx @seal-dev/mcp-proxy --config seal.config.tsYour agent's code does not change — you point the MCP client at the proxy instead of at
the upstream server. See Local mode for the config, the terminal
prompt and --verify.
Local mode is one developer at one terminal, and it is complete for that. A second reviewer, an agent running in CI, or an auditor who needs more than a JSONL file is what the hosted product is for — and switching is one added block in the same config.
Or gate a call from your own code
The rest of this page uses the SDK against the hosted API, which is the path to take when the call you want to gate is a function rather than an MCP tool. It ends with your first Slack approval.
1. Install the SDK
npm install @seal-dev/sdkThe SDK is ESM + CJS and ships its own types. It talks to the REST API over fetch, so it
runs on Node 18+ or any runtime with a global fetch.
2. Create a gate
You need two things: an API key with the approvals:write and approvals:read
scopes, and the base URL of your Seal API.
import { createGate } from "@seal-dev/sdk";
export const gate = createGate({
apiKey: process.env.SEAL_API_KEY!,
baseUrl: process.env.SEAL_BASE_URL!, // e.g. https://api.your-org.com
});SEAL_API_KEY=sk_live_...
SEAL_BASE_URL=https://api.your-org.com3. Require approval before acting
Wrap the risky action. require() creates the approval, then long-polls until it settles.
It resolves with the approved request and throws a typed error on rejection,
expiry, or timeout — so the happy path is a straight line.
import { ApprovalRejectedError } from "@seal-dev/sdk";
import { gate } from "./gate.js";
async function wireTransfer(to: string, amount: number) {
try {
await gate.require({
action: "wire.transfer",
policyKey: "payments.high_value",
payload: { to, amount },
context: { reason: "Vendor invoice #4471 auto-paid by finance agent" },
});
// Reached only after a human approves.
await bank.send({ to, amount });
} catch (err) {
if (err instanceof ApprovalRejectedError) {
console.log("Rejected:", err.feedback);
return;
}
throw err;
}
}action— what the agent wants to do.policyKey— which policy decides the verdict.payload— the data the policy evaluates and the reviewer sees.context— the agent's reasoning, shown to the reviewer (never evaluated).
4. Wire the policy
A policy attached to payments.high_value decides whether this action
needs a human, and who approves. The example below auto-allows small transfers and routes
anything over 1000 to Slack:
{
"match": { "action": "wire.transfer" },
"rules": [
{
"when": [{ "field": "payload.amount", "op": "gt", "value": 1000 }],
"then": "require_approval"
},
{ "when": [], "then": "allow" }
],
"approvers": {
"channel": "slack",
"users": ["U0123ABC"],
"teams": [],
"escalation": [],
"timeoutSeconds": 900,
"onTimeout": "deny"
}
}If no policy matches, Seal fails closed: the verdict is
require_approval, never allow. You can never accidentally ship a permissive default.
Policies are managed in the dashboard. For a local dev instance, pnpm db:seed installs
exactly this policy under the key payments.high_value.
5. Approve in Slack
Call wireTransfer("acct_999", 5000). Because 5000 > 1000, the policy returns
require_approval, a card appears in Slack with Approve / Reject, and your
require() call is still awaiting. Click Approve — the promise resolves and the
transfer runs. Reject it and ApprovalRejectedError is thrown with the reviewer's feedback.
That is the whole loop. Next:
- Policy authoring — match rules, condition operators, escalation, timeouts.
- SDK reference —
requireoptions,resume, error types. - Webhooks — react to decisions out of band.
- Audit export — pull the tamper-evident log for compliance.