Skip to content

api reference

Intended Documentation

API Reference: Audit

Query the per-tenant SHA-256 audit chain, verify its integrity, and export HMAC-signed evidence bundles. Exact endpoints, response shapes, and the precise verification guarantee.

Audit API#

Every state-changing runtime operation appends to a per-tenant, SHA-256 hash-chained audit log as a side effect — you do not opt in. Each entry stores the hash of the previous entry, so any later tampering breaks the chain and is detectable. This API lets you query that chain, verify its integrity, and export a signed evidence bundle for a single intent.

One audit chain per tenant
Two tenants, each with its own isolated hash-chained audit log, signing keys, and single-use token nonce namespace — no shared state crosses the boundary.

Each tenant has its own hash chain and its own signing key. Audit entries never cross the tenant boundary, and evidence is signed with the tenant's own secret.

What the chain stores#

The audit-grade substrate is the AuditEntry record: { id, tenantId, correlationId, intentId, actorId, actorType, eventType, payload, previousHash, entryHash, createdAt }, unique on (tenantId, entryHash). The entryHash is a SHA-256 over the canonical, key-sorted JSON of the entry, and previousHash links to the prior entry's hash (the chain head's previousHash is null). Appends run inside a serializable transaction. There are 40+ eventTypes — INTENT_RECEIVED, AUTHORITY_DECISION, TOKEN_ISSUED, EXECUTION_ATTEMPTED, EXECUTION_RESULT, ESCALATION_EVENT, and so on.

Info

A single POST /intent typically writes several entries (received → decision → token/execution → result). The audit.entriesWritten and audit.latestHash in the /intent response tell you how many were appended and the resulting chain head.

Query Tenant Audit Events#

GET/tenants/:tenantId/auditRequires auth

Returns audit events for a tenant with offset pagination, filterable by correlation id and event type.

tenantIdstring*Tenant identifier (path). Must match the credential's tenant.
correlationIdstringFilter to one request's entries.
eventTypestringFilter by event type, e.g. TOKEN_ISSUED.
limitnumber1–1000 (default 100).
offsetnumberPagination offset (default 0).
bash
curl "https://api.intended.so/tenants/tenant_acme_prod/audit?eventType=TOKEN_ISSUED&limit=50" \
  -H "Authorization: Bearer $INTENDED_API_KEY" \
  -H "x-tenant-id: tenant_acme_prod"

Response: { tenantId, total, limit, offset, events: [...] }. Each event carries its eventType, payload, correlationId, entryHash, previousHash, and createdAt.

Verify Chain Integrity#

GET/tenants/:tenantId/audit/chain-verificationRequires auth

Recomputes every entry's content hash and re-checks every link in the tenant's chain.

bash
curl "https://api.intended.so/tenants/tenant_acme_prod/audit/chain-verification" \
  -H "Authorization: Bearer $INTENDED_API_KEY" \
  -H "x-tenant-id: tenant_acme_prod"

Response:

json
{ "valid": true, "totalEntries": 1284 }

If the chain is broken, valid is false and brokenAt is the index of the first failing entry:

json
{ "valid": false, "brokenAt": 512, "totalEntries": 1284 }

Info

A cross-tenant sweep, POST /audit/verify-chain, runs the same verification across every tenant and raises an ESCALATION_EVENT alert on any broken chain. Note a known reporting quirk: its per-tenant checkedRecords field is always 0 (the underlying verifier returns totalEntries, not checkedRecords). The valid / brokenAt results are correct; only that count is wrong. The single-tenant endpoint above is unaffected.

Generate an Evidence Bundle#

GET/tenants/:tenantId/intents/:intentId/evidenceRequires auth

Returns a self-contained, signed evidence bundle for one intent — all its audit events plus an integrity hash and signature.

bash
curl "https://api.intended.so/tenants/tenant_acme_prod/intents/8aa3f5f6-b1a9-4c5b-a29f-b489f7d0be58/evidence" \
  -H "Authorization: Bearer $INTENDED_API_KEY" \
  -H "x-tenant-id: tenant_acme_prod"

Response (EvidenceBundle):

json
{
  "intentId": "8aa3f5f6-b1a9-4c5b-a29f-b489f7d0be58",
  "tenantId": "tenant_acme_prod",
  "correlationId": "a9d903e0-8b45-4a2f-9d30-6f9ce7d04d78",
  "generatedAt": "2026-06-06T12:00:05.000Z",
  "eventCount": 6,
  "events": [ /* the intent's audit entries */ ],
  "integrityHash": "sha256:…",
  "signature": "hmac-sha256:…",
  "chainValid": true
}
FieldNotes
integrityHashsha256:<hex> over the serialized events.
signaturehmac-sha256:<hex> over intentId:integrityHash, keyed with the tenant's own stored secret.
chainValidWhether the underlying chain verified at generation time.

Warning

On what "signed" means here. The evidence signature is a symmetric HMAC-SHA256 keyed with the tenant's own secret. Verifying it (verifyEvidenceBundle, with timing-safe comparison) requires possession of that same secret. It is therefore not publicly or asymmetrically verifiable the way the Ed25519 decision token is — a third party cannot independently verify the bundle without the tenant key. State this precisely in any compliance claim.

StatusCodeCause
400VALIDATION_ERRORBad path params.
404NO_AUTHORITY_KEYTenant has no ACTIVE signing key.
404NO_AUDIT_EVENTSNo audit events for that intent.
500EVIDENCE_BUNDLE_FAILEDGeneration failed closed.

Run-Stream Endpoints#

The runtime also keeps an unsigned, unchained narrative stream (AuditRun / AuditRunEvent) for replay and export, distinct from the tamper-evident chain above.

MethodPathPurpose
GET/audit/run/:runId?tenantId=…Fetch the run's event stream.
GET/audit/run/:runId/replay?tenantId=…Replay the run deterministically.
GET/audit/run/:runId/export?tenantId=…Export the run's narrative.
GET/audit/:tenantId/timeline?limit=…Executive-readable timeline (limit 1–200, default 100).

Info

The narrative stream is for human review and replay; it is not the tamper-evident substrate. Tamper-evidence comes from the hash-chained AuditEntry log queried above. Keep the two distinct in any audit narrative.

Enterprise Export Jobs#

For formal, asynchronous compliance exports, use the admin export jobs. These require the enterprise audit:read permission and resolve through the tenant boundary.

MethodPathPurpose
POST/admin/audit-exportsCreate an export (201).
GET/admin/audit-exports?tenantId=…List exports.
GET/admin/audit-exports/:id?tenantId=…Export status.
GET/admin/audit-exports/:id/download?tenantId=…Download the artifact (404 AUDIT_EXPORT_ARTIFACT_NOT_READY until ready).

The download response carries the artifact plus x-intended-artifact-digest and x-intended-artifact-signature headers.

bash
curl -X POST https://api.intended.so/admin/audit-exports \
  -H "Authorization: Bearer $INTENDED_API_KEY" \
  -H "x-tenant-id: tenant_acme_prod" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "tenant_acme_prod",
    "includeAnomalies": true,
    "includeRecommendations": true,
    "from": "2026-06-01T00:00:00Z",
    "to": "2026-06-06T00:00:00Z"
  }'

Warning

The system provides the mechanisms of audit — an immutable hash chain, signed export, and tamper detection — but it encodes no compliance-framework control IDs (no SOC 2, ISO 27001, or EU AI Act mappings live in code). Do not represent these endpoints as a certified control mapping; represent them as the evidence substrate an auditor can build on.

Next steps#

API Reference: Audit | Intended