Skip to content

api reference

Intended Documentation

Policies API

The authority policy surface — evaluate an intent against policy with POST /authority/evaluate, manage policy sets, and run the draft → review → deploy → rollback lifecycle. All routes are fail-closed and tenant-scoped.

Policies API#

Policy is what the authority runtime consults to reach a decision. This page covers three related surfaces:

  1. POST /authority/evaluate — run the modern policy engine directly against a structured intent and get a decision back, without minting a token or executing anything.
  2. /authority/policies — read and upsert the policy sets the engine evaluates.
  3. /policy/* — the governed draft → review → deploy → rollback lifecycle for promoting policy changes safely.

Warning

POST /authority/evaluate returns the modern enum ALLOW / DENY / REQUIRE_APPROVAL / ESCALATE. This is a different vocabulary from POST /intent, which returns APPROVED / ESCALATED / DENIED. They are produced by two different engines — keep your client mapping distinct. See the Intents API.

How the engine decides#

The policy engine loads the tenant's policy sets, matches rules by their bindings and conditions, and combines results most-severe-wins (DENY > REQUIRE_APPROVAL > ESCALATE > ALLOW). When no rule matches, it defaults to DENY. Interpretation-layer (LIM) signals can only upgrade severity — they never soften a decision. The risk score follows the same formula as /intent: baseRiskScore + (privileged?20) + (production?20) + (sensitive?15), capped at 100.

Evaluate an Intent#

POST/authority/evaluateRequires auth

Evaluates a fully-structured intent against the tenant's policy and returns the decision and the rules that fired. Requires the authority:evaluate permission. No token is minted and no execution is attempted.

tenantIdstring*Must match the credential's tenant AND intent.tenantId AND context.tenantId.
intentobject*An IntentIR: the compiled intent (id, correlationId, adapterId, adapterTarget, actor, targetSystem, proposedAction, riskContext, createdAt).
contextobject*AuthorityRuntimeContext: { tenantId, userId, role, environment, tool, targetSystem, budget?, runId?, limConfidence?, chain? }.

Request#

bash
curl -X POST https://api.intended.so/authority/evaluate \
  -H "Authorization: Bearer $INTENDED_API_KEY" \
  -H "x-tenant-id: tenant_acme_prod" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "tenant_acme_prod",
    "intent": {
      "id": "9d2c…",
      "correlationId": "a9d903e0-8b45-4a2f-9d30-6f9ce7d04d78",
      "tenantId": "tenant_acme_prod",
      "adapterId": "github-actions-adapter",
      "adapterTarget": "acme/platform-api",
      "actor": { "id": "svc-ci-bot", "type": "service" },
      "targetSystem": "acme/platform-api",
      "proposedAction": "dispatch release workflow",
      "riskContext": {
        "baseRiskScore": 42, "policyCompliant": true,
        "requiresPrivilegedAccess": false, "touchesProduction": true,
        "containsSensitiveData": false,
        "github": { "owner": "acme", "repo": "platform-api", "ref": "refs/heads/main" }
      },
      "createdAt": "2026-06-06T12:00:00.000Z"
    },
    "context": {
      "tenantId": "tenant_acme_prod",
      "userId": "svc-ci-bot",
      "role": "service",
      "environment": "production",
      "tool": "github-actions",
      "targetSystem": "acme/platform-api"
    }
  }'

Response#

json
{
  "decision": "REQUIRE_APPROVAL",
  "policySetId": "ps_prod_controls",
  "triggeredRuleIds": ["rule_prod_change"],
  "rationale": ["production change requires approval"],
  "riskScore": 62,
  "requiredApproval": { "approverRoles": ["approver"], "minApprovals": 1 }
}
FieldTypeNotes
decisionenumALLOW / DENY / REQUIRE_APPROVAL / ESCALATE.
policySetIdstring | nullThe policy set that produced the decision, or null when defaulted.
triggeredRuleIdsstring[]Ids of rules that matched.
rationalestring[]At least one human-readable reason.
riskScoreinteger 0–100Computed risk for this evaluation.
requiredApprovalobjectPresent on REQUIRE_APPROVAL.
escalationobjectPresent on ESCALATE.
StatusCodeCause
400INVALID_AUTHORITY_EVALUATION_REQUESTBody failed schema (issues[]).
400TENANT_MISMATCHtenantIdintent.tenantId or context.tenantId.
401UNAUTHORIZEDNo valid credential.
403FORBIDDENMissing authority:evaluate permission.
403TENANT_MISMATCHBody tenant ≠ credential tenant.

Policy Sets#

The rules /authority/evaluate consults live in policy sets.

GET/authority/policiesRequires auth

List the tenant's policy sets. Query: tenantId.

GET/authority/policies/:policySetIdRequires auth

Fetch one policy set. Query: tenantId. Returns 404 POLICY_SET_NOT_FOUND if absent.

POST/authority/policiesRequires auth

Create or upsert a policy set. Requires authority:policy:write. Returns 201 with the saved set. Subject to a per-plan quota (403 QUOTA_EXCEEDED).

PUT/authority/policies/:policySetIdRequires auth

Update a policy set by id. Requires authority:policy:write. A body id that disagrees with the path returns 400 POLICY_SET_ID_MISMATCH.

bash
curl "https://api.intended.so/authority/policies?tenantId=tenant_acme_prod" \
  -H "Authorization: Bearer $INTENDED_API_KEY" \
  -H "x-tenant-id: tenant_acme_prod"

A policy set is { id?, tenantId, name, description?, enabled, version, defaultDecision, rules[] }. Each rule carries bindings (intentTypes, actions, targetSystems, tools, actorRoles, environments), optional confidenceThresholds / budgetCeilings / conditions, and an effect ({ decision, approvalRequirement?, escalation? }).

Policy Lifecycle#

For governed promotion, work through drafts rather than upserting policy sets directly. Every step appends to the audit ledger and requires authority:policy:write.

Read#

MethodPath
GET/policy/packs?tenantId=…
GET/policy/packs/:pack/versions?tenantId=…
GET/policy/drafts?tenantId=…
GET/policy/drafts/:id?tenantId=…
GET/policy/drafts/:id/impact-summary?tenantId=…

Write#

MethodPathBodyReturns
POST/policy/drafts{ tenantId, policyPackName, title, createdBy, policySet, description?, baseVersion?, metadata? }201 draft
PUT/policy/drafts/:id{ tenantId, title?, description?, policySet?, metadata?, updatedBy? }draft
POST/policy/drafts/:id/review{ tenantId, submittedBy, comments?, simulationRunId? }review state
POST/policy/drafts/:id/approve{ tenantId, reviewerId, comments? }approved state
POST/policy/drafts/:id/reject{ tenantId, reviewerId, comments? }rejected state
POST/policy/deploy/:draftId{ tenantId, deployedBy, comments? }deployed version
POST/policy/rollback/:versionId{ tenantId, rolledBackBy, comments? }rolled-back state

Create a draft#

bash
curl -X POST https://api.intended.so/policy/drafts \
  -H "Authorization: Bearer $INTENDED_API_KEY" \
  -H "x-tenant-id: tenant_acme_prod" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "tenant_acme_prod",
    "policyPackName": "prod-release-controls",
    "title": "Require approval for prod deploy",
    "createdBy": "user_admin",
    "policySet": { "name": "prod-release-controls", "version": 1, "defaultDecision": "DENY", "rules": [] }
  }'

Impact summary before you deploy#

GET /policy/drafts/:id/impact-summary returns a semanticDiff, an impact projection (sample size, changed-outcome rate, approval-load and token-issuance deltas, confidence, a lowData flag), and a rolloutSafety checklist with a recommendedMode of shadow / review / enforce. Read it before promoting a draft.

Warning

Policy routes are fail-closed and tenant-scoped. Always send x-tenant-id matching the body tenantId, or the request is rejected with 403 TENANT_MISMATCH.

Info

The CLI exposes deploy as the flat command deploy-policy (an alias of policy-pack-install). There is no intended policy … command group — any older reference to one is wrong.

Next steps#

Policies API | Intended