Skip to content

guides

Intended Documentation

API Quickstart

Obtain an intended_live_ API key, submit your first intent to POST /intent, and handle the APPROVED (200), ESCALATED (202), and DENIED (403) outcomes.

API Quickstart#

This quickstart submits an action to the canonical runtime route, POST /intent, and walks through the three decision outcomes. The submitted action is an IntentRequest — not an action/resource/subject triple — and the route returns the legacy decision vocabulary APPROVED / ESCALATED / DENIED mapped to HTTP 200 / 202 / 403.

Note

POST /intent returns APPROVED / ESCALATED / DENIED. The modern POST /authority/evaluate route returns a different vocabulary (ALLOW / DENY / REQUIRE_APPROVAL / ESCALATE). They are distinct — don't mix them.

Step 1 — Get an API key#

Mint a tenant API key from the console. Customer keys are prefixed intended_live_…. There is no separate sandbox or test key — one kind of key covers every use, and the environment label you can attach to a key is cosmetic (it does not gate authorization, billing, or data isolation). A legacy mrt_… prefix is still accepted for older staff keys.

Store the key and your tenant id as shell variables. Keep the key server-side; it is stored only as a SHA-256 hash and can never be recovered from Intended.

bash
export INTENDED_API_KEY="intended_live_your_key_here"
export TENANT_ID="tenant_acme_prod"

Step 2 — Submit an intent#

The request body is an IntentRequest: who wants to do what, to which system, with what risk. Intended returns a decision and, on approval, a signed token. Your agent runs the action itself — Intended decides and proves; your agent acts.

For agent actions, POST /v1/agent-intent takes a flatter body.

bash
curl -X POST https://api.intended.so/intent \
  -H "Authorization: Bearer $INTENDED_API_KEY" \
  -H "x-tenant-id: $TENANT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "tenant_acme_prod",
    "actor": { "id": "svc-ci-bot", "type": "service" },
    "targetSystem": "acme/platform-api",
    "proposedAction": "dispatch release workflow",
    "riskContext": {
      "baseRiskScore": 42,
      "policyCompliant": true,
      "requiresPrivilegedAccess": false,
      "touchesProduction": false,
      "containsSensitiveData": false
    }
  }'

Request fields#

FieldTypeRequiredNotes
tenantIdstringyesMust match the tenant your credential resolves to.
actor.idstringyesThe acting identity.
actor.type"user" | "service"yes
targetSystemstringyesThe system the action targets, for example acme/platform-api.
proposedActionstringyesHuman-readable description of the action.
riskContext.baseRiskScoreinteger 0–100yesStarting risk before modifiers.
riskContext.policyCompliantbooleanyes
riskContext.requiresPrivilegedAccessbooleanno (default false)Adds +20 to risk when true.
riskContext.touchesProductionbooleanno (default false)Adds +20 to risk when true.
riskContext.containsSensitiveDatabooleanno (default false)Adds +15 to risk when true.

The effective risk score is baseRiskScore + (privileged ? 20 : 0) + (production ? 20 : 0) + (sensitive ? 15 : 0), capped at 100.

Step 3 — Handle the outcome#

The response body carries authorityDecision, and on approval, the signed token:

json
{
  "intentId": "8aa3f5f6-b1a9-4c5b-a29f-b489f7d0be58",
  "correlationId": "…",
  "authorityDecision": {
    "decision": "APPROVED",
    "riskScore": 42,
    "policyCompliant": true,
    "rationale": ["…"],
    "gateTrace": []
  },
  "authorityDecisionToken": "eyJhbGciOiJFZERTQSIsImtpZCI6…",
  "audit": { "entriesWritten": 3, "latestHash": "…" }
}
OutcomeHTTPWhat you getWhat to do
APPROVED200authorityDecisionToken present.Act, and have the receiving service verify the token first (see below).
ESCALATED202escalationId returned; no token issued.Route to your approval queue; resubmit or resume after a human approves.
DENIED403No token (fail-closed).Surface the rationale; the action is not authorized.

A validation failure returns 400 VALIDATION_ERROR. Any internal failure during evaluation returns 500 AUTHORITY_LOOP_FAILED — the runtime fails closed rather than letting an action through on error.

Step 4 — Verify the token before you execute#

The authorityDecisionToken is an Ed25519 JWT bound to this one intent. The system that performs the action must verify it locally before acting — cloud approval alone is not sufficient.

ts
const verification = await sdk.verifyAuthorityToken({
  token: result.authorityDecisionToken as string,
  publicKeyPem,                 // tenant public key for the token's kid
  expectedKid: kid,             // pin the key id (required)
  expectedTenantId: "tenant_acme_prod",
  expectedAdapterId: "decision-only",
  maxTokenTtlSeconds: 300,
  clockSkewSeconds: 60,
});
if (!verification.valid) throw new Error(verification.reason ?? "TOKEN_INVALID");

sdk.verifyAuthorityToken(...) delegates to @intended/verify's verifyToken. See Verify Decision Tokens for the full flow, including consuming the single-use nonce in your service.

Next steps#

API Quickstart | Intended