Approvals
Create, read, and decide approval requests — POST /v1/approvals, GET /v1/approvals/:id, and POST /v1/approvals/:id/decision.
POST /v1/approvals
Creates an approval request. The referenced policy is evaluated
synchronously: an allow verdict is auto-approved and returned with 200; anything else is
created as pending and returned with 201.
Requires the approvals:write scope.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
action | string | yes | 1–200 chars. |
payload | object | yes | JSON object evaluated by the policy. |
policyKey | string | yes | 1–200 chars. |
context | object | no | Reviewer-facing reasoning; not evaluated. |
agent | string | no | 1–200 chars. |
idempotencyKey | string | no | Returns the existing record on replay. |
curl -X POST https://api.your-org.com/v1/approvals \
-H "authorization: Bearer sk_live_..." \
-H "content-type: application/json" \
-d '{
"action": "wire.transfer",
"policyKey": "payments.high_value",
"payload": { "to": "acct_999", "amount": 5000 },
"context": { "reason": "Vendor invoice #4471" }
}'ApprovalRequestView
Both endpoints return this shape.
{
"id": "6f1c…-uuid",
"status": "pending",
"action": "wire.transfer",
"payload": { "to": "acct_999", "amount": 5000 },
"context": { "reason": "Vendor invoice #4471" },
"createdAt": "2026-07-05T12:00:00.000Z",
"expiresAt": "2026-07-05T12:15:00.000Z",
"feedback": null
}| Field | Type | Notes |
|---|---|---|
id | string (uuid) | Approval id. |
status | enum | pending, approved, rejected, expired, escalated. |
action | string | Echoes the request. |
payload | object | The evaluated payload. |
context | object | Present only if supplied. |
createdAt | string (ISO 8601) | Creation time. |
expiresAt | string | null | When a pending request times out; null once terminal-safe. |
feedback | string | null | Reviewer feedback; present once approved or rejected. |
Responses
| Status | When |
|---|---|
200 | policy returned allow; request auto-approved |
201 | request created as pending (or idempotencyKey hit) |
402 | free-tier monthly limit reached |
403 | key lacks approvals:write |
422 | body failed validation |
GET /v1/approvals/:id
Reads one approval. Supports long-polling: pass ?wait=<seconds> (1–30) and, if the request
is still pending, the server holds the connection until it settles or the budget elapses,
then returns the current state.
Requires the approvals:read scope.
curl "https://api.your-org.com/v1/approvals/6f1c…-uuid?wait=30" \
-H "authorization: Bearer sk_live_..."| Status | When |
|---|---|
200 | returns the ApprovalRequestView |
403 | key lacks approvals:read |
404 | no approval with that id in your org |
POST /v1/approvals/:id/decision
Records a human decision on a pending approval. Reviewers normally decide in Slack, the
reviewer console, or a signed decision link — use this endpoint to decide programmatically, for
example from a custom reviewer integration.
Requires the approvals:decide scope.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
verdict | enum | yes | approved or rejected. |
feedback | string | no | ≤ 5000 chars. Stored on the record and included in the webhook. |
editedPayload | object | no | Approve with a modified payload; the approved record carries it. |
curl -X POST https://api.your-org.com/v1/approvals/6f1c…-uuid/decision \
-H "authorization: Bearer sk_live_..." \
-H "content-type: application/json" \
-d '{ "verdict": "approved", "feedback": "Amount and recipient verified." }'Returns the updated ApprovalRequestView.
Responses
| Status | When |
|---|---|
200 | decision recorded; returns the ApprovalRequestView |
403 | key lacks approvals:decide |
404 | no approval with that id in your org |
409 | approval is not pending (already decided or expired) |
422 | body failed validation |
Prefer the SDK over calling create/read directly — gate.require() handles the
create-then-poll loop and surfaces terminal states as typed errors.