Skip to content

guides

Intended Documentation

Quickstart

Submit your first intent to the Intended authority runtime, read the APPROVED / ESCALATED / DENIED outcome, and capture the Authority Token — end to end in a few minutes.

Quickstart#

In this guide you submit a single POST /intent call, interpret the authority decision, and capture the Authority Token that downstream systems verify before they act. This is the core loop of the platform: an action is proposed, evaluated against policy, and either authorized with a signed token or stopped.

The authority loop
A proposed action enters the runtime, is interpreted and evaluated against policy, and either returns an Authority Token on approval or stops on denial or escalation — every outcome written to the audit chain.

Submit a proposed action; the runtime evaluates it and returns one of three outcomes. Only APPROVED mints a token.

Prerequisites#

  • A tenant ID (for example tenant_acme_prod).
  • An API key for that tenant — prefixed intended_live_… (legacy mrt_… keys are still accepted). There is no separate sandbox or test key. See API Authentication.
  • curl, Node.js, or Python — whichever you build in.

Tip

Keep the key server-side. It is stored only as a SHA-256 hash, so the raw value can never be recovered from Intended. Inject it from your secrets manager and rotate it immediately if it leaks.

Steps#

Set your credentials

Export the key and tenant so the examples stay copy-pasteable.

bash
export INTENDED_API_KEY="intended_live_your_key_here"
export INTENDED_TENANT_ID="tenant_acme_prod"

Submit an intent

Send the proposed action and its risk context to POST /intent. The body is an IntentRequest: tenantId, actor, targetSystem, proposedAction, and riskContext. The credential goes in Authorization; the tenant in x-tenant-id (and the body tenantId must match it, or the call is rejected with 403 TENANT_MISMATCH).

bash
curl -X POST https://api.intended.so/intent \
  -H "Authorization: Bearer $INTENDED_API_KEY" \
  -H "x-tenant-id: $INTENDED_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
    }
  }'

You can also do this from the command line:

bash
intended intent-submit \
  --apikey "$INTENDED_API_KEY" \
  --tenant "$INTENDED_TENANT_ID" \
  --actor svc-ci-bot \
  --action "dispatch release workflow"

Interpret the outcome

POST /intent returns one of three decisions, mapped to an HTTP status. These values — APPROVED / ESCALATED / DENIED — are the ones the intent endpoint uses.

HTTPDecisionWhat it meansToken?
200APPROVEDThe action verified and was authorizedYes — in authorityDecisionToken
202ESCALATEDA human approval is required before authorizationNo (yet) — an escalationId is returned
403DENIEDThe action is not authorized; execution is skippedNo

If the runtime cannot reach a decision, it fails closed and returns 500 AUTHORITY_LOOP_FAILED — treat that as "not authorized," never as "allow." A malformed body returns 400 VALIDATION_ERROR.

Capture the Authority Token

On APPROVED, the response carries the signed token plus the decision context and audit linkage:

json
{
  "intentId": "8aa3f5f6-b1a9-4c5b-a29f-b489f7d0be58",
  "correlationId": "corr_01HZ…",
  "authorityDecision": {
    "decision": "APPROVED",
    "riskScore": 42,
    "policyCompliant": true,
    "rationale": ["matched rule release-dispatch-allow"],
    "gateTrace": ["kill_switch", "rbac", "structural", "authority_rules", "default"]
  },
  "authorityDecisionToken": "eyJhbGciOiJFZERTQSIsImtpZCI6Im…",
  "execution": { "attempted": true, "status": "executed", "adapterId": "github" },
  "audit": { "entriesWritten": 3, "latestHash": "9f2c…" }
}

The authorityDecisionToken is an Ed25519 JWT with a 300-second TTL and a single-use nonce. Hand it to whatever system performs the action; that system verifies it locally before executing. Continue with Verify a Token.

How the risk score drives the decision#

The riskContext you send shapes the outcome. The runtime computes:

riskScore = baseRiskScore
          + (requiresPrivilegedAccess ? 20 : 0)
          + (touchesProduction        ? 20 : 0)
          + (containsSensitiveData    ? 15 : 0)   // capped at 100

The decision is rule-based and default-deny: if no policy rule matches the action, it is denied. Where multiple rules apply, the most severe wins (DENY > REQUIRE_APPROVAL/escalate > ALLOW). Interpretation-layer signals can only raise severity, never lower it. So flipping touchesProduction to true on the call above can push the same action from APPROVED into ESCALATED or DENIED.

Two decision vocabularies

POST /intent returns APPROVED / ESCALATED / DENIED. The lower-level POST /authority/evaluate engine returns ALLOW / DENY / REQUIRE_APPROVAL / ESCALATE. They map onto the same outcomes — just keep the vocabularies distinct when reading responses.

Next steps#

Quickstart | Intended