Quickstart
Put AgentPlane in front of one tool in about ten minutes. You will register an agent, send its first action, and see it allowed, held or blocked.
Quickstart
- 1Create a workspace. It starts with eight recommended policies.
- 2In the console, open Agents and register one. List only the tools it needs, such as
kb.*oremail.send. Copy the key; it is shown once. - 3Before your agent calls a protected tool, send the action to evaluate. Here is the smallest possible request:
curl -X POST https://your-host/api/v1/action-requests/evaluate \
-H "Authorization: Bearer $AGENTPLANE_KEY" \
-H "Content-Type: application/json" \
-d '{
"initiating_principal": "maya.chen@example.com",
"objective": "Resolve support case 48213",
"action": { "type": "kb.article.read", "operation": "read" },
"resource": { "type": "kb_article", "id": "kb-1182",
"sensitivity": "public", "environment": "sandbox" }
}'{
"request": { "id": "req_4f1c09a2be71", "payload_hash": "9b0e...c41d", "status": "decided" },
"decision": {
"id": "dec_7d20e4c1d3f8",
"decision": "ALLOW",
"risk_score": 5,
"risk_band": "LOW",
"reason_codes": ["PUBLIC_READ"],
"matched_policies": [{ "id": "pol_public_reads", "version": 5 }],
"obligations": [],
"expires_at": "2026-10-21T12:05:00Z"
},
"approval": null
}Every decision carries its reason codes, the policy versions that matched and each risk factor's contribution. Open the request in the console to see the same explanation.
The action envelope
One shape for every framework. Missing context is never trusted by default.
| initiating_principal | Who started the task. Missing means ESCALATE. This person cannot approve it. |
| objective | Why, in a sentence. Missing means ESCALATE. |
| action.type | Dotted tool name. Must be in the agent's permitted tools. |
| action.operation | read, write, send, export, delete, transfer or admin. |
| resource | Type, id, sensitivity, environment, destination, record_count, amount. |
| payload | Exactly what the tool will receive. It is hashed; approvals bind to the hash. |
| idempotency_key | Safe retries. The same key returns the same request. |
// Wrap every protected tool call. Nothing reaches the tool without a decision.
async function guarded(action, run) {
const res = await fetch("https://your-host/api/v1/action-requests/evaluate", {
method: "POST",
headers: {
authorization: `Bearer ${process.env.AGENTPLANE_KEY}`,
"content-type": "application/json",
},
body: JSON.stringify(action),
});
const { request, decision } = await res.json();
switch (decision.decision) {
case "ALLOW":
return run(); // then report the outcome via execute
case "REQUIRE_APPROVAL":
return { held: request.id }; // resume when the approval lands
case "ESCALATE":
return { escalated: decision.reason_codes };
case "DENY":
throw new Error(decision.reason_codes.join(", "));
}
}import os, requests
def evaluate(action: dict) -> dict:
r = requests.post(
"https://your-host/api/v1/action-requests/evaluate",
headers={"Authorization": f"Bearer {os.environ['AGENTPLANE_KEY']}"},
json=action,
timeout=5,
)
r.raise_for_status()
return r.json()["decision"]
decision = evaluate({
"initiating_principal": "maya.chen@example.com",
"objective": "Resolve support case 48213",
"action": {"type": "email.send", "operation": "send"},
"resource": {"type": "email", "id": "customer:jordan", "sensitivity": "confidential",
"environment": "production", "destination": "external"},
"payload": {"to": "jordan@example.net", "template": "refund_confirmed"},
"idempotency_key": "case-48213-refund-email",
})Handling decisions
Executing safely
Call POST /api/v1/action-requests/{id}/execute to run through the enforcement point. It refuses anything denied, escalated, unapproved, expired, already executed, changed since approval, from a disabled agent, or sent while the kill switch is on. After running, a verifier checks the postcondition and records evidence.
Only actions that pass through the enforcement point are protected. Route every protected tool through it, and keep tool credentials out of the agent's reach.
Shadow mode
Start any agent in shadow mode to see what enforcement would do before it does anything. Your agent keeps working; every decision is recorded with what it would have been, in the would_be field.
- Would-be holds, escalations and no-policy denials are allowed to proceed and are verified afterwards. No approval is created.
- The hard floor is enforced in every mode: hard-deny rules, critical risk, disabled or revoked agents, tools outside scope, and the kill switch.
- The shadow report shows how many actions would settle by policy, how many reviews a day enforcement would create, and which routine actions need a policy first.
- Shadow decisions are left out of the overview numbers, so rehearsals never inflate your real automation rate.
How policies resolve
- Kill switch on, agent disabled, or tool not permitted: deny.
- Any matching hard deny wins over everything else.
- Missing principal, objective or a clear target: escalate.
- Lowest priority number wins. A tie goes to the stricter effect.
- Risk can only tighten: high risk turns allow into approval, critical turns anything into deny.
- No matching policy: deny. There is no implicit allow.
Try any change in the simulator before it goes live.
Errors
Every error has the same shape: a machine-readable code, a message, a request id and whether it is safe to retry.
| 400 | INVALID_REQUEST | The envelope failed validation. The message names the field. |
| 401 | CREDENTIAL_INVALID | Unknown or rotated agent key. |
| 403 | AGENT_MISMATCH | The key belongs to a different agent_id. |
| 409 | APPROVAL_REQUIRED | Execute was called before an approver decided. |
| 409 | PAYLOAD_MISMATCH | The payload changed after it was authorized. |
| 409 | REPLAY_REJECTED | This action already ran. Send a new request. |
| 409 | DECISION_EXPIRED | The allow decision is older than 5 minutes. |
| 409 | KILL_SWITCH_ACTIVE | An Owner or Admin stopped all agent traffic. |
| 413 | PAYLOAD_TOO_LARGE | Bodies are capped at 64 KB. |
| 429 | RATE_LIMITED | Over 120 evaluate calls a minute for this agent. Safe to retry. |
API reference
| POST | /api/v1/action-requests/evaluate | Validate and decide. Never executes. |
| POST | /api/v1/action-requests/{id}/execute | Run through the sandbox enforcement point. |
| GET | /api/v1/action-requests | Search requests by agent, decision or text. |
| GET | /api/v1/decisions/{id} | A decision with its full explanation. |
| GET | /api/v1/approvals | Approvals by status. |
| POST | /api/v1/approvals/{id}/decision | Approve or deny, bound to payload_hash. |
| GET POST | /api/v1/agents | List or register agents. |
| POST | /api/v1/agents/{id}/rotate-credential | Rotate, or revoke with revoke_only. |
| POST | /api/v1/agents/{id}/mode | Switch between enforce and shadow. |
| GET | /api/v1/shadow-report | Would-be outcomes and readiness per shadow agent. |
| GET POST | /api/v1/policies | List or create policies. |
| POST | /api/v1/simulate | Dry run against live policies. |
| GET | /api/v1/audit-events | Search the audit chain and verify it. |
| POST | /api/v1/kill-switch | Engage or release, with a reason. |
| GET | /api/health/ready | Readiness of store, policies and audit chain. |
Limits and guarantees
- Decisions are deterministic: the same envelope and policy set always give the same answer.
- Agent keys are stored as hashes and shown once. Rotation stops the old key immediately.
- Audit events cannot be edited or deleted through any API, and the hash chain shows if they were changed.
- Secrets, tokens and card-like fields are removed before anything is logged. Emails are masked.
- The executor in this release is sandbox-only. Production adapters require a security review first.