Skip to content

security

Intended Documentation

Roles and Token Governance

The enterprise role/permission matrix and the API credential lifecycle — create, list, and revoke tenant API keys, with exact scopes, secret handling, and the events ledger.

Roles and Token Governance#

This page covers the two governance levers a tenant administrator controls: who can do what (the enterprise role/permission matrix) and which machine credentials exist (the API credential lifecycle). Both are tenant-scoped, permission-checked, and audited.

Two different vocabularies — keep them apart

Enterprise admin permissions (this page — tokens:manage, members:manage, evidence:export, …) gate the /admin/* console operations. API-key scopes (authority:evaluate, authority:policy:write, intent:create, …) gate the runtime authority API. A credential carries scopes; a person carries an enterprise role. They are resolved and enforced independently.

The role/permission matrix#

There are six enterprise roles. Each resolves to a fixed set of the eleven enterprise permissions. admin is currently equivalent to owner.

GET/admin/rolesRequires auth

Returns the full role matrix for the tenant plus your resolved role and effective permissions. Requires trust:read.

tenantIdstring*Tenant identifier; must match the credential and x-tenant-id.
Permissionowneradminoperatorauditorreviewerread_only
trust:read
audit:read
approvals:review
tokens:manage
policies:manage
gateway:manage
evidence:export
members:manage
tenant:settings:manage
identity:manage
emergency:invoke

What each role is for:

  • owner / admin — full tenant control, including member management, identity settings, and emergency actions.
  • operator — hands-on runtime operator: policies, gateway, approvals, and credential governance, but no member, identity, or emergency authority.
  • auditor — read everything and export evidence, with no mutating authority.
  • reviewer — approval-focused, with read-only trust context.
  • read_only — trust and audit visibility only.

How your role is resolved

Your enterprise role comes from your explicit per-tenant assignment (tenantMemberRole) when one exists; otherwise it is mapped from your portal role (owner→owner, admin→admin, operator/developer→operator, approver→reviewer, viewer→read_only). API-key actors with no member record resolve to read_only.

API credentials#

Credentials are the intended_live_ API keys your services use to call the runtime (a legacy mrt_ prefix is still accepted on older keys). They are issued, listed, and revoked through /admin/api-tokens, and every lifecycle action is recorded as an event.

List credentials#

GET/admin/api-tokensRequires auth

Returns the tenant's credentials (with status, scopes, environment, prefix, and per-credential event counts) and a flat events[] ledger. Requires tokens:manage.

tenantIdstring*Tenant identifier.

Each credential record exposes: id, name, prefix (the first 12 characters of the key, e.g. intended_liv...), environmentKey, scopes[], status (active · revoked · expired), createdBy, createdAt, lastUsedAt, revokedAt, and eventCount. The raw key value is never returned here — only on creation.

Create a credential#

POST/admin/api-tokensRequires auth

Issues a new intended_live_ credential and returns the plaintext secret exactly once. Requires tokens:manage. Responds 201 Created.

tenantIdstring*Tenant identifier; must match credential and x-tenant-id.
namestring*Human-readable label.
environmentKeystring*Environment the key belongs to, e.g. production.
scopesstring[]*Non-empty list of runtime API-key scopes the key may exercise.
expiresAtstring (ISO 8601)Optional expiry; after this the key resolves as expired.
ipAllowliststring[]Optional source-IP allowlist for the key (enforced at the auth gate as 403 IP_NOT_ALLOWED).
bash
curl -X POST https://api.intended.so/admin/api-tokens \
  -H "Authorization: Bearer $INTENDED_API_KEY" \
  -H "x-tenant-id: tenant_acme_prod" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "tenant_acme_prod",
    "name": "ci-pipeline",
    "environmentKey": "production",
    "scopes": ["intent:create", "authority:policy:read"],
    "ipAllowlist": ["203.0.113.10"]
  }'
# 201 → { "credential": { "id": "...", "prefix": "intended_liv...", ... }, "secret": "intended_live_<64 hex>" }

The secret is shown once

The plaintext secret (intended_live_ + 64 hex characters) is returned only in the create response. Intended stores only a SHA-256 hash of it and cannot recover the raw value. Persist it directly into your secrets manager; if it is lost, revoke the credential and issue a new one.

Revoke a credential#

POST/admin/api-tokens/:id/revokeRequires auth

Revokes a credential immediately and records a revoked event. Requires tokens:manage. Returns the refreshed credential list.

idstring*Path parameter: the credential id to revoke.
tenantIdstring*Body field; must match credential and x-tenant-id.
reasonstringRecorded on the revocation event.
bash
curl -X POST https://api.intended.so/admin/api-tokens/cred_123/revoke \
  -H "Authorization: Bearer $INTENDED_API_KEY" \
  -H "x-tenant-id: tenant_acme_prod" \
  -H "Content-Type: application/json" \
  -d '{ "tenantId": "tenant_acme_prod", "reason": "Offboarding ci-pipeline" }'

Emergency revocation

For incident response you can also revoke a credential through the token_revoke emergency control, which records the action in the emergency-event ledger as well as the credential-event ledger.

The credential event ledger#

Every create and revoke writes an apiCredentialEvent (eventType: created · revoked · rotated · used) with the acting actorId, environmentKey, a human-readable summary, and createdAt. These events are returned in the events[] array of GET /admin/api-tokens (most recent first) and feed the audit evidence export. They give you a complete, queryable history of credential governance for a tenant.

Failure modes#

StatusCodeCause
400INVALID_ADMIN_API_TOKEN_CREATE_REQUESTMissing name/environmentKey, empty scopes, or malformed body
400INVALID_ADMIN_API_TOKEN_REVOKE_REQUEST / INVALID_ADMIN_ROLES_QUERYMalformed revoke body or roles query
401UNAUTHENTICATED_ACTORNo resolvable actor on the request
403TENANT_MISMATCHx-tenant-id ≠ body/query tenantId
403FORBIDDEN (+ permission: "tokens:manage" or "trust:read")Role lacks the required permission
404ORG_NOT_FOUNDNo organization exists for the tenant
404CREDENTIAL_NOT_FOUNDThe credential id does not belong to this tenant

Next Steps#

Roles and Token Governance | Intended