Developers
Turrigan API reference
Turrigan is one HTTP call placed in a path you already control. POST an
AI output, or a proposed agent tool call, to /v1/govern and get back
allow, block, or escalate in milliseconds, screened by a deterministic core with no LLM
in the hot path. Everything else on this page reads back what happened: the decisions, the evidence, the
conformity posture and the integrity of the log itself.
This is a readable rendering of the live OpenAPI specification, published here because an interactive reference is JavaScript and cannot be read by anything that does not run it. The interactive version, with request bodies you can send, is at api.turrigan.com/docs, and the raw spec is at api.turrigan.com/openapi.json.
Base URL and authentication
The base URL is https://api.turrigan.com. Governance calls authenticate with a scoped,
expiring, revocable API key in the X-API-Key header. Keys are least-privilege: a key minted
for Guard ingest can post guard events and read its own entitlement and nothing else, and cannot read
decisions or govern. Console and management calls use a bearer token from a user session.
Requests are bounded at 200 KB, rate isolation is per tenant so one caller cannot degrade another, and the governance path fails closed: an unreachable datastore can never silently allow.
The one call that matters
curl https://api.turrigan.com/v1/govern \
-H "X-API-Key: trg_..." \
-H "Content-Type: application/json" \
-d '{
"input": "user question",
"output": "what your AI is about to say",
"profile": "default",
"language": "en"
}'
The response carries the verdict, whether the output may be delivered, which controls were triggered, and which detectors flagged. What is stored is redacted and hash-chained, so the record proves the decision without retaining the personal data that caused it.
Governance
The chokepoint. One call per AI output or per proposed agent action, returning allow, block or escalate.
POST /v1/govern
Govern an AI output (allow / block / escalate)
The inline chokepoint. Submit the model input and output; the deterministic detector floor runs (PII, verifiable claims, prompt injection, safety, uncertainty, fairness tagging) and an immutable, redacted decision is appended to the hash-chained log. `delivered` is true only for `allow`. `escalate` and `block` enter the human-oversight queue. **Advisory endpoint:** the returned verdict is a RECORD; this endpoint does not itself prevent the caller from sending data anywhere. If you supply a `destination_key`, which is decisive only when the destination registry is enabled for your tenant, the destination's approved status is OPERATOR-ATTESTED, not verified by Turrigan, and a raised verdict on a floor-tripping class to a destination attested external-unapproved is evidentiary. How far the verdict is raised depends on the finding: a confirmed special-category finding blocks, which of the Article 9 subtypes means health; a probable special-category finding escalates for biometric identification, health, religious or philosophical belief and sexual orientation; and a probable finding for any other special-category subtype does not raise the verdict at all; a probable criminal-offence finding escalates; high-harm-identifier and children's-data findings escalate rather than blocking, because neither is a measured detection of the output. Egress is enforced only on the governed proxy surface, and what it enforces is ROUTING: the request is forwarded solely to the resolved destination and an unresolved route hard-fails. A destination the operator registered but classed external-unapproved is still forwarded to, and the raised verdict withholds the reply rather than preventing that transfer. **Idempotency:** send an optional `Idempotency-Key` header to make retries safe: a replay of the same key returns the ORIGINAL decision (`idempotent_replay: true`) instead of appending a duplicate; reusing a key with a different body is 422, and a still-in-flight key is 409.
Authentication: scoped API key, bearer token
Clean output → allow
{
"input": "What are your opening hours?",
"output": "We are open Sunday to Thursday, 8am to 3pm.",
"profile": "default",
"language": "en"
}
Leaks PII (ID + card) → block
{
"input": "Confirm the account holder for this claim.",
"output": "The policy holder is Ahmed Al-Rashidi, Emirates ID 784-1990-1234567-1, card 4111 1111 1111 1111.",
"profile": "default",
"language": "en"
}
POST /v1/govern/action
Authorize a proposed agent action (tool call)
Plane C: an agent submits a proposed tool call before executing it. Checks: tool allow-list (unlisted blocks), privilege scope (exceeding granted scopes escalates), parameter risk, plus the content floor over the parameters. Turrigan authorizes the DECISION to act; it never executes the action. `granted_scopes` must be attested by the calling service, not forwarded from the agent. If you supply a `destination_key`, its approved status is OPERATOR-ATTESTED, not verified by Turrigan; this endpoint is advisory: it records a decision, it does not prevent egress, and it never executes the action. **Idempotency:** an optional `Idempotency-Key` header makes retries safe: a replay returns the original decision (`idempotent_replay: true`) rather than appending a duplicate.
Authentication: scoped API key, bearer token
Allow-listed tool, in-scope → allow
{
"tool": "read_record",
"parameters": {
"record_id": 4821
},
"agent_id": "assistant-1",
"granted_scopes": [
"read"
],
"profile": "default"
}
Allow-listed tool needs a scope the agent lacks → escalate
{
"tool": "update_record",
"parameters": {
"record_id": 4821,
"field": "status",
"value": "reviewed"
},
"agent_id": "assistant-1",
"granted_scopes": [
"read"
],
"profile": "default"
}
Turrigan Guard (input-side)
Ingest for the Turrigan Guard browser extension, which governs what goes into an AI app rather than what comes out.
GET /v1/guard/entitlement
Guard subscription entitlement (polled by the extension)
Returns whether the paid Guard capability is active for this tenant, the paid-through instant, and the offline grace window (days) the extension applies before treating an unreachable check as lapsed. On lapse the enterprise extension degrades to on-device blocking; it does not stop protecting the user.
Authentication: scoped API key
GET /v1/guard/events
List Guard audit events for the tenant console (newest first)
Tenant console read of THIS organisation's Guard audit events, strictly org-scoped (an explicit tenant filter, never a request parameter). Masked samples only, never raw PII. Optional filters: `host` (exact AI app host), `q` (free-text over the stamped user/org identifiers), and a `since`/`until` received-at range (ISO 8601). Cursor pagination via `before_id` - pass the response's `next_before_id` for the next older page. Console (JWT) auth only: a `guard:ingest` API key cannot read.
Authentication: bearer token
POST /v1/guard/events
Record a Guard PII-exposure event (enterprise, subscription-gated)
The Turrigan Guard browser extension posts one MINIMISED event here when a user proceeds past both PII-review prompts. The body is the `turrigan.guard.audit/v1` contract: detector types plus MASKED samples only, never raw PII (a non-masked sample is 422). The event is appended to the tenant's tamper-evident, hash-chained guard-events log. **Subscription-gated:** an inactive/expired advance-paid subscription is 402 and a suspended tenant is 403 (nothing is written). **Idempotent:** re-posting the same client event `id` returns the original `event_uid` with `deduped: true` instead of double-recording.
Authentication: scoped API key
User proceeded past both PII prompts (masked samples only)
{
"schema": "turrigan.guard.audit/v1",
"id": "b7f3c1a2d4e54f6a8b9c0d1e2f3a4b5c",
"event": "pii_proceeded",
"app": {
"host": "chatgpt.com"
},
"findings": [
{
"type": "email",
"count": 1,
"sample": "\u2022\u2022\u2022\u2022.com"
},
{
"type": "credit_card",
"count": 1,
"sample": "\u2022\u2022\u2022\u20224242"
}
],
"mode": "enterprise",
"extVersion": "0.3.0"
}
GET /v1/guard/events/verify
Verify the tenant's Guard audit chain is untampered
Recomputes the per-tenant, tamper-evident hash chain over the guard-events log and returns whether every link is intact, plus the event count. Powers the console 'Verify audit chain' button. Org-scoped to the caller's tenant.
Authentication: bearer token
Decisions & Explanations
Read back any governed decision, with a deterministic, human-readable reason for the verdict.
GET /v1/decisions
List governed decisions (newest first)
Authentication: bearer token
GET /v1/decisions/{uid}
Full decision detail (redacted, with evidence and version set)
Authentication: bearer token
GET /v1/decisions/{uid}/content-credential/verify
Verify a decision's content-provenance mark (EU AI Act Art 50(2))
Recomputes the C2PA mark's COSE signature and assertion binding, checks the provenance-hash sealed into the version-set, and confirms the mark re-derives from the decision's recorded fields. ok=false (not an error) when the decision has no mark.
Authentication: bearer token
POST /v1/decisions/{uid}/erase
GDPR erasure of one decision's content, crypto-shred (auditor)
GDPR right-to-erasure via crypto-shredding: destroy this decision's encryption key. The content becomes permanently unrecoverable; the append-only record + hash chain stay intact and verifiable. Logged to the (control-plane) security audit log.
A2: refused (423) while an active legal hold or an open incident covers the decision, a legal hold trumps an erasure request until it is lifted.
Authentication: bearer token
GET /v1/decisions/{uid}/explanation
Right-to-explanation for a decision (deterministic)
Authentication: bearer token
POST /v1/decisions/{uid}/hold
Place a legal/investigation hold on a decision (auditor)
While the hold is active, a crypto-shred of this decision is refused (423), a legal hold trumps an erasure request until it is released. Placing the hold and its reason are recorded in the tamper-evident security log.
Authentication: bearer token
Hold for an open investigation
{
"reason": "Under regulatory investigation, do not erase",
"reference": "CASE-2026-0042"
}
GET /v1/holds
List active legal holds for this tenant (auditor)
Active legal holds for this tenant (decision- and tenant-scoped).
Authentication: bearer token
POST /v1/holds/{hold_id}/release
Release a decision-scoped legal hold (auditor)
Releases a DECISION-scoped hold. Tenant-scoped holds are placed and released only by a super-admin, so an auditor cannot lift a platform-level hold.
Authentication: bearer token
Release once the matter is closed
{
"reason": "Investigation closed; hold lifted"
}
Conformity & Evidence
Live conformity posture per control, and evidence export including the EU AI Act Annex IV core pack.
GET /v1/conformity/posture
Conformity posture for a framework
Per-control coverage (covered / not covered, honestly reported) and live evidence counts, derived from the framework's versioned control mappings.
Authentication: bearer token
GET /v1/evidence/control/{framework}/{control_key}
Evidence items for one control (auditor)
Authentication: bearer token
GET /v1/evidence/export
Bulk evidence export: Annex IV core (JSON) or CSV (auditor)
`format=annexiv` returns the EU AI Act Annex IV core technical-documentation pack as JSON (see the AnnexIVPack schema). `format=csv` returns `text/csv` with X-Evidence-* coverage headers. Every export is written to the security audit log.
Authentication: bearer token
GET /v1/tenant/export
GDPR data-portability export of this tenant's decisions (auditor)
GDPR data-portability: the calling tenant's governed decisions with decrypted detail + explanations. Auditor/admin scoped; logged.
Authentication: bearer token
Audit Integrity
Verify the hash chain, anchor checkpoints, and detect the exact position of any tampering.
POST /v1/audit/anchor
Sign the current chain head into a checkpoint (auditor)
Sign the current chain head into an append-only, externally-publishable checkpoint. For a BYO tenant, also RETAIN the signed head in the control plane (SC1-D) so it survives deletion of the customer-side anchor.
Authentication: bearer token
GET /v1/audit/anchors
List checkpoints and verify signatures against the live chain (auditor)
List checkpoints + verify each signature and that the signed head still matches the live chain (a post-checkpoint rewrite shows head_matches_chain=false).
Authentication: bearer token
GET /v1/audit/control-plane
Full integrity check: hash chain + control-plane anchors (auditor)
Runs BOTH the hash-chain verification (edit/reorder/corruption) and the control-plane anchor cross-check (wholesale rewrite/truncation of the anchored prefix); ok requires both. control_plane.unanchored_suffix is the written-but-not-yet-anchored tail; control_plane.anchored=false means anchoring not yet enabled.
Authentication: bearer token
GET /v1/audit/merkle
Merkle transparency checkpoint over the audit chain (auditor)
A compact Merkle root over every audit-chain record, for periodic publication to an external transparency sink plus O(log n) inclusion proofs (A7).
Authentication: bearer token
GET /v1/audit/verify
Verify the decision log's hash chain
Recomputes content hashes and links over the append-only log. Any edit, delete, or reorder is reported with the break position.
Authentication: bearer token
Human Oversight
The review queue for escalated decisions, with a mandatory justification on resolution.
GET /v1/review/pending
Escalated / blocked decisions awaiting human review
Authentication: bearer token
POST /v1/review/{uid}/claim
Claim a pending decision for review (reviewer)
Authentication: bearer token
POST /v1/review/{uid}/resolve
Resolve a reviewed decision (reviewer; justification is mandatory)
Authentication: bearer token
Approve after human review
{
"action": "approve",
"justification": "Reviewed; content is safe to deliver."
}
Reject the output
{
"action": "reject",
"justification": "Confirmed PII leak; must not be sent."
}
Incidents (Art. 73)
GET /v1/incidents/open
Open incidents (with 15-day deadlines)
Authentication: bearer token
POST /v1/incidents/{uid}/classify
Classify an incident; 'serious' starts the Art. 73 clock
Classify a candidate incident. 'serious' starts the 15-day Art-73 reporting clock.
Authentication: bearer token
Classify as serious (starts the 15-day clock)
{
"classification": "serious"
}
POST /v1/incidents/{uid}/dismiss
Dismiss a candidate incident
Dismiss a candidate incident as a false positive - leaves the open queue.
Authentication: bearer token
Dismiss a candidate incident
{
"reason": "False positive on review."
}
POST /v1/incidents/{uid}/mark-reported
Record that the incident was reported to the authority (auditor)
Authentication: bearer token
Record the authority report
{
"authority": "MoI",
"reference": "INC-2026-0142"
}
GET /v1/incidents/{uid}/report
Pre-filled Art. 73 serious-incident report (auditor)
Authentication: bearer token
POST /v1/incidents/{uid}/report
Raise an incident on a decision
Authentication: bearer token
Raise a serious incident
{
"severity": "serious",
"description": "Model disclosed customer PII in production."
}
Drift (Art. 72)
POST /v1/drift/baseline
Capture a golden-set baseline of governor behaviour (auditor)
Authentication: bearer token
GET /v1/drift/check
Check current behaviour against the latest baseline (auditor)
Authentication: bearer token
Fairness
GET /v1/fairness
Disparate-impact screening over governed outcomes (auditor)
Batch disparate-impact screening over governed-decision outcomes (auditor-scoped).
Authentication: bearer token
Catalog & Profiles
GET /v1/catalog/controls
Loaded controls
Authentication: bearer token
GET /v1/catalog/detectors
Registered detectors with versions
Authentication: bearer token
GET /v1/catalog/frameworks
Loaded frameworks
Authentication: bearer token
GET /v1/profiles
Sector / jurisdiction profiles (conformity lens)
Sector/jurisdiction profiles (a conformity lens of applicable frameworks).
Authentication: bearer token
Security Log
GET /v1/security/events
This tenant's security audit log (auditor)
The tamper-evident security audit log for the CALLING tenant (auditor/admin): auth, access denials, exports, erasures, integrity checkpoints. Org-scoped (an auditor is a per-tenant role); metadata only - no secrets/PII.
Authentication: bearer token
GET /v1/security/verify
Verify the security log's hash chain (auditor)
Verify the security log's hash chain (any edit/reorder/delete is detected). Integrity is global; the reported length is scoped to the calling tenant.
Authentication: bearer token
Metrics
GET /v1/metrics
Tenant metrics: decisions by verdict, queues, incidents (auditor)
Operational metrics for the calling tenant (org-scoped) + global integrity stats - a text-based surface for dashboards/alerting (a SIEM/Prometheus exporter is the ops add).
Authentication: bearer token
Auth & Session
POST /api/auth/change-password
Change the current user's password
Change the authenticated user's password.
Enforces: - Current password must be correct. - New password must differ from the current one. - New password must not match any of the last PASSWORD_HISTORY_COUNT passwords. - Passwords match (new == confirm). After success, password_changed_at is updated and the old password is archived.
Authentication: bearer token
Rotate the current user's password
{
"current_password": "old-password",
"new_password": "a-strong-new-passphrase",
"confirm_new_password": "a-strong-new-passphrase"
}
POST /api/auth/login
Log in (username + password, optional MFA code)
Username + password
{
"username": "admin",
"password": "your-password"
}
With an MFA code
{
"username": "admin",
"password": "your-password",
"mfa_code": "123456"
}
POST /api/auth/logout
Revoke the current session token
Revoke the presented token id (jti) so it can't be reused before expiry.
Authentication: bearer token
GET /api/auth/me
Current session identity
Authentication: bearer token
POST /api/auth/mfa/disable
Disable MFA for the current user
Disable MFA - requires a valid current code (can't be turned off by a stolen session alone).
Authentication: bearer token
Disable with a current TOTP code
{
"code": "123456"
}
POST /api/auth/mfa/enroll
Start TOTP MFA enrolment (returns the otpauth URI)
Begin TOTP enrolment. Requires encryption (the seed must be sealable). If MFA is ALREADY active, a valid current code is required to re-enrol, and the new seed is only STAGED (mfa_pending_secret) - the active second factor is never cleared until the new seed is proven via /mfa/verify. This blocks a stolen session from turning MFA off.
Authentication: bearer token
First enrolment (no code needed)
{}
POST /api/auth/mfa/verify
Confirm MFA enrolment with a TOTP code
Activate MFA by proving the PENDING (newly enrolled) seed; promote it to active.
Authentication: bearer token
Confirm enrolment with a TOTP code
{
"code": "123456"
}
Users (self-service)
GET /api/users
List this organization's users
Authentication: bearer token
POST /api/users
Create a user (returns a one-time temporary password)
Create a user in the admin's organization. Returns a one-time temporary password.
Authentication: bearer token
Create a reviewer
{
"username": "j.doe",
"email": "j.doe@example.gov",
"role": "reviewer"
}
GET /api/users/{user_id}
Get one user
Authentication: bearer token
PATCH /api/users/{user_id}
Change a user's role or active flag
Update a user's role and/or active state. An admin cannot change their OWN role or deactivate themselves (prevents accidental self-lockout and self-escalation loops).
Authentication: bearer token
Change role
{
"role": "auditor"
}
Deactivate (access cutoff)
{
"active": false
}
POST /api/users/{user_id}/reset-password
Reset a user's password (returns a new one-time temporary password)
Issue a fresh one-time password for a user in the admin's org (returned once).
Authentication: bearer token
API Keys (self-service)
GET /api/keys
List this org's keys
Authentication: bearer token
POST /api/keys
Mint a scoped API key (secret shown once)
Authentication: bearer token
Governance key, 30-day expiry
{
"name": "prod-assistant",
"scopes": [
"govern"
],
"expires_in_days": 30
}
POST /api/keys/cmk
Switch this org to customer-managed key custody
Switch the caller org to customer-managed-key custody (true sovereignty). The tenant TEK is re-wrapped under a customer key from the KMS provider; existing data stays readable. Managed is the default; this is the opt-in upgrade.
Authentication: bearer token
Point at your own KMS key
{
"key_ref": "aws-kms:arn:aws:kms:me-central-1:123456789012:key/abcd-1234"
}
POST /api/keys/cmk/revoke
Revoke the customer key: crypto-shreds this tenant (irreversible)
Customer-key revocation = crypto-shred this tenant (irreversible). Destroys the customer key so all the tenant's content becomes unrecoverable; the append-only chain stays intact.
A2: refused (423) while an active legal hold or an open incident covers the tenant, the BYOK "delete during retention / dispute" prohibition.
Authentication: bearer token
GET /api/keys/custody
Key custody status (managed or customer-managed)
Authentication: bearer token
POST /api/keys/{key_id}/revoke
Revoke a key immediately
Authentication: bearer token
Datastores (BYO)
GET /api/datastores
Current datastore mode (pooled or byo)
Authentication: bearer token
POST /api/datastores
Register this org's own database (DSN validated, migrated, sealed)
Validate + migrate + register the caller org's client-hosted DB. The DSN is sealed at rest; only non-secret metadata (driver/host) is ever returned or logged.
Authentication: bearer token
Register your own Postgres
{
"dsn": "postgresql://user:pass@db.internal:5432/turrigan",
"tls_required": true
}
DELETE /api/datastores
Deregister the client database (data stays with you)
Stop routing to the client DB (future govern goes back to pooled). The customer's data stays in THEIR database - only our registry pointer + cached engine are dropped.
Authentication: bearer token
POST /api/datastores/health
Datastore health: reachability, freshness, anchoring lag (+ ?verify_chain deep check)
On-demand health of the caller org's BYO datastore (D6-SC-2): reachability, freshness (write-lag), and anchoring lag; plus an opt-in deep chain + anchor-signature verification (`?verify_chain=true`).
FAIL-OPEN-STRUCTURED: an unreachable/degraded datastore is a normal 200 body (`reachable=false` + a closed-enum `reason_code`), NOT an error -- so the admin can SEE the state. This is DELIBERATELY unlike the govern chokepoint, which fails CLOSED (503) on an unreachable BYO DB (H-iii); a monitor must read the `reachable`/`status` field, not the HTTP status. The report is content-free and read-only. Rate- limited per org (it opens a live connection to the customer's DB).
Authentication: bearer token
POST /api/datastores/test
Liveness probe for the registered client database
Liveness probe for the caller org's client DB (fails CLOSED if unreachable).
Authentication: bearer token
Health
GET /api/health
Liveness + active LLM provider seam