Skip to content

reference

Intended Documentation

Troubleshooting

Symptoms, causes, and fixes for common Intended issues — authentication errors, unexpected denials, token rejections, and audit-chain failures — using real endpoints and CLI commands.

Troubleshooting#

Common issues, organized by category. Each entry lists symptoms, likely causes, and fixes that use real endpoints and CLI commands.

Tip

Most transient problems show up first at the operational probes. Check GET /health (liveness) and GET /ready (readiness) — both are unauthenticated — before deeper debugging.


Authentication & authorization#

Every request returns 401#

Symptoms: all calls return 401 UNAUTHORIZED, regardless of route.

Likely causes:

  • The API key is invalid, revoked, or has an unrecognized prefix (customer keys are intended_live_…; the legacy mrt_ prefix is still accepted). An unrecognized prefix returns 401 INVALID_API_KEY.
  • The Authorization header is malformed.

Fix:

Confirm the header and key prefix

Use the Bearer scheme (or X-API-Key), with no stray whitespace, and a recognized key prefix:

bash
curl -fsS https://api.intended.so/ready \
  -H "Authorization: Bearer $INTENDED_API_KEY"   # intended_live_… (legacy mrt_… also accepted)

Check for an idle portal session

If you authenticate with a portal session rather than an API key, an idle or expired session returns 401 INVALID_PORTAL_SESSION — re-authenticate the session.

403 on some routes but not others#

Symptoms: some calls succeed; others return 403.

Likely causes & fixes:

CodeCauseFix
TENANT_MISMATCHThe credential's tenant ≠ the x-tenant-id / body tenantIdSend the tenant your credential resolves to
FORBIDDENAuthenticated but missing the route's permissionGrant the named scope/role to the key (e.g. intent:create, authority:policy:write)
IP_NOT_ALLOWEDSource IP not on the tenant allowlistCall from an allowlisted address
MFA_REQUIREDPrivileged portal role without MFAComplete MFA on the session

The response body names the required permission for FORBIDDEN — compare it against the API authentication permission table.

429 Too Many Requests#

Symptoms: intermittent 429 RATE_LIMITED.

Likely causes: exceeding the global per-IP limit (500 requests/minute) or the stricter limit on auth endpoints.

Fix: back off exponentially and retry. If you hit the limit steadily, distribute load or contact support about your allocation.


Unexpected denials#

An intent you expected to pass is DENIED (403) or ESCALATED (202)#

Symptoms: POST /intent returns DENIED (403) or ESCALATED (202) when you expected APPROVED (200).

Likely causes:

  • No matching allow rule (default-DENY).
  • A more-severe rule won (DENY > REQUIRE_APPROVAL > ESCALATE > ALLOW).
  • The risk score crossed an escalation/deny threshold via a modifier (requiresPrivilegedAccess +20, touchesProduction +20, containsSensitiveData +15).

Fix:

Read the rationale and gate trace

The /intent response carries authorityDecision with riskScore, rationale[], and gateTrace[]. The rationale names why the decision landed where it did.

Reproduce without recording

Use a simulation to see the outcome without writing audit entries:

bash
intended intent-simulate \
  --tenant "$TENANT_ID" \
  --actor svc-data-pipeline \
  --action "dispatch release workflow" \
  --risk 42

Check your riskContext

A modifier flag set unexpectedly (e.g. touchesProduction: true) can push an otherwise-allowed intent over a threshold. Confirm the riskContext you send matches reality.

500 AUTHORITY_LOOP_FAILED#

Symptoms: POST /intent returns 500 AUTHORITY_LOOP_FAILED.

Cause: a failure inside the authority loop. This is fail-closed — the runtime could not complete the decision, so it denied.

Fix: treat it as a denial, not a transient to retry into an allow. Check /ready, confirm the tenant has an active policy, and inspect recent audit entries (intended audit-query --tenant "$TENANT_ID" --limit 5) for context. If it persists, capture the correlationId and contact support.


Token issues#

Authority Token rejected by an adapter#

Symptoms: the adapter rejects a token (tenant/adapter mismatch, expired, decision not approved, or replayed).

Likely causes:

  • The token's TTL elapsed (300 s default and maximum).
  • The kid does not resolve to the tenant's current public key (rotation since issue).
  • The token's tenantId / adapterId / adapterTarget do not match the adapter.
  • The nonce was already consumed (replay) — single-use tokens cannot be reused.

Fix:

Inspect the token claims

bash
intended inspect-token --token "$AUTHORITY_TOKEN"

Confirm decision: "APPROVED", the tenantId/adapterId/adapterTarget, and that exp has not passed.

Verify the signature explicitly

bash
intended verify \
  --token "$AUTHORITY_TOKEN" \
  --key tenant-public.pem \
  --kid "$KID" \
  --tenant "$TENANT_ID"

A kid mismatch or signature failure here points at key rotation or the wrong public key.

If expired or replayed, re-submit

Tokens are short-lived and single-use by design. Submit a fresh intent to mint a new token rather than reusing one.

Note

Per-tenant authority public keys are distributed via helper functions, not yet via a JWKS HTTP route. The /.well-known/jwks.json endpoint currently serves only the physical-AI signer (an ephemeral dev keypair). Roadmap: a JWKS route for tenant authority keys.


Audit & integrity#

Chain verification reports valid: false#

Symptoms: audit/chain-verification returns { "valid": false, "brokenAt": <n> }.

Cause: a content or linkage mismatch at entry n — either a system fault or tampering.

Fix: treat it as a security incident. Capture brokenAt, the surrounding entries, and the verification timestamp before any remediation. See Enforcement Lineage.

Evidence bundle fails to verify#

Symptoms: verifyEvidenceBundle reports an HMAC signature mismatch.

Likely cause: verification was attempted without the tenant's signing secret. The bundle signature is symmetric HMAC keyed off that secret — it cannot be verified without it.

Fix: verify with the correct tenant signing key, or have Intended (the secret holder) verify on your behalf. An evidence bundle is not publicly/asymmetrically verifiable.


Getting more help#

  1. Capture the correlationId from the /intent response or audit entries.
  2. Query the relevant window: intended audit-query --tenant "$TENANT_ID" --correlation "$CORRELATION_ID".
  3. Confirm terminology against the Glossary.
  4. Contact support with the correlation ID and tenant ID.
Troubleshooting | Intended