Autorización de agentes
Autorice acciones de agentes de IA: concesiones, recibos, supervisión y los SDKs que hablan el protocolo.
El cuerpo de la documentación está en inglés.
Overview
The Overturo Authorization Protocol (OAP) is a wire-level authorization protocol for AI agents. It sits one layer above OAuth 2.0 / OIDC: identity tokens answer who is the agent; OAP answers which actions is this agent authorized to take, under which bounds, on whose behalf, and how is each decision recorded.
OAP ships in two adoption depths. Pick one per application; per-agent-class promotion is a configuration change, not a re-integration.
counter) added. The Conductor Mode tab covers approvals originating from origin: authorize; the Oversight Mode tab covers approvals originating from origin: attestation. The embed/SDK story is identical — what differs is who originates and who is asked. The full authorize cascade is on the Conductor Mode tab.
Quickstart — three short paths
Pick the role you're integrating from. Each path links into the tab with the field shapes, error codes, and code samples you'll need.
Agent builder (Conductor)
- Generate an API token at Developer → Credentials.
- Install
@overturo/sdk(TypeScript) oroverturo(Python). See the SDKs tab. POST /api/v1/grantswith a DPoP-bound credential. See the Conductor Mode tab for the request body.- Use the returned agent token to call
authorize()per action.
Policy-engine operator (Oversight)
- Provision a Trusted Attester at /trusted_attesters and copy the one-time bearer token.
- Set the application's mode to
oversightorhybrid. See the Oversight Mode tab. - Install
@overturo/sdk(TypeScript) oroverturo(Python), or deploy the OPA adapter sidecar. POST /api/v1/oap/attestationsper decision; heartbeat between bursts.
Counterparty (Verifier)
- Install
@overturo/verify— zero runtime deps. - Call
verifyReceipt(jwt, { jwksUrl }). - Branch on
iss_role—authorizervswitnesscarry different trust weights. See the Receipts & Verification tab.
Evidence holder (Records)
- Fetch the durable record:
GET /api/v1/authorization_receipts/:id(audit:verifyscope). - Pick a flavor —
signedverifies offline against published keys, years later. - Resolve a held runtime receipt by
jtithrough the dispute kit's join slice. See Receipts & Verification → Durable authorization records.
OAP has two layers. Runtime receipts authorize — short-lived, verified before honoring an action. Durable authorization records prove — one frozen, standards-structured document per delegated authority (ISO/IEC TS 27560-aligned), with the agent's public accountability record, its content-hashed capability manifest, and Article 50 disclosure receipts around it. The wire is closed; the record is published and independently verifiable.
Two adoption modes
Conductor Mode
The platform is in the decision path. Agents call POST /api/v1/grants; the platform runs a 15-step evaluation cascade and either issues a signed receipt, escalates to the principal, or denies with a category-tagged reason.
Trust posture: receipts carry iss_role: "authorizer". Counterparties may rely on the receipt as the authorizing artefact.
Oversight Mode
Your existing policy engine (Open Policy Agent, AWS Cedar, in-house rules) decides. You submit attestations of those decisions to POST /api/v1/oap/attestations; the platform witnesses, signs, anchors to the audit chain, and surfaces escalations.
Trust posture: receipts carry iss_role: "witness". Counterparties weight them lower than authorizer receipts — the distinction is wire-level.
Which mode should I pick?
- Already have a policy engine you trust? Start in Oversight. You keep your decision authority and gain a tamper-proof audit substrate, a branded human-approval surface, regulator-grade evidence packages, and sub-30-second global revocation.
- Building net-new agent surfaces? Start in Conductor. The 15-step cascade replaces most of what you'd otherwise build (rate limits, scope checks, value bounds, time windows, escalation routing) and gives counterparties a stronger artefact to rely on.
- Mixed estate? Hybrid is a first-class mode — different agent classes on the same application can run in different depths.
Where OAP sits next to OAuth and OIDC
- OAuth 2.0 / OIDC still issues the agent's identity tokens. See OAuth Authorization.
-
OIDC-A 1.0 identity overlays plug into OAP as a third
agent_credentialpath alongside SD-JWT and W3C VC. See the OIDC-A Interop tab. -
MCP integrations use OAP receipts as the wire-level proof that an action is authorized; the
@overturo/mcp-authorizemiddleware verifies them in-process.
Closed wire, open SDKs
OAP's wire format is proprietary; this surface documents the shapes integrators call and consume. The first-party SDKs that issue, accept, and verify OAP receipts are open-source (Apache 2.0). See the SDKs tab for what's published today, in which languages, and what each library covers.
Conductor Mode
The platform decides. Every action an agent takes flows through a 15-step evaluation cascade and emits a signed receipt or a category-tagged denial. Counterparties verify the receipt; the tamper-evident audit chain records every step.
Human-approval flow (Trust Surface integration)
When the cascade returns escalate, the platform mints a Trust Surface decision session (kind: approval) and returns {decision_url, session_token, deadline} in the response envelope. Your application either redirects the principal or embeds the decision client-side:
<overturo-decision token="dts_..."></overturo-decision>
<script type="module" src="https://cdn.overturo.com/js/v1/overturo.js"></script>
Three outcomes (origin: authorize):
approve— principal authorizes the action as-is; agent receivesapproval.grantedwebhookdeny— principal rejects; agent receivesapproval.deniedcounter— principal proposes alternative terms; agent receivesapproval.counteredwithcounter_proposalin payload
Quorum support: escalations with quorum_type: "all" | "majority" | "threshold" are already shipped. Each approver renders the same <overturo-decision> element; the webhook fires once with the aggregated outcome.
Python and JavaScript code samples are on the SDKs tab. Embed reference: the Embedding tab in Developer Docs.
Grant lifecycle
POST /api/v1/grants— agent submits credential + scope + actions + valid_until + (optionally)agent_attestation.- The cascade evaluates; outcome is allow, escalate, or deny.
- On allow: an Ed25519-signed receipt is issued (
iss_role: "authorizer") and persisted to the audit chain. - On escalate: an approval request is opened, the principal is notified, and a signed approval URL is delivered through the configured channel.
- On deny: the response carries a
denial_category+cascade_stepso the agent (and your dashboard) know exactly which check failed.
Worked example
Authentication is a personal API token, generated at Developer → Credentials. Every grant must carry an idempotency key so retries are safe. The agent_dpop_jkt is the SHA-256 thumbprint of the DPoP public key the agent will use when authorizing actions — see the SDKs tab for the helper that computes it.
curl -X POST https://overturo.com/api/v1/grants \
-H "Authorization: Bearer $OVERTURO_API_TOKEN" \
-H "Idempotency-Key: 7f3c1d2e-…" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent-checkout-v3",
"agent_dpop_jkt": "<base64url SHA-256 of the agent DPoP public key>",
"scope": ["payments:write"],
"actions": ["payment.send"],
"valid_until": "2026-12-31T23:59:59Z",
"value_bounds": { "amount_per_action": "100.00", "currency": "USD" },
"human_register": { "displayed_to_principal": "..." }
}'
Response (allow)
{
"grant_id": "ath_us_xxxxxxxx",
"agent_token": "eyJhbGciOi…",
"grant_attestation": "eyJhbGciOi…",
"iss": "https://overturo.com",
"oap_ver": "1.0"
}
The 15-step evaluation cascade
Positions 1–2 reject malformed or unauthenticated requests at the API edge. Positions 3–15 are evaluated by the authorization engine. Two positions are reserved for future use; the rest are active in v1.0.
| # | Check | Denial category on failure |
|---|---|---|
| 1 | Request shape (controller) | authorization_denied |
| 2 | Authentication (controller) | authorization_denied |
| 3 | Grant exists & is active | authorization_denied |
| 4 | Scope allowed by grant | authorization_denied |
| 5 | Action allowed by grant | authorization_denied |
| 6 | Reserved (action_not_prohibited) | — |
| 7 | Within value bounds | intent_denied |
| 8 | Within time window | intent_denied |
| 9 | Reputation threshold met | intent_denied |
| 10 | Escalation bounds (escalate if needed) | intent_denied |
| 11 | Purpose-of-use compatible | intent_denied |
| 12 | Reserved (max_actions) | — |
| 13 | Sequence bounds | trajectory_denied |
| 14 | Continuation chain depth | trajectory_denied |
| 15 | Drift detection | trajectory_denied |
Denial categories
Every deny response carries one of three categories. They map to the cascade phase that rejected the request, so a counterparty (or your operator dashboard) can route the response to the right reviewer.
| Category | Phase | Meaning |
|---|---|---|
| authorization_denied | Steps 1–5 | The agent is not entitled to take this action at all — grant missing, expired, or doesn't cover this scope/action. |
| intent_denied | Steps 7–11 | The action is in scope but this particular invocation violates a bound (amount, time, reputation, purpose). |
| trajectory_denied | Steps 13–15 | Each call individually would pass but the sequence — rate, ordering, drift from declared trajectory — fails. |
Escalation
When the cascade hits an escalation bound, the platform opens an approval request, renders the action context using a template specific to the action class, and signs an approval URL the principal can click from any channel — email, SMS, in-app notification, or an MCP elicitation prompt.
Approval URLs carry a third iss_role value — approval_url_signer — distinct from the receipt-issuer roles. See the Receipts & Verification tab for the full iss_role taxonomy.
Unified approval URL. The authorize response on the escalate branch returns three TrustSurface fields alongside the existing escalation payload:
decision_url—https://<region>/decisions/<session_token>, suitable for embed / popup / redirect via@overturo/js.session_token— 43-char URL-safe Base64 handle for the TrustSurface session.approval_ttl_at— ISO-8601 deadline; the postMessage v2 protocol emits countdown ticks against this.
The branded /checkout/<jwt> URL form (platform-managed notification dispatch) continues unchanged for opaque-link integrations.
Version + conformance
GET /api/v1/oap/version advertises the supported OAP versions, regional issuers, conformance levels, and audit-chain genesis hashes. The Conformance tab walks the payload field by field.
Oversight Mode
Your existing policy engine — Open Policy Agent, AWS Cedar / Verified Permissions, in-house rules — keeps making the allow/deny decision. The platform witnesses each decision: tamper-proof audit chain, signed witness receipts, regulator-grade evidence packages, branded human-approval surface (Overturo Checkout), and sub-30-second global revocation.
Receipts issued under Oversight carry iss_role: "witness". Counterparty verification libraries weight them lower than authorizer receipts — the distinction is wire-level and the @overturo/verify libraries throw (not warn) on missing or unexpected iss_role.
Human-approval flow (Trust Surface integration)
When an Oversight attestation determines a human is needed (your policy returned escalate, or your attestation included requires_human_approval: true), the platform mints a Trust Surface decision session with origin: attestation. The same <overturo-decision> element renders it — identical chrome to the Conductor-Mode flow, identical postMessage protocol, identical webhook contract.
What differs is who originated the ask and who is asked. Approvers see "policy XYZ flagged this action for review" instead of "agent ABC requested authorization." Both pages cross-reference each other — see the Conductor Mode human-approval flow for the origin: authorize equivalent.
Three outcomes (origin: attestation):
approve— reviewer authorizes; attestation completes as approved with witnessed receiptdeny— reviewer rejects; attestation completes as deniedcounter— reviewer proposes alternative terms (different scope, different bounds); your policy engine receives the counter-proposal on the next attestation
Python and JavaScript examples are on the SDKs tab. Trust Surface embed reference: the Embedding tab in Developer Docs.
Application mode
Each application carries a mode attribute. The three valid values:
conductor(default) — every agent action passes through the cascade.oversight— the platform never authorizes; it only witnesses attestations submitted by your policy engine.hybrid— per-agent-class routing within one application. Promotion or demotion is a configuration change, not a re-integration.
Setting the mode on an application
A new application is created in conductor mode by default. Two paths exist to change it:
- Self-service promotion (Oversight → Conductor): after attestations are flowing, use the What-if-Conductor dashboard to promote individual agent classes or the whole application. Each promotion is one click and is reversible.
- Initial mode (Conductor → Oversight or Hybrid): not yet self-service. Contact your account manager (hosted) or use the operator console (self-hosted) to set the mode flag before your first integration.
Provisioning a Trusted Attester
Self-service UI: /trusted_attesters. An account admin creates the attester identity, the platform mints a bearer token, and the token is shown exactly once on the create / rotate screen — it is stored only as a SHA-256 digest server-side. Copy it into your secret store before navigating away; there is no "show again" affordance.
Lifecycle actions
| Action | When to use it |
|---|---|
| Create | First integration, or one identity per environment (staging / prod) so a leaked dev token never touches production data. |
| Rotate | On a routine cadence or on suspected compromise. Rotation mints a new token in a second slot; the old token keeps working for the overlap window (default 24h, configurable) so you can roll your fleet without downtime. |
| Suspend / Unsuspend | Temporary stop without revoking. Suspended attesters return a distinct denial response so your client can distinguish "rotate me" from "stop calling". |
| Destroy | Permanent. Both token slots are cleared. Use when retiring a service. |
| Request elevation | To add the operator-gated revocations:trigger permission. Posts a rationale to the operator queue for review. |
Scoping
Each Trusted Attester belongs to one account. Optionally, you can scope an attester to a single application — the same policy engine identity then can't attest for any application it isn't bound to. Leave the application unset to attest for all of the account's applications with one identity.
Trusted Attester authentication
Oversight uses a fifth platform auth mechanism alongside the four documented under Authentication: the Trusted Attester bearer token. Your policy engine identifies as a peer service, not as a user or an agent.
Authorization: Bearer tat_<region>_<base64url(32)>
Tokens are issued per trusted-attester identity and carry a permission set drawn from this vocabulary:
attestations:submit— create attestationsattestations:heartbeat— gap-detection livenessescalations:create— request human approvalrevocations:trigger— operator-gated; customer needs scope-elevation approval
Tokens support two-slot rotation (overlap rotation without downtime). Suspension is a distinct denial response so your client can distinguish "rotate me" from "stop calling".
Attestation ingest
POST /api/v1/oap/attestations records one decision your policy engine made. The platform signs a witness receipt, anchors it to the tamper-evident audit chain, and returns the artefact.
Required body fields
| Field | Type | Purpose |
|---|---|---|
| attestation_id | string ≤ 64 | Your idempotency key; replays return the original receipt. |
| application_id | prefix id | The application the decision was made for. The legacy alias touchpoint_id is still accepted for backward compatibility. |
| agent_class | string ≤ 128 | Identifier for the agent role (e.g. checkout_assistant). |
| action_class | string ≤ 64 | Action taxonomy entry (e.g. pay). |
| decision | enum | One of allow, deny, escalate. |
| decided_at | RFC 3339 | When your engine made the decision. |
| sequence_number | positive int | Monotonic per agent_class. Gaps are flagged by the platform. |
| legal_basis | enum | Lawful basis for the decision. Wire enum derives from GDPR Article 6: consent, contract, legal_obligation, vital_interests, public_task, legitimate_interests. US deployments map their CCPA purpose-limitation or HIPAA permitted-use rationale to the closest of these values. Mandatory — evidence packages depend on it. |
| evidence_digest | 64-hex SHA-256 | Hash of the decision inputs (the policy engine's decision log entry, canonicalised). Anchors the witness without leaking the inputs themselves. |
| context | object | Optional structured context (≤ 4 KiB). Surfaced in escalations and the operator dashboard. |
Worked example
curl -X POST https://overturo.com/api/v1/oap/attestations \
-H "Authorization: Bearer $OVERTURO_ATTESTER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"attestation_id": "att-2026-06-03-0042",
"application_id": "app_us_xxxxxxxx",
"agent_class": "data_export_agent",
"action_class": "mcp_tool_call",
"decision": "allow",
"decided_at": "2026-06-03T09:14:22Z",
"sequence_number": 42,
"legal_basis": "legitimate_interests",
"evidence_digest": "a1b2c3...",
"context": { "policy_ref": "policy-export-v1", "policy_version": "1.2.0" }
}'
Response shape
{
"attestation": {
"id": "<prefix id>",
"received_at": "2026-06-03T09:14:23Z",
"sequence_number": 42,
"decision": "allow",
"idempotent_replay": false
},
"receipt": "<Ed25519-signed JWS with iss_role: witness>"
}
Heartbeat & gap detection
POST /api/v1/oap/attestations/heartbeat tells the platform your policy engine is alive even when it hasn't made any decisions to report. Combined with the monotonic sequence_number on attestations, the platform detects gaps and surfaces them on the operator dashboard and in evidence packages.
The heartbeat + sequence-number contract is non-negotiable: without it, an Oversight customer could quietly fail to attest some decisions and still have the platform's brand applied to the resulting audit trail.
Client-triggered revocation
POST /api/v1/oap/revocations (requires revocations:trigger permission). Propagates through the same webhook + security-event-stream pipeline as Conductor revocations, under the inherited < 30-second SLA. Supports an Idempotency-Key header so retries are safe.
Revocations are surfaced in the operator dashboard at /surfaces/oversight/revocations alongside their triggering attestation (if any).
Operator surfaces
/surfaces/oversight/attestation_stream— live stream + export./surfaces/oversight/escalations— Oversight-side escalation inbox./surfaces/oversight/revocations— client-triggered revocation history./surfaces/oversight/hybrid_routing— per-agent-class mode resolution./surfaces/oversight/what_if_conductor— counterfactual comparison: which decisions would the platform have made differently?
Approval URL
Oversight escalations expose the same unified /decisions/<session_token> URL as Conductor escalations. The escalation-creation response surfaces decision_url, session_token, and approval_ttl_at alongside the existing fields. Principals can embed, pop up, or redirect to this URL via @overturo/js.
The branded /checkout/<jwt> URL form (platform-managed notification dispatch) is unchanged for opaque-link integrations. The legacy /oap/escalations/<prefix_id>?token=<jwt> form 301-redirects to the unified URL during the migration window.
Receipts & Verification
An OAP runtime receipt is an Ed25519-signed JWS that captures one decision the platform made or witnessed — the short-lived artifact a counterparty verifies before honoring an action. Counterparties verify the signature, inspect the iss_role claim, apply the appropriate trust weight, and proceed. Verification works offline; online verification adds replay protection. Behind every runtime receipt stands the durable authorization record — the frozen, independently verifiable document of the delegation itself (below).
The iss_role claim
Every receipt carries a required iss_role claim. The platform issues three role values; counterparty libraries weight them differently and refuse receipts where the claim is absent or unrecognised.
| Value | Issued by | What it asserts |
|---|---|---|
| authorizer | Conductor Mode | The platform ran the 15-step cascade and authorized the action. Strongest trust weight. |
| witness | Oversight Mode | A trusted attester (your policy engine) reported its decision; the platform signed the witness record. Lower trust weight — the platform did not authorize. |
| approval_url_signer | Escalation flow | An approval URL that, when clicked by the principal, resolves an escalation. Not an authorization artefact — a delivery vehicle. |
The @overturo/verify library exposes the role-to-trust-weight mapping at ISS_ROLE_TO_TRUST_WEIGHT; integrations that mix authorizer and witness receipts in the same pipeline should branch on it explicitly.
Receipt examples
Both receipts are Ed25519-signed JWS with a header carrying { alg: "EdDSA", typ: "oap+jwt", kid: "oap-<region>-v<N>" }. The payload differs:
iss_role: "authorizer" (Conductor)
{
"iss": "https://overturo.com",
"iss_role": "authorizer",
"aud": "did:web:vendor.example",
"jti": "<nonce-from-authorize>",
"oap_ver": "1.0",
"oap_conformance": "federated",
"grant_id": "ath_us_xxxxxxxx",
"action": "payment.send",
"scope": "payments:write",
"value": "75.00",
"currency": "USD",
"chronicle_id": "audit_rec_xxx",
"single_use": true,
"context_hash": "<64-hex SHA-256>",
"iat": 1748951662,
"exp": 1748951962
}
iss_role: "witness" (Oversight)
{
"iss": "https://overturo.com",
"iss_role": "witness",
"sub": "att_us_xxxxxxxx",
"oap_ver": "1.0",
"oap_conformance": "oversight_basic",
"decision": "allow",
"legal_basis": "legitimate_interests",
"policy_ref": "policy-export-v1",
"policy_version": "1.2.0",
"attestation_digest": "sha256:<64-hex>",
"jti": "wrec_xxxxxxxx",
"iat": 1748951662,
"exp": 1748955262
}
The authorizer receipt names the action being authorized; the witness receipt names the decision your policy engine made. chronicle_id and grant_id are the join keys: hold onto them — they resolve a runtime receipt into the durable authorization record's trail and the dispute kit's runtime_join slice, months after the receipt expired.
Verifying a receipt — TypeScript
import { verifyReceipt } from "@overturo/verify"
const verified = await verifyReceipt(receiptJwt, {
jwksUrl: "https://overturo.com/.well-known/jwks.json",
})
if (verified.iss_role === "authorizer") {
// strong: platform ran the cascade and authorized this action
acceptPayment(verified)
} else if (verified.iss_role === "witness") {
// witness_only: client's policy engine decided; platform recorded
recordForAudit(verified)
} else {
// approval_url_signer — not a decision artefact, do not honor
throw new Error("not an authorization receipt")
}
verifyReceipt throws ReceiptInvalid on missing or unknown iss_role, expired exp, future iat, signature failure, or unknown kid. The Python equivalent is verify_receipt_offline in overturo.
Offline verification
Fetch the platform JWKS once (GET /.well-known/jwks.json), cache it, and verify receipts locally with any Ed25519-aware JWS library. The first-party verifiers are @overturo/verify (TypeScript) and overturo (Python). The same two packages also verify the durable signed record export offline — verifyRecord and verify_record respectively, resolving the record's stamped key version against the published key set. See the SDKs tab.
Offline verification does not catch revocation. For replay-sensitive flows (payments, irrevocable actions), follow up with an online check.
One boundary worth writing down: the runtime receipt's TTL bounds honoring, not evidence. An expired receipt still verifies cryptographically forever — which is exactly what a dispute months later needs.
Online verification
POST /.well-known/overturo-verify. Authenticated as a counterparty (bearer token). Body:
{
"receipt": "<JWS compact serialization>",
"action_completed": true
}
Response carries signature validity, current revocation status, and (when action_completed: true) a one-shot consumption acknowledgement that prevents the same receipt being used twice on different counterparties.
Decode helper
POST /.well-known/overturo-decode. Anonymous. Returns the receipt payload without signature verification — useful for debugging integrations and dashboard rendering. Don't use it as a verification step.
Revocation list
GET /.well-known/oap-revocation-list returns the current revocation set, supports cursor pagination, and is cached for 60 seconds. Bulk verifiers fetch this once and check membership locally.
Durable authorization records
Every delegated-authority commitment freezes exactly one authorization record — the durable, standards-structured document of who authorized which agent under which enforced bounds. Fetch it with
GET /api/v1/authorization_receipts/:id (requires the audit:verify token scope) in three flavors via ?flavor=:
canonical (the rendered document), signed (the frozen snapshot + a detached signature that verifies offline against published keys), and dpv (standards-vocabulary JSON-LD — served only when the record carries personal-data processing; pure authority semantics refuse with dpv_unavailable rather than inventing pseudo-vocabulary). Unknown flavors and unsignable reconstructions refuse with typed errors (unknown_flavor, not_signable).
Out-of-band agent disclosures mint durable, anonymous-capable disclosure receipts via
POST /api/v1/disclosure_receipts (requires disclosures:write; the payload is closed — flow_id, agent_id, disclosed_at, locale — and delivery provenance is always recorded as operator-declared; no payload field can claim platform delivery).
Record holders download the dispute kit from the record page: the signed record, the lifecycle timeline, the runtime_join slice that resolves held runtime receipts by jti, and oversight evidence where a human decided. Full field semantics, flavors, and worked verification examples live in Docs → Receipts and the Receipt Interop & Verification guide published with the developer documentation.
Discovery
Three discovery surfaces tell integrators what this OAP deployment supports: the OIDC discovery document (extended with OAP fields), /api/v1/oap/version, and the OAP-specific well-known endpoints.
OIDC discovery extensions
/.well-known/openid-configuration carries the standard OAuth 2.0 / OIDC fields plus an OAP block. Notable extensions:
oap_jwks_uri— Ed25519 receipt-signing keys (distinct fromjwks_uriwhich serves the OAuth/OIDC RSA/ES256 keys).oap_protocol_versions_supported— array of OAP versions the issuer currently serves.agent_claims_supported/agent_types_supported/delegation_methods_supported— OIDC-A 1.0 capability advertisement.agent_attestation_endpoint/agent_attestation_formats_supported/agent_attestation_signing_alg_values_supported— attestation discovery triad.
The OIDC-A Interop tab covers the agent-side discovery fields in full.
/api/v1/oap/version
The single source of truth for what this deployment implements. The Conformance tab walks the payload field by field; the high-level shape:
{
"oap_ver": "1.0",
"supported": ["1.0"],
"current": "1.0",
"deprecated": [],
"regional_issuers": { ... },
"chronicle_genesis_hashes": { ... },
"oap_conformance": "conductor_full",
"conformance_capabilities": { ... },
"oversight_conformance": "oversight_basic",
"oversight_conformance_levels": ["oversight_basic", "oversight_full"],
"spec_url": "https://overturo.com/specs/oap-v1.0"
}
OAP-specific well-known endpoints
| Endpoint | Purpose |
|---|---|
| /.well-known/jwks.json | JSON Web Key Set for authorization server token verification |
| /.well-known/oap-attestation | OAP agent attestation provider discovery |
| /.well-known/oap-credentials/:id | OAP agent credential metadata (by credential id) |
| /.well-known/oap-revocation-list | OAP token revocation list |
| /.well-known/witness-configuration | External witness anchoring configuration and receipt signing-key discovery (current + historical region keys) |
| /.well-known/transparency/:application_id | Controller identification record for an application's receipts: declared parties, export flavors, and the schema, interpretation, and key-discovery pointers |
| /.well-known/transparency/agents/:agent_id | Agent accountability record: the operator's declared identity, the agent's platform-attested status, current credential attestations, and the receipt machine pointers |
| /.well-known/transparency/agents/:agent_id/manifests/:version | A published capability-manifest version: the agent's declared capability set frozen at pin time, with its content hash for independent verification |
| POST /.well-known/overturo-verify | Online receipt verification + revocation check. |
| POST /.well-known/overturo-decode | Anonymous receipt decoder (debugging — not a verification step). |
The full developer-discoverable /.well-known/* catalogue lives under Docs → API Reference.
SDKs
First-party libraries for issuing, accepting, and verifying OAP receipts. All OAP SDKs ship under Apache 2.0. The wire format itself is proprietary; the SDKs are the supported way to interop with it.
overturo.decisions.* as the kind-agnostic primitive plus overturo.consent.* and overturo.approvals.* facades, alongside the <overturo-decision> custom element. The Python SDK adds a decisions module for server-side session minting and Conductor's OapApprovalRequired exception handling. See the Embedding tab in Developer Docs for the integration reference.
For agent builders (Conductor Mode)
Submit grant requests, refresh credentials, sign DPoP proofs, verify receipts you received from counterparties.
@overturo/sdk
TypeScript · v1.5.0 · Apache 2.0 · Sourcenpm install @overturo/sdk
import { OverturoAuthorize, jwkThumbprint } from "@overturo/sdk"
// 1. Generate a DPoP keypair, then bind its thumbprint at grant creation.
const jkt = await jwkThumbprint(dpopKey.publicJwk)
// ... POST /api/v1/grants with `agent_dpop_jkt: jkt` ...
// 2. After cooling-off, wrap the returned credentials.
const client = new OverturoAuthorize({
grantId: "ath_us_xxxxxxxx",
agentToken: process.env.OVERTURO_AGENT_TOKEN,
dpopKey,
iss: "https://overturo.com",
})
// 3. Authorize a specific action. Throws OapError on deny.
const result = await client.authorize({
nonce: crypto.randomUUID(),
action: "payment.send",
scope: "payments:write",
value: "75.00",
currency: "USD",
})
overturo
Python 3.10+ · v1.2.0 · Apache 2.0 · Sourcepip install overturo
import uuid, os
from overturo import DpopKeyPair, OverturoAuthorize, jwk_thumbprint
dpop_key = DpopKeyPair.generate()
jkt = jwk_thumbprint(dpop_key.public_jwk)
# ... POST /api/v1/grants with `agent_dpop_jkt: jkt` ...
with OverturoAuthorize(
grant_id = "ath_us_xxxxxxxx",
agent_token = os.environ["OVERTURO_AGENT_TOKEN"],
dpop_key = dpop_key,
iss = "https://overturo.com",
) as client:
result = client.authorize(
nonce = str(uuid.uuid4()),
action = "payment.send",
scope = "payments:write",
value = "75.00",
currency = "USD",
)
For counterparties (receipt verification)
You're on the receiving end — an agent presented you a receipt and you need to verify it before honoring the action. Both libraries enforce the iss_role contract and reject malformed receipts.
@overturo/verify
TypeScript · v0.4.3 · Apache 2.0 · Sourcenpm install @overturo/verify
import { verifyReceipt } from "@overturo/verify"
const verified = await verifyReceipt(receiptJwt, {
jwksUrl: "https://overturo.com/.well-known/jwks.json",
})
console.log(verified.iss_role) // "authorizer" | "witness" | "approval_url_signer"
console.log(verified.trust_weight) // "strong" | "witness_only" | "approval_url"
The package also ships verifyRecord — offline verification of the durable signed record export (the ?flavor=signed download) against published keys, plus verifyManifestPin for capability-manifest pins. See the Receipts tab.
@overturo/mcp-authorize
TypeScript · v1.1.0 · Apache 2.0 · Source
Drop-in middleware for MCP servers — verifies _meta.oap_receipt on every tool call.
npm install @overturo/mcp-authorize @overturo/sdk
import { mcpAuthorize, isOapError } from "@overturo/mcp-authorize"
import { rawEd25519ToJwk } from "@overturo/sdk"
// Pre-fetch the regional JWKS at server boot.
const jwks = (await fetch("https://overturo.com/.well-known/jwks.json")
.then(r => r.json())).keys.map(k => rawEd25519ToJwk(k.kid, k.x))
const verify = mcpAuthorize({
acceptedIssuers: ["https://overturo.com", "https://eu.overturo.com"],
ownIdentifier: "did:web:my-mcp-server.com",
jwks,
required: true,
})
// Then wire `verify(req)` into your MCP server's request pipeline.
For Oversight Mode integrators
Submit attestations from your policy engine. The OPA adapter is a deployable sidecar that bridges Open Policy Agent's decision log into OAP attestations without any code changes to your policies. Each SDK reads the attester token from an environment variable so it never lands in source.
@overturo/sdk
TypeScript · v1.5.0 · Apache 2.0 · Sourcenpm install @overturo/sdk
import { OverturoOversight } from "@overturo/sdk"
import { evidenceDigest } from "@overturo/sdk/utils"
const client = await OverturoOversight.create({
baseUrl: process.env.OVERTURO_BASE_URL,
token: process.env.OVERTURO_ATTESTER_TOKEN,
applicationId: process.env.OVERTURO_APPLICATION_ID,
heartbeatEnabled: true,
startSequence: await loadFromRedis("oversight:seq"),
onSequenceUpdate: async (seq) => saveToRedis("oversight:seq", seq),
})
await client.attest({
agentClass: "data_export_agent",
actionClass: "mcp_tool_call",
decision: "allow",
legalBasis: "legitimate_interests",
evidenceDigest: evidenceDigest(opaInput, opaOutput),
})
overturo
Python 3.10+ · v1.2.0 · Apache 2.0 · Sourcepip install overturo
import asyncio, os
from overturo_oversight import OverturoOversight, evidence_digest
async def main():
client = await OverturoOversight.create(
base_url = "https://overturo.com",
token = os.environ["OVERTURO_ATTESTER_TOKEN"],
application_id = os.environ["OVERTURO_APPLICATION_ID"],
heartbeat_enabled= True,
)
await client.attest(
agent_class = "data_export_agent",
action_class = "mcp_tool_call",
decision = "allow",
legal_basis = "legitimate_interests",
evidence_digest = evidence_digest(opa_input, opa_output),
)
await client.close()
asyncio.run(main())
Synchronous wrappers (e.g. client.attest_sync(...)) are available for non-async callers.
overturo/opa-adapter
Go sidecar binary · v1 · Apache 2.0 · SourceBridge between Open Policy Agent's decision log and OAP attestations. No application code change — point OPA's decision log at the adapter.
docker pull overturo/opa-adapter:v1
# In your OPA config:
decision_logs:
console: false
service: overturo
services:
- name: overturo
url: http://opa-adapter:8181/v1/logs
# Then run the adapter with the attester token:
docker run -d \
-e OVERTURO_BASE_URL=https://overturo.com \
-e OVERTURO_ATTESTER_TOKEN=$OVERTURO_ATTESTER_TOKEN \
-e OVERTURO_APPLICATION_ID=$OVERTURO_APPLICATION_ID \
-p 8181:8181 overturo/opa-adapter:v1
Coverage gaps to be aware of
-
No first-party Ruby OAP SDK. The
overturoRuby gem is a generic REST client for the Overturo HTTP API, not an OAP library. For Ruby OAP, verify receipts directly using any Ed25519 JWS library against the published JWKS — the wire shape is documented in the Receipts tab. - No Java or Go OAP library SDK. The Go binary above is a sidecar for OPA, not an embeddable library. Counterparties on those runtimes verify receipts using their language's standard Ed25519 + JWS libraries.
-
Don't confuse
@overturo/jswith the OAP SDKs. That package is the consent-flow embed SDK for the Connect / Consent surfaces. The OAP TypeScript SDKs are the fourauthorize/verify/mcp-authorize/oversight-sdkpackages above.
REST API client (not OAP-specific)
The overturo Ruby gem (Apache-2.0) is a generic client for the Overturo HTTP API. Useful for application-management tasks (creating applications, configuring webhooks, etc.) but does not implement OAP grant issuance, attestation submission, or receipt verification.
Source: github.com/overturo/sdk-ruby.
OAP for Agents (OIDC-A 1.0)
Overturo Authorize (OAP) accepts OIDC-A 1.0–compatible ID Tokens as an agent credential. This page covers the acceptance pipeline, the receipt overlay claims, the discovery extensions, and the error reference.
Overview — authorization above identity
OAP and OIDC-A cover different layers of the agent-trust stack. OIDC-A is an OpenID Connect extension that standardises agent identity claims. OAP is the bounded-authorization-and-commerce layer that sits above any agent identity substrate.
-
OAP consumes OIDC-A identity proofs. When an agent vendor publishes OIDC-A 1.0–compatible ID Tokens, principals pass them as
agent_credentialat grant creation. OAP extracts the standardised identity claims and binds them to the grant. - OIDC-A-compatible agents integrate one step shorter. No bespoke agent registration: if your IdP already issues OIDC-A ID Tokens, the same token authorizes grant creation in OAP.
- OAP is the bounded-authority layer above any standard agent identity layer. OIDC-A tells you who the agent is. OAP enforces what the agent is allowed to do — with per-action receipts, escalation, audit, revocation, and commerce primitives the identity layer doesn't cover.
Overturo claims compatibility at identity boundaries, not OIDC-A conformance. OAP is not an OIDC-A extension; it adopts OIDC-A claim names where the two specs overlap.
Grant creation — the OIDC-A ID Token path
agent_credential accepts an OIDC-A 1.0–compatible ID Token issued by a trusted Authorization Server (e.g. the agent vendor's identity service), alongside the existing SD-JWT and W3C VC paths. The token MUST:
- Be a compact-serialised JWT (
<header>.<payload>.<signature>) - Carry the OIDC-A §2.1 required claims:
iss,sub,aud,iat,exp,agent_type,agent_model,agent_provider,agent_instance_id - Have
audincluding the regional OAP issuer (https://us.overturo.com,https://eu.overturo.com, orhttps://ap.overturo.com) for the region where the grant is being created - Be signed under a key in the issuer's published JWKS, with an algorithm in the issuer's trust list entry (defaults:
EdDSA,ES256,RS256) - Not have been consumed before (cross-platform single-use per
(iss, jti); tokens withoutjtisynthesize one from the canonical hash)
When this path is used, the platform extracts agent_type, agent_model, agent_provider, agent_version, and agent_instance_id from the token's claims and stores them on the grant. The token bytes are discarded after grant creation; only the SHA-256 canonical hash and the extracted claims persist.
Maximum token size: 16 KB.
agent_metadata block (optional integrator self-report)
When agent_credential is an SD-JWT, W3C VC, or absent, the grant creation request MAY carry an agent_metadata block declaring the agent's identity. Unknown fields are rejected with agent_credential_malformed.
| Field | Type | Notes |
|---|---|---|
| agent_type | string | Closed taxonomy: assistant, retrieval, coding, domain_specific, autonomous, supervised |
| agent_model | string ≤ 200 chars | Vendor model identifier |
| agent_provider | string ≤ 200 chars | Vendor name |
| agent_version | string ≤ 100 chars | Vendor version string |
| agent_instance_id | string ≤ 200 chars | Unique-per-running-instance identifier |
Provenance: the block is stored with provenance: "integrator_self_report". The dashboard surfaces a neutral badge — the platform makes no claim that self-reported values are true. When both an OIDC-A ID Token and an agent_metadata block are supplied, every field present in BOTH MUST match exactly (agent_metadata_mismatch on conflict; detail.disagreeing_fields lists them).
Receipts — OIDC-A overlay claims
When the underlying grant carries agent_metadata, receipts emit additional informational claims alongside the OAP-native fields. Counterparties unaware of OIDC-A treat unknown claims as informational pass-through — receipts with overlay claims still verify against pre-spec-96 verifiers.
| Claim | Source | Notes |
|---|---|---|
| agent_type | grant.agent_metadata.agent_type | Closed taxonomy |
| agent_model | grant.agent_metadata.agent_model | – |
| agent_provider | grant.agent_metadata.agent_provider | – |
| agent_version | grant.agent_metadata.agent_version | – |
| agent_instance_id | grant.agent_metadata.agent_instance_id | – |
| delegator_sub | grant.agent_metadata.delegator_sub | Subject who delegated to the agent (optional) |
| delegation_purpose | grant.agent_metadata.delegation_purpose | Free-text purpose of the delegation (optional) |
| delegation_chain | grant.delegation_chain | Multi-hop delegation only; array shape below |
delegation_chain shape (multi-hop)
{
"iss": "https://us.overturo.com",
"sub": "user_alice",
"aud": "agent_xyz",
"delegated_at": "2026-01-01T00:00:00Z",
"scope": "wallet:usdc:spend",
"purpose": "Pay vendors",
"jti": "hop-1"
}
Validation rules per OIDC-A §2.4.2 — implemented by the reference verification libraries (Node + Python SDKs):
- Chronological ordering —
delegated_atincreases across the array. - Audience chaining —
audof hop N MUST equalsubof hop N+1. - Scope reduction — each hop's
scopeis a subset of the prior hop's.
Validation failures surface as delegation_chain_invalid.
Attestation — runtime configuration proof
OAP v1.1 accepts an optional agent_attestation block alongside the grant-creation body (and via the refresh endpoint). The attestation is a vendor-signed JWT asserting the agent runtime's configuration — build hash, model version, optionally hardware-attested provenance — distinct from the identity claims covered above. Identity says who the agent is; attestation says what configuration it's running in.
Signature verified, not content. When the platform accepts an attestation it has verified the provider's SIGNATURE against a trusted JWKS. The platform makes no representation that the provider's content claims (build hash, model version, agent_version, etc.) are true. Counterparties whose policy gates on attestation content take their own audit position on the provider's claims; the platform is a pass-through.
v1.1 scope: vendor-signed JWTs only. Hardware-attested formats (EAT, RATS, TPM, SEV-SNP, TDX) are v1.2+ per spec §1.1.
agent_attestation block (creation + refresh)
Closed shape, two fields. Submitted on POST /api/v1/grants (alongside the grant body) or POST /api/v1/grants/<grant_id>/attestation (refresh). Extra fields are rejected with agent_attestation_malformed.
| Field | Type | Notes |
|---|---|---|
| format | string | URI from agent_attestation_formats_supported (discovery). v1.1: urn:overturo:agent_attestation:vendor-jwt-v1 |
| token | string ≤ 16 KB | Compact JWT, signed by a trusted provider |
Example body submitted at grant creation:
{
"agent_id": "tp_app_abc123",
"agent_dpop_jkt": "<base64url SHA-256 of the agent's DPoP key>",
"scope": ["wallet:usdc:spend"],
"actions": ["pay"],
"valid_until": "2026-12-31T23:59:59Z",
"agent_attestation": {
"format": "urn:overturo:agent_attestation:vendor-jwt-v1",
"token": "eyJhbGciOiJFZERTQSIsImtpZCI6ImsxIn0.eyJpc3MiOi..."
}
}
Refresh endpoint
Agents refresh their own attestation BEFORE exp without re-prompting the principal:
POST /api/v1/grants/<grant_id>/attestation- Authenticate with the DPoP-bound agent token (same auth as
/authorize) - Required
Idempotency-Keyheader (UUIDv4) - Region-pinned: the request MUST land on the grant's home region (the SDK auto-routes on a 421
grant_region_mismatch) - Single-use per
(iss, jti)— cross-platform replay defence, 409agent_attestation_replayedon re-use
Receipt overlay claim
When a grant has a current, unexpired attestation, receipts emit an agent_attestation sibling overlay alongside the OIDC-A identity overlay. Six fields total:
| Claim | Notes |
|---|---|
| format | Attestation format URI (matches discovery) |
| iss | Attestation provider issuer URI |
| jti | Attestation token jti — retrieval handle for the endpoint below |
| iat | Integer seconds. Attestation token's issued-at time |
| exp | Integer seconds. Past this point the overlay claim is omitted from new receipts (§2.1.4) — the agent provider's chosen exp is the authoritative freshness signal |
| claims_digest | SHA-256 hex of the JWT compact serialization — retrieval-key handle |
The receipt deliberately does NOT carry the JWT bytes inline (size budget — receipts stay under 2 KB). Counterparties needing the bytes for content inspection fetch them via the attestation endpoint below.
agent_attestation_endpoint (token retrieval)
GET /.well-known/oap-attestation?jti=<receipt_jti>&digest=<claims_digest> (advertised in the discovery doc). Anonymous, cacheable for 5 minutes (private). Four documented status codes:
| Status | Meaning |
|---|---|
| 200 | Body: {format, token} — exactly two fields, no extras |
| 404 | Unknown receipt OR receipt has no attestation overlay (uniform — privacy) |
| 409 | digest does not match the attestation associated with the receipt (anti-probe) |
| 410 | Past 90-day retention (bytes cleared) or refresh-history-pruned |
Discovery extensions
GET /.well-known/openid-configuration advertises spec-96 OIDC-A compatibility alongside the standard OpenID Connect fields:
| Field | Type | Description |
|---|---|---|
| agent_claims_supported | array of strings | OIDC-A claim names the platform understands at input and emits at output |
| agent_types_supported | array of strings | Closed taxonomy intersected with platform-recognised values |
| delegation_methods_supported | array of URIs | Mechanisms the platform accepts as agent_credential |
| oap_jwks_uri | URI | OAP receipt-signing JWKS endpoint. Distinct from jwks_uri |
| oap_protocol_versions_supported | array of strings | OAP wire-format versions the issuer currently serves (e.g. ["1.0"]) |
| agent_attestation_endpoint | URI | Anonymous endpoint for fetching attestation JWT bytes by (jti, claims_digest) |
| agent_attestation_formats_supported | array of URIs | Attestation formats the pipeline accepts. v1.1: vendor-jwt-v1 |
| agent_attestation_signing_alg_values_supported | array of strings | Signature algorithms the verifier honors per-provider. v1.1: EdDSA, ES256, RS256 |
| agent_capabilities_endpoint | URI template | URL for the public capability discovery endpoint with the literal {agent_id} placeholder (OIDC-A §4.5 convention) |
| agent_capabilities_projection_versions_supported | array of strings | Projection versions the endpoint may emit on oidc_a.projection_version. v1.1 ships ["1"] |
jwks_uri vs oap_jwks_uri: the discovery document advertises two JWKS endpoints. jwks_uri serves the platform authorization server's RSA / ES256 keys for OAuth 2.0 / OIDC ID Token verification. oap_jwks_uri serves OAP-platform Ed25519 receipt-signing keys. Use oap_jwks_uri for OAP receipt verification; mixing the two will fail signature checks.
Capability discovery
OAP v1.1 ships a public, unauthenticated endpoint that lets counterparties discover what an agent is configured to do — its declared scopes, action verbs, bound annotations, and OIDC-A capability projection — before initiating a grant. Counterparties read this once at the start of a relationship and again whenever the agent's version_id rotates.
Not to be confused with the capability manifest at /.well-known/transparency/agents/:agent_id/manifests/:version — that surface serves the content-hashed declaration a durable authorization record pinned at grant time (post-grant frozen-pin verification); this endpoint serves the live configuration for pre-grant discovery. Both exist on purpose; they answer different questions.
Endpoint
GET https://<region-domain>/api/v1/agents/{agent_id}/capabilities
The full URL template is advertised in the discovery document under agent_capabilities_endpoint. The {agent_id} placeholder follows the OIDC-A §4.5 convention — substitute the actual id at fetch time. The path shape is agent_<region>_<base32> where the region segment is part of the id and routes the request to that agent's home region.
Response shape
{
"agent_id": "agent_us_AbCdEf...",
"region": "us",
"version_id": "tpcv_us_...", // immutable snapshot id
"published_at": "2026-05-21T12:34:56Z",
"scopes": [{ "id": "...", "label": "...", "parent": "...",
"custom": false, "status": "stable" }, ...],
"actions": ["read", "write", ...],
"bounds_supported": {
"scope": {}, "action": {}, "value": {},
"time": {}, "escalation": {}, "reputation": {}
},
"oidc_a": {
"agent_capabilities": ["oap:openid", "oap:profile", ...],
"projection_version": "1",
"agent_type": "assistant",
"agent_provider": "anthropic",
"agent_model": "claude-opus-4-7",
"agent_version": "2.0.1"
},
"cache": { "max_age_seconds": 300, "version_id": "tpcv_us_..." }
}
Freshness model
- ETag: strong validator (no
W/prefix), SHA-256 of the canonical-form response body.If-None-Matchreturns 304 when nothing changed. - Cache-Control:
public, max-age=300on 200 + 304 responses.no-storeon 4xx so intermediaries never cache rejections. - Cross-region: 307 redirect with
Cache-Control: public, max-age=86400when the agent's home region differs from the request host. The redirect fires before any DB lookup — privacy-preserving.
OIDC-A capability projection
The oidc_a.agent_capabilities array is a sorted, deduplicated, top-most-per-root list of "oap:<scope_id>" strings. v1 algorithm: drop custom + preview-status scopes, group by root segment, keep the most-general ancestor per group, prefix with oap:, sort lex. The version is pinned via projection_version so cached projections survive when new versions ship additively.
| projection_version | Description |
|---|---|
| 1 | v1.1 ships v1 only — 4-step projection over the scope list. Future projections (v2+) extend the discovery list additively. |
Privacy invariant — uniform 404
The endpoint returns a byte-identical 404 envelope (reason_code agent_not_discoverable) for FIVE sub-cases: unknown agent_id, application not of agent type, operator-suppressed (capabilities_published = false), no snapshot yet, and operator-managed suppression-list entry. An external observer cannot distinguish them — capability discovery never leaks whether a given agent_id exists in the system.
Rate limit: 60 req/min per IP for the GET path. OPTIONS preflight is intentionally unrate-limited so browser CORS handshakes never trip the throttle. Counterparties that need elevated quotas authenticate with their counterparty token.
Error reference
These errors fire during the OIDC-A ID Token acceptance pipeline at POST /api/v1/grants. All carry the standard OAP error envelope; HTTP status is 400 unless noted.
reason_code |
HTTP | Cause |
|---|---|---|
| agent_credential_malformed | 400 | Not a parseable JWT, missing required claims, or > 16 KB |
| untrusted_agent_credential_issuer | 400 | iss not in trust list for this region, or kid not in per-issuer allowlist |
| agent_credential_signature_invalid | 400 | Signature verification failed; detail.reason distinguishes jwks_fetch_failed, jwks_unreachable, jwks_malformed |
| agent_credential_expired | 400 | iat ≤ now < exp window failed (±60s clock skew) |
| agent_credential_audience_mismatch | 400 | aud didn't include the regional OAP issuer URI |
| agent_credential_replayed | 409 | Same (iss, jti) already consumed cross-platform |
| agent_metadata_mismatch | 400 | agent_metadata block and OIDC-A ID Token disagree on a field; detail.disagreeing_fields lists them |
| agent_type_outside_taxonomy | 400 | agent_type value not in the closed taxonomy |
Attestation acceptance errors (OAP v1.1)
Fire during the attestation pipeline at POST /api/v1/grants (creation) and POST /api/v1/grants/<id>/attestation (refresh). All carry the same OAP error envelope.
reason_code |
HTTP | Cause |
|---|---|---|
| agent_attestation_malformed | 400 | Not a parseable JWT, missing required claims (iss/aud/iat/exp/jti), unknown fields, or > 16 KB |
| agent_attestation_untrusted_provider | 400 | iss not in the regional trust list, OR kid not in the per-provider kid_allowlist |
| agent_attestation_signature_invalid | 400 | JWKS unreachable / malformed / wrong key / unsupported alg; detail.reason distinguishes |
| agent_attestation_expired | 400 | iat ≤ now < exp window failed (±60s clock skew) |
| agent_attestation_audience_mismatch | 400 | aud did not include the regional OAP issuer URI |
| agent_attestation_kid_revoked | 400 | Signature kid is in the operator's kid_revocations list for the provider |
| agent_attestation_replayed | 409 | Same (iss, jti) already consumed cross-platform |
| agent_attestation_identity_mismatch | 400 | Attestation's identity claims disagree with the grant's stored identity OR the prior attestation; detail.disagreeing_fields lists them |
| agent_token_grant_mismatch | 401 | Refresh path. Fires when EITHER (a) the URL :id does not match the grant the agent token was minted for, OR (b) the verified DPoP jkt does not match the grant's registered oap_agent_dpop_jkt. Both checks defend against agent tokens being reused across grants |
| grant_region_mismatch | 421 | Refresh request landed on the wrong-region endpoint — SDK auto-routes to the grant's home region (Misdirected Request) |
Capability discovery errors (OAP v1.1)
Fire on the public endpoint GET /api/v1/agents/{agent_id}/capabilities. The 404 envelope is byte-identical across all five sub-cases per the §2.1.2 privacy invariant — an observer cannot tell why a request failed.
reason_code |
HTTP | Cause |
|---|---|---|
| agent_id_malformed | 400 | agent_id failed format validation. The only 4xx that doesn't fold into the uniform 404 — format errors don't leak existence |
| agent_not_discoverable | 404 | Uniform across: unknown agent_id, application not of agent type, capabilities_published = false, no snapshot, operator suppression-list entry |
Platform operators
Trust-list management is deploy-managed, not runtime-mutable. Adding a new OIDC-A issuer requires a coordinated deploy by the platform operator.
- Hosted Overturo customers: contact your account manager to add a trusted issuer to your region.
- Self-hosted operators (OIDC-A identity trust list): trust-list shape, JWKS configuration, boot-time validation, rollout stages, and backout are covered in the platform operations runbook shipped with your deployment.
-
Self-hosted operators (capability discovery suppression list): the deploy-managed deny-list shape, when to use it (legal hold, regulator demand), and the per-application
capabilities_publishedtoggle that's the everyday alternative are documented in your operations runbook. -
Self-hosted operators (attestation provider trust list): the parallel trust list governing the OAP v1.1 attestation pathway — including per-
kidrevocation, the on-demand revocation sweep job, and the 90-day token-byte retention SLA — is covered alongside the identity trust list in the operations runbook.
Conformance
Conductor and Oversight conformance are orthogonal. A deployment may serve one, both, or neither at a given level. The live values for this deployment are advertised at /api/v1/oap/version.
Trust Surface conformance suite
In addition to the Conductor and Oversight conformance levels above, the Trust Surface delivery layer that hosts OAP human-approval flows has its own conformance suite. It validates:
Decidableinstance contract — every kind honors the 7-method interface, idempotentsubmit_decision, correctcanceltransitions- postMessage v2 envelope shape and per-kind, per-type schemas
- Surface invariants — CSP nonce, allowed-origin, sandbox attrs, deadline enforcement
- API shape vs.
/api/v1/openapi.jsondeclarations - Telemetry emissions match the published telemetry manifest
- Documentation linkcheck — every URL resolves, every code example parses
The public conformance corpus (signed-record fixtures, discovery fixtures, and the round-trip test) is at github.com/overturo/conformance.
Current deployment
OAP version: 1.0
Conductor conformance: federated
Oversight conformance: oversight_basic
Conductor conformance levels
Three nested levels. Each level inherits the required capabilities of the level below and adds its own. A deployment advertises the highest level whose required set it satisfies.
core
Required (10)
cascade_denial_categoriesfelicity_conditionshitl_timeout_invariantsix_bound_bounded_authoritysequence_boundstwo_register_redactiondenial_log_alertsreceipt_verification_offlinereceipt_verification_onlinerevocation_propagation_30s
Recommended (3)
purpose_of_use_level_1drift_detectioncontinuation_chains
delegated
Required (14)
cascade_denial_categoriesfelicity_conditionshitl_timeout_invariantsix_bound_bounded_authoritysequence_boundstwo_register_redactiondenial_log_alertsreceipt_verification_offlinereceipt_verification_onlinerevocation_propagation_30sdelegation_attenuationcascading_revocation_chainbounded_delegation_depthre_consent_rules_table
Recommended (3)
continuation_chainspurpose_of_use_level_1drift_detection
federated
Required (19)
cascade_denial_categoriesfelicity_conditionshitl_timeout_invariantsix_bound_bounded_authoritysequence_boundstwo_register_redactiondenial_log_alertsreceipt_verification_offlinereceipt_verification_onlinerevocation_propagation_30sdelegation_attenuationcascading_revocation_chainbounded_delegation_depthre_consent_rules_tablemulti_region_cross_region_ssowitness_network_integrationmerkle_anchoringcross_region_revocationdrift_detection
Recommended (3)
c2pa_attributionpurpose_of_use_level_1continuation_chains
Oversight conformance levels
-
oversight_basic -
oversight_full
oversight_basic covers attestation ingest, witness receipts, and the heartbeat / sequence-number contract. oversight_full adds the SDK + OPA reference adapter, evidence-package adapter, "what if Conductor" comparison surface, and hybrid mode coordinator. A Conductor-conformant deployment may serve no Oversight requests; an Oversight-conformant deployment may not implement the Conductor cascade at all.
Discovering conformance programmatically
GET /api/v1/oap/version returns the full conformance descriptor. See the Discovery tab for the response shape. Counterparties and partner systems should call this endpoint, not hard-code level names — the inventory of capabilities grows as new sub-specs ship.
Record conformance
The durable records this protocol produces carry their own conformance story: every record kind is live-shape-validated against the published receipt schema, and the claims line is deliberate — consent and notice records carry a self-declared conformance to ISO/IEC TS 27560:2023; pure authority records follow the standard's structure and claim 27560-aligned, never more. Details and field semantics: Docs → Receipts.
¿Listo para empezar?
Obtener un token de API