The SDK and the inbox use this API; you only need it to build your own client or tooling. Paths are relative to the server. Behind the self-host bundle the server is under /api, so /v1/plans is https://intentlock.acme.dev/api/v1/plans. Every path is defined once in API_ENDPOINTS in @intentlock/core.
There are two kinds of callers, never mixed:
- Agent apps send
Authorization: Bearer il_live_.... The key identifies the agent.
- People use the session cookie set by sign-in, setup or an accepted invite. Requests other than
GET, HEAD and OPTIONS must also send an x-intentlock-csrf header (any value).
Every route needs a session unless it is marked public or API key below. Read routes are scoped by role: members see only their own runs, holds and audit entries. Bodies are JSON and validated; errors come back as { statusCode, error }, where error is a stable code such as setup_complete.
API key.
| Method |
Path |
Body → response |
| POST |
/v1/plans |
{ plan } → { planId, token, planHash, policyVersion, policyHash, expiresAt }. The agent must be enabled and plan.user an active member. |
| POST |
/v1/plans/:id/refresh |
{ token } → { token, expiresAt }. Same plan hash only. |
| POST |
/v1/check |
{ token, call } → decision. Remote mode; audits every decision and creates holds. |
| POST |
/v1/holds |
{ token, call, ruleId } → { holdId, approvers, timeoutSeconds }. Hybrid mode; the server re-runs the check first. |
| GET |
/v1/holds/:id/wait?timeout=25 |
→ { status }: pending, approved, rejected or expired. Long-poll, at most 25 seconds per request. |
| POST |
/v1/report |
{ token, callId, outcome } → { planStatus }. outcome is ok or error. |
| POST |
/v1/audit/batch |
{ planId, tokens, entries } → { ackedThrough, rejected }. Hybrid mode, at most 100 entries, idempotent on (planId, clientSeq). |
| Method |
Path |
Notes |
| GET |
/v1/holds?status=pending |
Approvers see the holds they may approve; members see their own. |
| POST |
/v1/holds/:id/approve |
{ note? }. Session only. The approver is the signed-in user and must satisfy the hold’s approvers. |
| POST |
/v1/holds/:id/reject |
{ note? }. Same rules as approve. |
| POST |
/v1/holds/resolve |
{ ids, approve, note? } with up to 100 hold ids. Session only. Each hold is approved or rejected on its own, with the same rules and its own audit entry; returns an outcome per id (approved, rejected, expired, already resolved, not allowed, not found). |
Public unless noted.
| Method |
Path |
Notes |
| GET |
/v1/setup/status |
{ setupRequired }. |
| POST |
/v1/setup |
{ code, email, password, orgName? }. Creates the first admin; 409 once one exists. |
| GET |
/v1/auth/methods |
Which sign-in methods are enabled. |
| POST |
/v1/auth/login |
{ email, password } or { email, token } (CLI token). Rate-limited. |
| GET |
/v1/auth/oauth/:provider |
Starts Google or GitHub sign-in. |
| GET |
/v1/auth/oauth/:provider/callback |
OAuth callback. Existing users only, matched by verified email. |
| POST |
/v1/auth/password/forgot |
{ email } → 202. Only when SMTP is configured. |
| POST |
/v1/auth/password/reset |
{ token, password } → 204. Ends all the user’s sessions. |
| POST |
/v1/auth/logout |
Session. |
| GET |
/v1/me |
Session. { email, role, groups }. |
Session, for the signed-in user.
| Method |
Path |
Notes |
| GET |
/v1/account |
Profile, sign-in methods, CLI token status, holdEmails. |
| PATCH |
/v1/account |
{ holdEmails }. Turns your hold emails on or off. |
| POST |
/v1/account/password |
{ currentPassword?, newPassword }. Ends the other sessions. |
| GET, DELETE |
/v1/account/sessions |
List own sessions; DELETE signs out all others. |
| DELETE |
/v1/account/identities/:provider |
Unlinks Google or GitHub. 409 for the last sign-in method. |
| POST, DELETE |
/v1/account/cli-token |
Creates (shown once) or deletes the CLI token. |
| Method |
Path |
Notes |
| GET |
/v1/users |
The team. |
| PATCH |
/v1/users/:email |
{ role?, groups?, disabled?, label? }. Admin. label (up to 24 characters, null clears it) is shown next to the person in the inbox and never changes a decision. |
| POST |
/v1/users/:email/reset-link |
Admin. 24 hours, single use. |
| GET, POST |
/v1/invites |
Pending invites; { email, role, groups? } creates one. Admin. |
| DELETE |
/v1/invites/:id |
Revokes an invite. Admin. |
| POST |
/v1/invites/:id/resend |
New link and expiry. Admin. |
| POST |
/v1/invites/lookup |
{ token }. Public. |
| POST |
/v1/invites/accept |
{ token, password }. Public. Creates the user. |
| Method |
Path |
Notes |
| GET, POST |
/v1/agents |
List; { name, description? } creates one with the starter policy (admin). |
| GET, PATCH, DELETE |
/v1/agents/:id |
{ name?, description?, disabled? }. Changes are admin-only. |
| GET, POST |
/v1/agents/:id/api-keys |
List; { name } creates a key, shown once. Admin. |
| DELETE |
/v1/agents/:id/api-keys/:keyId |
Revokes a key. Admin. |
| Method |
Path |
Notes |
| GET |
/v1/agents/:id/policy |
{ active, draft? }. |
| GET |
/v1/agents/:id/policy/versions |
All versions. |
| GET |
/v1/agents/:id/policy/versions/:version |
One version with its source. |
| PUT |
/v1/agents/:id/policy/draft |
{ source, baseVersion }. Admin. |
| POST |
/v1/agents/:id/policy/validate |
{ source } → { ok, errors }. |
| POST |
/v1/agents/:id/policy/test |
{ source, calls?, replayLast? }. Dry run; nothing is saved. |
| POST |
/v1/agents/:id/policy/publish |
{ source, baseVersion, message }. Admin. 409 if baseVersion is stale. |
| POST |
/v1/agents/:id/policy/rollback |
{ toVersion, message }. Admin. Creates a new version. |
| Method |
Path |
Notes |
| GET |
/v1/runs |
Runs (plans) with decision counts. Filters: agentId, user, status, cursor. |
| GET |
/v1/runs/:planId |
Plan, policy version, decisions, holds and timing. |
| GET |
/v1/stats/overview |
Dashboard counts. Filters: from, to, agentId. |
| GET |
/v1/audit |
Entries with filters, newest first. ?page=N&limit=M returns numbered pages with a total; without page, follow nextCursor with ?cursor=. ?format=jsonl exports. |
| GET |
/v1/audit/verify |
{ ok, count, firstBrokenSeq?, flagged: { seq, flagged, planId }[] }. Each verify is recorded in the chain as audit_verified. |
| GET |
/v1/audit/summary |
{ count, head, oldest, retentionDays, lastVerified }: chain size, latest entry (seq, hash, time), oldest entry kept, the license’s retention days, and the last verification (when, by whom, result). |
| Method |
Path |
Notes |
| GET |
/v1/license |
Edition and limits. |
| GET |
/v1/keys/jwks |
Public. The plan-signing public keys. |
| GET |
/health |
Public. The process is up. |
| GET |
/health/ready |
Public. The database is reachable and the signing key loaded. |
A Socket.IO namespace /events on the path /socket.io (/api/socket.io behind the bundle). The handshake needs a session cookie. The server emits hold.created, hold.resolved and decision, filtered by what the connected user may see.