1. Schema identity on every response
Every response includes:2. Idempotency-Key required on mutations
PUT, POST, and DELETE endpoints require an Idempotency-Key header:
- First request with key K — the mutation executes. The response body,
status, and content type are cached for 24 hours keyed by
(user, K). - Retry with same K, same body — the cached response is returned
verbatim; the mutation is NOT re-executed. The replay carries
Idempotent-Replay: true. - Retry with same K, different body — returns
422 Unprocessable Contentwith an “Idempotency-Key reused with different inputs” error. Use a new key for a new mutation. - Expiry — keys older than 24 hours are pruned by a scheduled job.
3. Synchronous governance audit log
Every successful mutation writes exactly one row togovernance_audit_log
in the same request lifecycle as the mutation itself. The audit row
includes:
actor_user_id,actor_auth_method,actor_api_key_id?,actor_org_id?action(e.g.alignment_card.put,exemption.granted)target_type+target_idrequest_id(Cloudflare ray)idempotency_keybefore_json— canonical state before the mutation (nullable)after_json— canonical state after (nullable on delete)metadata— including theX-Mnemom-Schemavalue at write time
500 and the mutation is
NOT considered durable. This is by design — we never ship a card change
without its audit trail.
This log is distinct from the HTTP-request-level api_audit_log (which
records every authenticated write). governance_audit_log is the
domain-level record used for SOC 2 Article 12 / HIPAA BAA compliance.
4. Webhook events on every mutation
Each mutation emits exactly one webhook event via the existing DLQ-backed dispatcher:| Action | Event type |
|---|---|
PUT /v1/agents/:id/alignment-card | alignment_card.updated |
PUT /v1/agents/:id/protection-card | protection_card.updated |
PUT /v1/orgs/:id/alignment-template | org_alignment_template.updated |
DELETE /v1/orgs/:id/alignment-template | org_alignment_template.deleted |
PUT /v1/orgs/:id/protection-template | org_protection_template.updated |
DELETE /v1/orgs/:id/protection-template | org_protection_template.deleted |
POST /v1/agents/:id/exemptions | agent.exemption.granted |
DELETE /v1/agents/:id/exemptions/:eid | agent.exemption.revoked |
Exemption grant constraints
Exemptions relax an org-level floor for a specific agent. They are intentionally hard to create:- Auth — org owner or admin only, of the agent’s org. Agents without an org cannot have exemptions (no floor to exempt from; their own card is the source of truth).
reason— required, minimum 20 characters. Describe why this agent needs to bypass the org floor.expires_at— defaults tonow() + 90 daysif omitted. Permanent exemptions require explicit opt-in (expires_at: nullin the body).- Revocation is hard-delete — the
governance_audit_logrow is the permanent record that an exemption was granted and revoked.