Skip to content

Based on your connection, there may be an Overturo site better suited to you โ€” your data would be stored in United States.

Developer documentation

Getting started, concepts, authentication, the API reference, consent in your own forms, receipts, webhooks and embedding.

Getting Started

Everything you need to integrate with Overturo.

5-minute quickstart

  1. 1

    Mint a decision token from your server

    curl -X POST https://api.overturo.com/api/v1/decisions \
      -H "Authorization: Bearer $OVERTURO_API_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"kind": "consent", "flow_id": "flw_..."}'
  2. 2

    Render it in your page

    <overturo-decision token="dts_..."></overturo-decision>
    <script type="module" src="https://cdn.overturo.com/js/v1/overturo.js"></script>
  3. 3

    Receive the outcome via webhook

    Configure a webhook URL in Application Settings โ†’ Webhooks. Subscribe to consent.granted / consent.denied. Same shape works for approvals (approval.granted / .denied / .countered).

Full walk-through: the Embedding tab.

The 7 Surfaces

Documentation

Concepts & Vocabulary

The terms that show up across the SDK, API, and webhook examples โ€” what each one is, what it looks like, and where you'll see it.

The Decision primitive

A Decision is anything Overturo asks a principal to decide โ€” consenting to data sharing, approving an AI agent's action, signing an attestation. v1 ships two kinds: consent (a user permits your app to read or use their data) and approval (a human authorizes an action proposed by an AI agent or workflow).

Both kinds ride the same Trust Surface delivery layer โ€” same iframe, same SDK, same postMessage protocol, same webhook contract. The kind-specific work lives in the body of the decision (the question being asked), not the chrome around it.

Canonical entry point: the Embedding tab.

At a Glance

Term Prefix In one line
Decision dts_* A bounded ask: consent or approval.
Application tp_* Your project on Overturo.
Publishable Key pk_test_* / pk_live_* Public credential โ€” safe to ship in browser code.
API Token (opaque bearer) Server-only credential โ€” full account access.
Flow flw_* A reusable consent template you author once.
Consent Session (opaque) A short-lived link between a user and a flow.
Consent Token (opaque) The user's receipt of consent. Verify server-side.
AI Agent Token ra_agent_* Bounded credential for AI agents acting on a user's behalf.
Delegation Grant dlg_* Permission for one human to act on another's behalf.
Audit Trail โ€” Tamper-proof record of every consent decision.

1. Application

An Application is your project on Overturo. It owns the credentials you use to make API calls and the flows you publish to your users. You create one from Connect.

Each application gets a unique ID with format tp_<region>_<hashid> (for example tp_dev_7J580kDeZ6vGmnyxUaPAKblWโ€ฆ in development, tp_us_โ€ฆ or tp_eu_โ€ฆ in production), a publishable key for client-side identification, and one or more API tokens for server-side calls. Webhook deliveries, audit-trail records, and rate limits are all scoped per-application.

You'll see it as: the application_id field in webhook payloads and API responses.

2. Publishable Key

A Publishable Key identifies your application from client-side code (browser, mobile, embedded SDK). It cannot read sensitive data, modify resources, or list other users โ€” only create consent sessions. Safe to commit to source control and ship in JavaScript bundles.

Two flavours: pk_test_* hits a sandbox; pk_live_* hits production. Sent on every request as the X-Publishable-Key header โ€” the SDK does this for you when you pass publishableKey to the constructor.

Find it in: Credentials โ†’ Publishable Keys. Used in: JavaScript SDK initialization.

3. API Token

An API Token is a server-only bearer credential with full account-level access. Use it for server-to-server calls: creating applications, managing flows, fetching audit records, processing webhooks.

Treat like a password: never expose to a browser, never commit to source control, rotate regularly. Sent as Authorization: Bearer <token>. The token itself is an opaque high-entropy string with no marketed prefix; the surrounding record (creation time, scopes, last-used) has a api_token_* ID you'll see in the dashboard.

Find it in: Credentials โ†’ API Tokens. Used in: Authentication ยง1.

4. Flow

A Flow is a reusable consent template you author once and reference for every consent session. It declares: which data fields the user is consenting to share, the purpose of use, retention, revocation policy, and the multi-step UX the user walks through.

A flow has a stable ID like flw_dev_V3d1oQ6Zโ€ฆ (or flw_* in production). Once activated, a flow's terms are immutable โ€” to change them, you publish a new version; the old version keeps governing previously-collected consents.

You'll pass it as: flowId when creating a consent session.

7. AI Agent Token

An AI Agent Token (prefixed ra_agent_*) is a credential issued to an AI agent acting on a user's behalf. Unlike a user's API token, it carries explicit authority bounds โ€” scope, action, value, time, escalation, reputation โ€” that the user set when delegating.

See: Authentication ยง4 for the issuance flow and the bounds reference.

8. Delegation Grant

A Delegation Grant (format dlg_<region>_<hashid>) lets one human act on another's behalf โ€” guardian for a minor, caregiver for a patient, legal POA, IT admin for an employee, peer for a friend. The delegate authenticates as themselves and includes the grant ID in the X-Delegation-Grant-Id header alongside their own token.

Different from an agent token: agent tokens are for AI software, delegation grants are for human-to-human authority. See: Authentication ยง5.

9. Audit Trail

Every consent decision โ€” granted, denied, modified, revoked โ€” is recorded in a tamper-proof audit trail. Each record is hash-chained to the one before it, so a single after-the-fact edit invalidates every later record's signature. The chain head is published to an external witness every fifteen minutes, which means even Overturo can't rewrite history retroactively without producing visible evidence.

Three guarantees this gives you:

  • Non-repudiation. A consent that was granted can be proven granted at a specific time, and a denial can be proven denied โ€” no matter who later disputes it.
  • Witness trail. If the consent record changes, the cryptographic chain breaks; an external auditor can detect tampering without trusting Overturo.
  • Discoverable. The full history of any consent โ€” every state transition, every actor, every signature โ€” is queryable from your application's audit endpoints.

Practical implication: when a regulator, partner, or end-user asks "did this user actually consent?", the audit trail is the answer of record. Don't keep your own duplicate log unless you have a compliance reason โ€” the canonical trail is here.

Cross-border & residency

Every person's data is stored in their own country and stays there. When an integration spans countries, Overturo doesn't move the data โ€” it moves the smallest possible proof of a fact across the border. The substance stays home; the trust travels.

Two different things can cross a border:

  • Minimized status โ€” an opaque identifier, a status, a count, or a person's display initials. Never personal data. This is how a consent or a signature is known elsewhere without the record leaving its country.
  • A recorded transfer โ€” when personal data genuinely must cross, it is written to the tamper-proof transfer register with the lawful basis it relied on. A transfer basis is a distinct legal concept from the consent/processing basis.

A resource is addressed in the country where it's homed; calling the wrong country returns an explicit error telling you which country to call instead. The exact error contract is documented on the Authentication tab.

Honest boundary: data stays in its country today via per-country storage; the physical single-country guarantee strengthens as dedicated in-country infrastructure rolls out. We say which is which rather than imply the physical guarantee is already complete.

Surface Contract Specs

Overturo is organized into seven product surfaces. Each surface provides a focused set of APIs and capabilities for a specific aspect of personal data management.

Shared delivery layer: all seven surfaces share a single Trust Surface delivery layer for the moments when they need to ask a principal to make a decision — consenting to data sharing (Connect / Consent), approving a delegate action (Delegate / Comply), signing an attestation (Verify / Agree). The chrome, branding, deadline, SDK, and webhook contract are uniform across surfaces. See the Embedding tab.

Connect

Application registration and identity linkage.

  • Application configuration
  • Webhook configuration
  • Trust pairings
  • Device linking
  • Environment management
/applications /device_links /trust_pairings /connections Open surface →

Verify

Credential issuance, verification, and trust scoring.

  • W3C Verifiable Credentials
  • SD-JWT selective disclosure
  • Present-anywhere verification (issue in country, verify anywhere)
  • Trust scores
  • Vouch-based reputation
/credentials /presentations /trust_scores /vouches Open surface →

Protect

Data policies, access controls, and the data economy.

  • Data policy creation
  • DSAR management
  • Data licensing
  • Data offers and RFOs
  • Secure transfers
/policies /data_requests /data_licenses /data_offers Open surface →

Agree

Multi-party agreements and digital signatures.

  • Multi-party agreements
  • Cross-border co-signing (data stays home)
  • Quorum voting
  • Collaborative workspaces
  • Agreement templates
/agreements /multi_party_agreements /decisions /collaborations Open surface →

Delegate

Authority delegation to AI agents and third parties.

  • Delegation grants
  • AI agent registration
  • Bounded authority (7 types)
  • Jurisdiction-bounded agents (refuse out-of-bound crossings)
  • Durable authorization receipts + declared disclosure receipts
  • Emergency access
  • Security event streaming
/delegations /agent_identities /application_agents /authorities /authorization_receipts /disclosure_receipts Open surface →

Comply

Regulatory compliance and cross-border evidence.

  • Cross-border transfer register
  • Adequacy verdict
  • Cross-border data access (DSAR)
  • Proof of Residency
  • Pluggable compliance modules
  • Governance frameworks
  • Evidence packages
  • Clean rooms
  • External witnessing
/transfer_register /residency_posture /modules /governance /evidence_packages /clean_rooms Open surface →

Unified Trust Inbox

A single, per-principal list of pending decisions covering both consent requests and approval requests. Lives at /inbox.

/inbox

Cross-surface aggregation. Pending consent flows (from the Consent surface) and pending approval requests (from the Delegate surface) are unioned into one filterable, paginated list. The principal navigates from any row to the canonical per-decision URL at /decisions/:session_token.

  • Consent + approval rows in one list, filterable by kind
  • Trigger-type filter for approvals (scope, action, value, time, escalation, reputation)
  • Application-scoped narrowing via ?application_id=tp_dev_โ€ฆ
  • Deadline-ascending or newest-first sort
  • Auto-submitting Stimulus filter UI; CSP-clean
/inbox /decisions/:session_token

Migration note: the legacy /oap/escalations operator inbox URL has been retired. Per-row URLs (/oap/escalations/:id) and the JSON API namespace under /api/v1/oap/escalations remain unchanged.

Authentication Guide

Five authentication mechanisms, each suited to different integration scenarios.

At a Glance

Method Use Case Header
API TokenServer-to-server REST API callsAuthorization: Bearer <token>
OAuth 2.0 / OIDCUser-facing apps needing delegated accessAuthorization: Bearer <access_token>
Publishable KeyClient-side SDK and embed integrationsX-Publishable-Key: pk_live_*
AI Agent TokenAI agents acting on behalf of usersAuthorization: Bearer ra_agent_*
Delegation GrantHuman delegates acting on behalf of another userX-Delegation-Grant-Id: <grant_id>

1. API Tokens

API tokens provide simple bearer authentication for server-to-server calls. Create tokens from Credentials.

Include the token in the Authorization header:
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  https://overturo.com/api/v1/me

Security: API tokens grant full account-level access. Never expose them in client-side code, public repositories, or browser requests. Use publishable keys for client-side integrations instead.

2. OAuth 2.0 / OIDC

For applications that need to act on behalf of users, use the OAuth 2.0 Authorization Code flow with PKCE. Overturo is an OpenID Connect provider.

Step 1: Redirect the user to the authorization endpoint:

Authorization URL
GET /oauth/authorize?
  client_id=YOUR_CLIENT_ID&
  response_type=code&
  redirect_uri=YOUR_REDIRECT_URI&
  scope=openid+profile+email&
  state=RANDOM_STATE

Step 2: Exchange the authorization code for tokens:

Token exchange
curl -X POST https://overturo.com/oauth/token \
  -d grant_type=authorization_code \
  -d code=AUTHORIZATION_CODE \
  -d redirect_uri=YOUR_REDIRECT_URI \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET

Step 3: Use the access token for API calls:

curl -H "Authorization: Bearer ACCESS_TOKEN" \
  https://overturo.com/api/v1/me
Available Scopes
Scope Description
openidOpenID Connect identity token
profileUser profile information (name, picture)
emailUser email address
offline_accessRefresh token for long-lived sessions
audit:verifyAPI-token scope: read authorization receipts (GET /api/v1/authorization_receipts/:id)
disclosures:writeAPI-token scope: record declared agent disclosures (POST /api/v1/disclosure_receipts)
Additional Grant Types
  • Device Authorization (RFC 8628) โ€” for devices without a browser
  • Token Exchange (RFC 8693) โ€” for AI agent delegation flows
  • Pushed Authorization Requests (RFC 9126) โ€” for secure parameter transmission

3. Publishable Keys

Publishable keys (prefixed pk_live_ or pk_test_) are safe for use in client-side code. They identify your application but cannot access sensitive data.

SDK initialization or HTTP header:
import { Overturo } from "@overturo/js"

const overturo = new Overturo({
  publishableKey: "pk_live_abc123"
})

// Or via HTTP header:
// X-Publishable-Key: pk_live_abc123
Allowed Origins

Browser-based SDK calls and embedded consent flows for an application can only originate from URLs registered in that application's allowed origins. Server-side calls (Bearer tokens) are unaffected.

Add an origin via the dashboard: Connect โ†’ choose your application โ†’ Allowed Origins tab โ†’ enter https://yourdomain.com โ†’ Add.

Add via the API:

curl -X POST \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"allowed_origin":{"origin":"https://yourdomain.com"}}' \
  https://overturo.com/api/v1/applications/YOUR_APP_ID/allowed_origins

Debugging "CORS preflight failed": the application's Activity tab shows cors.preflight_denied events for any origin that hit a preflight without being registered.

Allowed Origins vs embed_origin: Allowed origins is a per-application registration (set once, applies to all sessions). embed_origin is the per-session URL the SDK was initialized on (the iframe host). Both must agree โ€” a session can't be created with an embed_origin that isn't in the application's allowed origins.

Allowed Origins vs redirect_uri: these are independent registrations. Redirect URI controls where OAuth callbacks may land (full URL with path, server-side check). Allowed Origins controls where browser-direct API calls and iframes may originate (origin only). Editing one does not edit the other.

4. AI Agent Tokens

AI agents authenticate using agent tokens (prefixed ra_agent_) obtained through the token exchange flow. These tokens carry bounded authority constraints.

  1. Register an AI agent identity via the Delegate API
  2. The user grants a delegation with specific authority bounds
  3. Exchange the delegation grant for an agent token via /oauth/token/exchange
  4. Use the agent token for API calls, including the MCP gateway
Agent token usage:
curl -H "Authorization: Bearer ra_agent_abc123..." \
  https://overturo.com/mcp/v1/agent

Wiring MCP into Claude Desktop, Cursor, or VS Code? MCP Launchpad pre-fills the config for you.

Authority Bounds
Bound Description
ScopeWhich API scopes the agent can access
ActionSpecific operations allowed (read, write, delete)
ValueMaximum monetary value per transaction
TimeToken validity window (expiry time)
EscalationWhether the agent can request additional permissions
ReputationMinimum trust score required for the agent

5. Delegation Grants

Delegation grants allow a human delegate (guardian, caregiver, authorized representative) to make API calls on behalf of another user.

  1. The principal creates a delegation grant specifying the delegate and allowed scopes
  2. The delegate authenticates normally with their own credentials
  3. The delegate includes the X-Delegation-Grant-Id header alongside their Authorization header
  4. The API processes the request in the context of the principal
Include both headers:
curl -H "Authorization: Bearer DELEGATE_USER_TOKEN" \
  -H "X-Delegation-Grant-Id: dlg_dev_abc123" \
  https://overturo.com/api/v1/me
Delegation Types
Type Description
GuardianLegal guardian acting for a minor or dependent
CaregiverCaregiver managing data on behalf of a care recipient
Power of AttorneyLegally authorized representative
IT AdminOrganizational administrator managing user accounts
PeerTrusted peer with limited delegated access

Key difference from AI Agent Tokens: Delegation grants are for human-to-human delegation. The delegate authenticates as themselves and the grant establishes who they are acting for. AI agents use their own ra_agent_* token instead.

Country-aware addressing

Each person's records are homed in their own country, and the API serves a resource from the country where it's homed. You don't choose the country per request โ€” it follows the subject. The home country is reported as an ISO country code (for example IT).

If you address a resource through the wrong country, the API returns an explicit error carrying a home_country field that tells you which country to re-issue the request against. It never leaks the resource or its data โ€” a "wrong door" signal, not a failure.

The exact error code and JSON shape are in the OpenAPI reference.

OAuth Authorization

Pushed Authorization Request (PAR) โ€” required for every authorization flow. Sits alongside the REST API but follows OAuth 2.0 conventions rather than RESTful CRUD.

Endpoint

POST /oauth/par Pushed Authorization Request (RFC 9126) โ€” also at /oauth2/par

PAR is required for all OAuth clients (require_par: true is the platform-wide default). Push your authorization parameters server-to-server, then redirect the user to /oauth/authorize with only client_id and request_uri.

Discovery

Standard OIDC discovery endpoints live under /.well-known/* โ€” see the Authentication tab.

The discovery document also advertises five OIDC-A extension fields (agent_claims_supported, agent_types_supported, delegation_methods_supported, oap_jwks_uri, oap_protocol_versions_supported). See the AI Agent Authorization โ†’ OIDC-A Interop surface for the full schema.

API Reference

The Overturo API is organized around REST. All requests and responses use JSON.

Decisions API โ€” canonical session-mint endpoint

POST /api/v1/decisions is the canonical endpoint for creating any Trust Surface session. It supersedes POST /api/v1/consent_sessions (now a deprecated alias).

Request body carries a closed-enum kind discriminator: "consent" or "approval". Response is kind-uniform โ€” the session_token resolves to a kind-specific Decision under the hood. See the Embedding tab for the full walkthrough.

The closed error-code reference for /api/v1/decisions/* is drawn from TrustSurface::Errors::REASON_CODES and visible in the OpenAPI explorer.

OpenAPI Reference: OpenAPI Reference · openapi.json

Base URL

https://overturo.com/api/v1/

Authentication

The API supports multiple authentication mechanisms. See the Authentication tab for full details.

Most common: Bearer token
Authorization: Bearer YOUR_API_TOKEN

Response Format

All responses are returned as JSON. Successful responses include the resource or collection directly. Errors include a message field.

{
  "id": "app_abc123",
  "name": "My Application",
  "environment": "sandbox",
  "created_at": "2025-01-15T10:30:00Z"
}

Error Codes

Code Meaning
400Bad Request โ€” invalid parameters or malformed JSON
401Unauthorized โ€” missing or invalid authentication
403Forbidden โ€” authenticated but insufficient permissions
404Not Found โ€” resource does not exist or is not accessible
422Unprocessable Entity โ€” validation errors on the submitted data
500Internal Server Error โ€” something went wrong on our end

Working with AI agents? The AI Agent Authorization surface covers OAP end to end โ€” Conductor and Oversight modes, receipts and verification, SDKs, and the OIDC-A 1.0 ID Token acceptance pipeline at OAP for Agents โ†’ Grant creation.

Well-Known Endpoints

Discovery endpoints following standard specifications. Receipt-specific machine endpoints (the published receipt schema and export flavors) are documented on the Receipts tab.

Endpoint Purpose
/.well-known/openapi REST API schema (OpenAPI 3.1)
/.well-known/api-catalog RFC 9727 catalog of API descriptions on this host
/.well-known/openid-configuration OAuth 2.0 / OpenID Connect authorization server discovery
/.well-known/jwks.json JSON Web Key Set for authorization server token verification
/.well-known/did.json DID document for decentralized identity
/.well-known/openid-credential-issuer Verifiable credential issuer metadata
/.well-known/credential-status/:id Verifiable credential status list (by status list id)
/.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/ssf-configuration Shared Signals Framework configuration
/.well-known/mcp-configuration Model Context Protocol discovery
/.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
/surfaces/developer/mcp Pre-configured client config snippets and a disposable-agent generator.

Cross-border endpoints

Read and prove the cross-border story. All are scoped to your application and carry no personal data unless noted. Full reference is in the OpenAPI document.

Method & path What it returns
GET /api/v1/transfer_register Cross-border transfers, each with its lawful basis and adequacy verdict.
POST /api/v1/transfer_register/evidence_package A signed, independently-verifiable evidence package of the register.
GET /api/v1/residency_posture Where your subjects' data is stored, per country, with an assurance status.
POST /api/v1/residency_posture/evidence_package A signed Proof of Residency โ€” proves data stayed in-country.
POST /api/v1/presentations Verify a credential presented from another country, against the issuing country's published key, with no callback.

A subject's right of access / portability is fulfilled across countries through the self-service data export. The signing decision kind rides the Decisions API โ€” see the Embedding tab.

Embedding a Decision

Four delivery modes (embed, popup, redirect, direct) for both kinds of Decision (consent, approval). Pick the simplest that meets your needs โ€” you can always upgrade later. The same <overturo-decision> element and the same postMessage protocol handle both kinds.

Keeping your own form? The Forms documentation covers the Overturo field, the capture snippet, Typeform and the Record API.

Unified surface URL family

The canonical participant URL is /decisions/:session_token (and /decisions/:session_token/embed for iframes). All Decision kinds โ€” consent today; approval, signature, verification in future โ€” mount on the same routes. The legacy /accord/:id and /oap/escalations/... URL families have been retired in favor of the unified URL family.

New: /api/v1/decisions/* session-mint API

The matching API endpoint family is now /api/v1/decisions with a closed-enum kind discriminator. Both URL families share session tokens โ€” a session minted via either URL resolves under either. The legacy /api/v1/consent_sessions/* endpoints continue to work as a thin alias subclass and emit RFC 8594 Deprecation + Sunset headers. See the OpenAPI reference for the full endpoint contract.

At a Glance

Approach Snippet JS required? Callbacks Use when
Redirect link <a href=โ€ฆ> No Webhook (server-side) Simplest path. User leaves your site, completes the Flow, returns to a callback URL. Token arrives via webhook.
Inline iframe <iframe src=โ€ฆ> Optional (for events) postMessage Show the Flow inside your page; user never leaves.
JavaScript SDK @overturo/js Yes (npm) Function callbacks Multi-step app integration; popup mode; programmatic embed creation.

1. Choosing your integration

Start at the top and stop at the first "yes":

  • Is a plain link acceptable โ€” user clicks, navigates to Overturo, completes the Flow, returns to your site? โ†’ Redirect link. One line of HTML, no JavaScript.
  • Do you want the Flow to render inside your page without the user navigating away? โ†’ Inline iframe. One line of HTML; add a postMessage listener if you need to react to the consent result.
  • Do you need callbacks (success / cancel / error), popup mode, dynamic Flow IDs, or anything beyond a static embed? โ†’ JavaScript SDK. npm install @overturo/js + a few lines of code.

All three approaches reach the same Flow. The choice is purely about how much surrounding JavaScript you're willing to write โ€” not about which features you can use.

2. Prerequisite: activate your Flow first

Embedding works only against active Flows. A Flow in draft status will return 404 from the Flow URL and reject session creation. Activation is one click in the Flow's editor โ€” and once activated, the Flow's terms are immutable, so do it after you've confirmed the field set, retention, and presentation are correct.

Where to activate: on the Consent surface, open your Flow โ†’ click Activate. The Flow's Embed tab unlocks at that point and starts showing copy-paste snippets pre-baked with your Flow's URL and publishable key โ€” see ยง6 below.

2.5 Prerequisite: register your origin

Browser-based integrations (ยง4 iframe, ยง5 SDK) load resources and call API endpoints from your site's origin. The Application's CORS allowlist must include that origin or the browser blocks the request โ€” typically with no error visible inside the Flow, just a failed preflight in the network tab. Server-side calls authenticated with a Bearer token are unaffected.

Where to register: Connect surface โ†’ your Application โ†’ Allowed Origins tab. Add the exact origin (e.g. https://yourdomain.com) โ€” no path, no query string, no trailing slash.

  • Production Applications require HTTPS.
  • Development Applications also accept http://localhost and http://127.0.0.1 (any port).
  • The tab shows the last preflight timestamp per origin so you can confirm a registration is taking effect.

3. Redirect link

The simplest possible integration: a plain <a> tag pointing at the Flow's hosted page. Zero JavaScript. The user clicks, walks through the Flow on a page hosted by Overturo, then returns to your redirect_uri.

<a href="https://trust.overturo.com/accord/FLOW_ID?redirect_uri=https://yoursite.com/consented">
  Give Consent
</a>

Substitute FLOW_ID with your Flow's ID (format flw_dev_โ€ฆ in development, flw_us_โ€ฆ / flw_eu_โ€ฆ in production โ€” see Concepts ยง4).

How completion is signalled

Redirect mode signals completion in two channels โ€” the URL the user lands on, and a webhook your backend receives. The trust signal is the webhook; the URL is for UX.

  • On success โ€” the user lands at your redirect_uri with no query parameters appended. Do not infer consent from the bare arrival; an attacker can hit your callback URL directly.
  • On decline โ€” the user lands at redirect_uri?error=access_denied. Use this to render a "you can try again" page; do not treat absence of error as proof of consent.
  • The Consent Token arrives via webhook โ€” when the user completes the Flow, your Application's webhook endpoint receives a consent.granted event carrying the consent token, the user identifier, and the Flow ID. That's the trust boundary. Verify the webhook signature, look up the user, and treat consent as granted from that point.

Wiring webhooks: see the Ruby SDK's webhook handler reference (search for "Webhooks") โ€” the same payload shape applies regardless of the language receiving it. Webhook endpoints are configured per-Application on Credentials โ†’ Webhooks.

The redirect URL is for UX, not trust. Pure-redirect mode is the simplest way to route the user, but the only authoritative signal that consent was granted is the webhook (or, equivalently, querying the API for that user's fulfilled consent on the Flow). If your integration needs a token in the page that handles the redirect, use the inline iframe or JavaScript SDK instead โ€” both deliver the Consent Token directly to your client code via postMessage.

4. Inline iframe

Render the Flow inside an iframe on your page. The user never navigates away. You receive completion events via window.postMessage.

<iframe
  src="https://trust.overturo.com/accord/FLOW_ID"
  sandbox="allow-forms allow-scripts allow-same-origin"
  style="width: 100%; height: 600px; border: none;"
  allow="publickey-credentials-get *">
</iframe>

The sandbox attribute is required and must include allow-forms allow-scripts allow-same-origin at minimum. The allow="publickey-credentials-get" permission is needed for passkey-based authentication inside the Flow.

The hosting page's origin must be on the Application's CORS allowlist or the browser will block the iframe's API calls โ€” see ยง2.5.

Receiving events

The iframe broadcasts events on the parent window โ€” completion, cancellation, resize, ready. The handler shape is the same one the JavaScript SDK uses internally; the JavaScript SDK reference documents the full event vocabulary (look for "Handle Consent Events"). Always verify event.origin === "https://overturo.com" before processing โ€” the postMessage you receive is your prompt to verify the consent token server-side, not your proof of consent.

5. JavaScript SDK

For everything beyond a static embed โ€” function callbacks, popup mode, programmatic Flow IDs, dynamic configuration โ€” install @overturo/js and call overturo.consent({ flowId, container, onComplete }). The SDK handles session creation, iframe lifecycle, postMessage routing, and origin validation for you.

Three things the SDK gives you that the raw iframe doesn't:

  • Popup mode. overturo.popup(...) opens the Flow in a separate window and posts the result back when the user finishes โ€” useful when iframe sizing is awkward.
  • Function callbacks. onComplete, onCancel, onError, onResize, onStep โ€” typed callbacks instead of message-event dispatch.
  • Typed errors. OverturoApiError exposes statusCode and the response body โ€” easier to branch on than a generic fetch failure.

The hosting page's origin must be on the Application's CORS allowlist or every SDK call from the browser will fail preflight โ€” see ยง2.5.

Full reference: JavaScript SDK โ€” installation, complete examples, and error handling.

6. Per-Flow snippets

Each Flow has its own Embed tab in the Consent surface that generates copy-paste-ready snippets pre-baked with the Flow's actual URL, publishable key, and any sandbox / postMessage configuration you've set on the Flow. The snippets there are the same shapes documented above; the difference is they have your real values filled in.

The per-Flow Embed tab also lets you configure things this developer-portal page intentionally doesn't cover โ€” sandbox flag tuning, allowed parent origins for postMessage, embed styling, session-token issuance, and embed analytics. Those are operational concerns scoped to a specific Flow; this tab is the conceptual reference for what's possible across all Flows.

Delivery modes

The per-Flow Embed tab generates two HTML-shape snippets, and popup is an SDK-managed shape:

  • redirect โ€” same shape as ยง3 above, with your Flow's URL filled in.
  • iframe โ€” same shape as ยง4 above, with your Flow's URL and any sandbox-flag overrides you've set.
  • popup โ€” SDK-managed (window.open, postMessage, focus restoration), not a static HTML snippet. Use the JavaScript SDK's overturo.popup(...) method.

How to find the per-Flow Embed tab: Consent surface โ†’ open your Flow โ†’ Embed tab. Available once the Flow is active.

This per-Flow Embed tab generates snippets for one Flow across any host. There is a complementary per-Application configurator that generates snippets for one Application across any of its Flows โ€” useful when several Flows live under the same Application and you want the publishable key already filled in.

How to find it: Connect surface โ†’ open your Application โ†’ Integration tab. The tab includes a Flow picker, three runnable integration patterns (embed / popup / redirect), and an Authentication-only section for the standards-OAuth path (see the gate at the top of this page).

The signing decision kind

Cross-border signing is delivered as a third Decision kind โ€” signature_request โ€” alongside consent and approval. The kind is inferred server-side from the session token, so the <overturo-decision> markup is unchanged: you mount it exactly as you do for consent.

A signing decision resolves to one of four outcomes โ€” signed, declined, expired, countered โ€” surfaced on the complete event and the matching signature.* webhooks. Signing decision state also carries the signer's home country (an ISO country code) and the signature assurance level.

Client SDK support for the signing kind is coming. The signature_request kind, the signature.* outcomes, and the residency/assurance metadata are being added to overturo-js. The server capability is live today; until the client ships, drive the signing kind through the Decisions API directly.

7. postMessage protocol v2

Embedded Flows post messages to your host page through window.postMessage. Every envelope carries six required fields and conforms to a published JSON Schema (Draft 2020-12). The JavaScript SDK validates inbound messages automatically; manual listeners should validate too.

Envelope
{
  "type": "overturo:state",            // closed-enum, see below
  "kind": "consent",                   // "consent" | "approval"
  "version": 2,                        // always 2 for this protocol
  "payload": { ... },                  // per-kind-per-type schema
  "ts": "2026-06-05T12:00:00.000Z",    // ISO-8601 UTC, server-authoritative
  "message_id": "550e8400-..."         // UUID v4, unique per emission
}
Message types (closed enum โ€” 7)
Type Direction When it fires Payload shape (consent / approval)
overturo:readyframe โ†’ hostIframe finished loadingkind, session_token, deadline_at, branding, mode, locale
overturo:stateframe โ†’ hostState transitionfrom, to, step_index, total_steps (consent) / kind_payload (approval)
overturo:completeframe โ†’ hostUser finishedoutcome ("granted"/"declined" or "approved"/"denied"), exchange_token, completed_at
overturo:cancelbidirectionalUser declines or host cancelsreason, cancelled_by
overturo:errorframe โ†’ hostError in the flowcode, message, fatal, recoverable_via
overturo:resizeframe โ†’ hostContent height changedheight (integer pixels)
overturo:deadlineframe โ†’ hostT-30s / T-15s / T-5s / T-0remaining_seconds, level ("warning" / "expired"), deadline_at
Host-side listener (manual integration)

If you're not using the JavaScript SDK, attach a listener and check the envelope yourself. Always validate event.origin against the Overturo host before trusting the payload.

window.addEventListener("message", function (event) {
  // 1. Validate origin
  if (event.origin !== "https://overturo.com") return;

  // 2. Validate envelope shape
  var env = event.data;
  if (!env || env.version !== 2) return;
  if (typeof env.type !== "string" || !env.type.startsWith("overturo:")) return;

  // 3. Route on type + kind
  switch (env.type) {
    case "overturo:ready":
      console.log("[overturo] ready", env.kind, env.payload);
      break;
    case "overturo:state":
      console.log("[overturo] state", env.kind, env.payload.to);
      break;
    case "overturo:complete":
      // exchange_token is exchanged via POST /api/v1/decisions/:session_token/exchange
      handleDecisionComplete(env.kind, env.payload);
      break;
    case "overturo:cancel":
      console.log("[overturo] cancelled", env.payload.reason);
      break;
    case "overturo:error":
      console.error("[overturo] error", env.payload.code, env.payload.message);
      break;
    case "overturo:deadline":
      console.warn("[overturo] " + env.payload.remaining_seconds + "s remaining");
      break;
    case "overturo:resize":
      iframe.style.height = env.payload.height + "px";
      break;
  }
});
JSON Schemas (normative)

Every envelope and every payload has a JSON Schema. They ship in the SDK at lib/sdk/overturo-js/schemas/postmessage-v2/:

  • envelope.json โ€” top-level six-field envelope
  • ready.json, resize.json, cancel.json โ€” kind-agnostic payload schemas
  • consent/{state,complete,error,deadline}.json โ€” consent-specific payloads
  • approval/{state,complete,error,deadline}.json โ€” approval-specific payloads
Kind discriminator

The kind field is required on every envelope. consent covers user-facing consent flows; approval covers AI-agent action approvals. Both share the envelope, the seven message types, and the lifecycle vocabulary; only the per-payload semantics differ.

Origin invariant

The frame's postMessage call never uses "*" as the target origin. If your Application's allowed origin isn't configured, the frame drops messages and increments a trust_surface.postmessage_dropped_total counter on the server with reason=origin_missing. Configure your allowed origins on the Application's Connect tab.

Consent in your own forms

You keep your sign-up, lead, intake, booking or checkout form. Overturo becomes the consent system of record behind the checkbox: the versioned wording the person read, a signed receipt they hold, a withdrawal path that reaches your systems, and the audit evidence that demonstrates consent.

What the record proves

Overturo proves when the decision was recorded, which wording was in force, who attested it and that nothing has changed since; for a record an organisation captured in its own form, Overturo does not vouch that the control was shown unticked; for a record captured through the Overturo field, Overturo rendered the control, showed it unticked and recorded the person's own change, and does not vouch for what the page placed around it.

Every receipt names where the decision was captured and who attested it: an Overturo-rendered field inside your form, your form committed by your server, your form committed by the browser, a form platform's signed webhook, your server directly, or an import of records you already held.

Four ways in

PathYou needReceipt says
Your own form + the Overturo fieldTwo script tags, one element where the checkbox used to be, and a server that commits the capture tokenrendered by Overturo inside your form, shown unticked, committed by you
Your own form + the snippetOne script tag, a map from purposes to inputs, and a server that commits the capture tokencaptured in your form, committed by you
TypeformA connected form, a field mapping, Typeform's signed webhookcaptured in a Typeform form, delivered by its signed webhook
Your server (Record API, import)Your application credentialattested by you; imported records keep your original capture time

Want Overturo's page instead of your form? See Embedding.

Which flows can be captured

A flow can be captured when its consent needs nothing but a decision: no verification, data-collection or bank step, and a declared consent duration (what bounds how long the person's details are kept for a consent they never claimed an account for). A capture carries decisions and how they were presented, never collected data fields. The flow page's Records tab shows the verdict.

What a capture may attest

For every purpose the flow resolves, a capture states the decision and the presentation it was made under (shown ticked by default, mandatory). The same rules that govern Overturo's own decision screens apply here: a pre-ticked or mandatory box is never consent.

PurposeAccepted attestationRecorded as
Consent (explicit or informed)A decision, not default-ticked, not mandatorygranted or declined; an unticked box is a decline, never silence
Consent, any mechanismDefault-ticked or mandatoryrefused, with the purpose named
Legitimate interest with the right to objectA decision; default-ticked allowedticked โ†’ noticed; unticked โ†’ an objection on the transparency register
Notice-only (contract, legal obligation)No decisionnoticed

A purpose the flow does not resolve, or a missing consent purpose, is refused by name. Your terms-of-service acceptance is a contract purpose, not a consent checkbox.

1. Your own form

Newsletter, lead, checkout, promotion, loyalty, messaging and research opt-ins have ready-made flows under the Opt-ins solution; every one of them is capturable here.

The Overturo field: the control rendered by Overturo, inside your form

Place one element where the checkbox used to be. Overturo renders the published wording for the flow, shows the control unticked, and observes the person's change in a frame your page cannot script. At submit the field captures from its own frame and hands your form the capture token; your server commits it exactly as for the snippet. The receipt says the control was rendered by Overturo, which notice version was shown, and how visible the control was when the form was submitted. Choose the field when the receipt must prove how the control was shown; choose the snippet below when you must keep your own markup.

In your page:
<!-- Two scripts: the core and the field entry (loaded explicitly, never fetched behind your back) -->
<script src="https://overturo.com/sdk/v1/overturo.js"></script>
<script src="https://overturo.com/sdk/v1/overturo-field.js"></script>

<form id="signup-form" action="/signup" method="post">
  <input id="email" name="email" type="email" required>
  <!-- Overturo renders the consent control here: the published wording, shown unticked, in your form -->
  <overturo-consent-field name="consent" flow="flw_โ€ฆ" locale="en"
    appearance='{"fontFamily":"system","accent":"#2563eb","radius":"6px","density":"regular"}'></overturo-consent-field>
  <label><input name="terms" type="checkbox" required> I accept the terms</label>
  <button type="submit">Create account</button>
</form>
Attach it to the form:
const overturo = new Overturo({ publishableKey: "pk_live_โ€ฆ" })

// The field is a form control: at submit it captures from its own frame and
// hands the capture token to your form (a hidden input and the element's value).
overturo.forms.attach({
  flowId: "flw_โ€ฆ",
  form: "#signup-form",
  field: "overturo-consent-field",          // the element inside the form (one per flow)
  subjectField: "#email",
  externalIdField: "#customer_id",          // optional: your own identifier
  onCaptured: (r) => console.log("capture token", r.token),
  onError: (e) => console.warn("capture failed; the form submitted anyway", e.message),
})

// Then, on your server, commit the token with your application credential โ€”
// exactly as for the snippet. The receipt says the control was rendered by Overturo.

The field renders every purpose of the flow that takes a decision (name a subset with purposes="โ€ฆ", but a consent purpose can never be left out), notice-only purposes as text, and a right-to-object purpose ticked with the objection wording. It renders no required control: your terms-of-service checkbox stays your own. One field per flow per form. The application needs at least one allowed origin.

Appearance. A bounded vocabulary, applied inside the frame and never sent to Overturo: fontFamily (system, inter, serif, mono), fontSize, text, muted, accent, border, radius, density (compact, regular, relaxed); plus theme and locale attributes. An invalid value is ignored. A small "Recorded by Overturo" mark is always shown, so the person knows who holds their receipt.

The field needs no cookie: a signed render proof, known only inside the frame, authenticates the capture, so it works where third-party cookies are blocked. The host page learns nothing but the state of the controls and the capture token.

The snippet: your own control, captured at submit

At submit, before the form leaves the page, the snippet reads each mapped control (ticked or not, shown ticked by default or not, required or not), builds a sanitised snapshot (consent controls and labels kept, every other input value stripped) and posts one capture with your publishable key. It receives a capture token, writes it into a hidden input named overturo_capture_token, and lets the submission proceed. If Overturo is unreachable, the form still submits and the error event fires.

In your page:
import { Overturo } from "@overturo/js"

const overturo = new Overturo({ publishableKey: "pk_live_โ€ฆ" })

// Your existing form keeps every input. Map each consent control to its purpose,
// name the email field, and let the snippet capture the decision at submit.
overturo.forms.attach({
  flowId: "flw_โ€ฆ",
  form: "#signup-form",
  map: { marketing: "#marketing_opt_in", analytics: "#analytics_opt_in" },
  subjectField: "#email",
  externalIdField: "#customer_id",            // optional: your own identifier
  labels: { marketing: "#marketing_label" },  // the label the person reads: its digest is attested (match / drift in the record)
  // wording: { marketing: "#marketing_label" }, // or: write the published wording into the label
  onCaptured: (r) => console.log("capture token", r.token, "wording", r.wordingVerdict),
  onError: (e) => console.warn("capture failed; the form submitted anyway", e.message),
})

Nothing is recorded until your server commits the token with your application credential. The commit binds the person by the email you supply, freezes the receipt, sends the receipt email (unless you suppress it) and fires your webhooks.

On your server:
# The capture token came in with the form submission; commit it with your application credential.
curl -X POST "https://overturo.com/api/v1/decisions/$CAPTURE_TOKEN/authorize" \
  -H "Authorization: Bearer $OVERTURO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "subject": { "email": "[email protected]", "external_id": "crm-42" } }'
Ruby:
# Your form handler receives the hidden field overturo_capture_token with the submission.
client = Overturo::Client.new(api_key: ENV["OVERTURO_API_KEY"])

decision = client.consent.decisions.authorize(
  params[:overturo_capture_token],
  subject: {email: params[:email], external_id: current_customer.id},
  suppress_receipt: false
)
decision.status
decision.record_id  # the consent record; its receipt is at /api/v1/consent_receipts/{record_id}

An uncommitted capture expires after 24 hours and leaves no record. Name the labels the person reads (labels, purpose โ†’ selector) and the snippet attests a digest of each label's text with the capture, so the record says purpose by purpose whether your wording matched the wording published for the flow; or ask it to write the published wording into your labels (wording) so the two are the same by construction.

No server? Enable browser commit on the application (it requires allowed origins). The browser then commits in the same call and every such record carries a lower-assurance channel in its receipt.

2. Form platforms: TypeformBeta

A form hosted by Typeform cannot run the snippet and has no server to commit. Typeform's own signed submission webhook is the commit.

  1. On the application's Forms tab, connect the form: its form id and the flow it answers. Overturo gives you a webhook URL and a secret (shown once). With a Typeform personal access token connected, Overturo installs the webhook on the form for you and reads the form definition.
  2. Map the fields by reference: the email field; optionally a hidden field as your own identifier; one field per consent purpose (a yes/no or legal field, granted when true; or one choice of a multiple-choice, checkbox, dropdown or picture-choice field, granted when that choice is among the chosen); and one field per right-to-object purpose (the person must be able to object in the form, so it is mapped like a consent field).
  3. A consent purpose mapped to a field the definition marks required is refused at mapping time; so is a decision mapped to a field that cannot carry one (a rating, a number, free text), and a choice field mapped without the reference of the choice that means yes. Without a token the required flag cannot be read, so you attest that the field is optional and the receipt records that it was attested rather than read.

Each submission: the signature is verified (the previous secret is honoured for a day after a rotation), the response token is the idempotency key (a retry returns the existing record), the mapped field's title is the attested wording, the submitted time is the capture time, and a mapped consent field with no answer is a decline. Typeform's "send test request" is answered as accepted and records nothing; the connection shows "test delivery received".

Alternatives. A Typeform that redirects on completion can hand off to the hosted flow with the email recalled; the person confirms the address on the hosted page. A server that receives Typeform's webhook can call the Record API below. Prefer the connector when you want the wording drift verdict and no server; prefer the redirect when the person should see Overturo's own screen; prefer the Record API when you already process submissions server-side.

3. Your server: the Record API and the import

One call records a consent your server captured: the flow, the subject reference (email and optionally your identifier), the per-purpose decisions with their presentation, the time you captured it, an optional digest of the notice text, and an idempotency key of your choosing. A later request for the same person and flow with changed decisions produces a new record and withdraws the purposes that moved from granted to declined, so your webhooks fire.

Record one consent:
curl -X POST https://overturo.com/api/v1/consent_records \
  -H "Authorization: Bearer $OVERTURO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "flow_id": "flw_...",
    "subject": { "email": "[email protected]", "external_id": "crm-42" },
    "decisions": { "marketing": { "decision": "granted", "default_checked": false, "mandatory": false } },
    "captured_at": "2026-10-12T09:01:30Z",
    "idempotency_key": "signup-42"
  }'
Ruby:
client = Overturo::Client.new(api_key: ENV["OVERTURO_API_KEY"])

record = client.consent.consent_records.create(
  flow_id: "flw_...",
  subject: {email: "[email protected]", external_id: "crm-42"},
  decisions: {marketing: {decision: "granted", default_checked: false, mandatory: false}},
  captured_at: Time.now.utc.iso8601,
  idempotency_key: "signup-42"
)
record.status
record.purposes
Node:
import { OverturoConsentRecords } from "@overturo/sdk"

const records = new OverturoConsentRecords({ baseUrl: "https://overturo.com", apiToken: process.env.OVERTURO_API_KEY })

const { consent_record } = await records.create({
  flow_id: "flw_...",
  subject: { email: "[email protected]", external_id: "crm-42" },
  decisions: { marketing: { decision: "granted", default_checked: false, mandatory: false } },
  captured_at: new Date().toISOString(),
  idempotency_key: "signup-42",
})
console.log(consent_record.status, consent_record.purposes)

Historical consents are imported, not back-dated: each row keeps your original capture time as captured_at and the platform's time as recorded_at; the declared duration counts from your time; no receipt email goes out unless you ask; rows that break the rules above are refused by name. Run a dry run first.

Import, dry run:
# Dry run first: what would be recorded, what would be refused โ€” nothing is written.
curl -X POST https://overturo.com/api/v1/consent_records/imports \
  -H "Authorization: Bearer $OVERTURO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "flow_id": "flw_...",
    "dry_run": true,
    "records": [
      { "subject": { "email": "[email protected]" },
        "decisions": { "marketing": { "decision": "granted" } },
        "captured_at": "2025-03-01T10:00:00Z",
        "idempotency_key": "crm-row-1" }
    ]
  }'
# Then read the outcome: GET /api/v1/consent_records/imports/{id}

What flows back

  • The person receives the receipt with the wording as published, your organisation named, and the account offer; a person with an existing account sees the record among their decisions with a "This was not me" action.
  • Your consent.granted, consent.revoked and consent.purpose_revoked webhooks carry the subject reference you supplied and the per-purpose outcome.
  • With a connected mailing audience, every record event is written to the member's marketing permissions and tags, and the member carries a merge field with the person's own records link; put that merge tag where your unsubscribe link is. The person can withdraw one purpose, object, or agree again from there.
  • The connection's inbound endpoint receives the audience's unsubscribe and permission-change events. A change is applied only after the member is read back from the audience, and only as a withdrawal: a subscription made in the mailing tool is never recorded as a consent โ€” it is listed on the connection for you to resolve by capturing consent in your form. Changes made through the API are excluded from the subscription, so the two systems never echo each other.
  • The status read answers for a subject reference as well as a person id:
Status by your identifier:
# The per-purpose status for a person, by the reference you supplied (email or your identifier).
curl "https://overturo.com/api/v1/applications/$APPLICATION_ID/consent_status?subject[external_id]=crm-42" \
  -H "Authorization: Bearer $OVERTURO_API_TOKEN"

The flow page's Records tab lists every record with its channel, platform, attester, wording verdict and subject reference. Manage your forms, Typeform connections and settings on the application's Forms tab; the receipt vocabulary is documented under Receipts.

Consent, notice & authorization receipts

Every completed decision issues a receipt aligned with ISO/IEC TS 27560:2023 โ€” a self-declared conformance to the Technical Specification, backed by the platform's receipts conformance suite. Receipts are independently verifiable: the signed export carries a detached signature anyone can check against published keys with no platform access.

One distinction, stated plainly. ISO/IEC TS 27560 is a personal-data-consent standard: conformance language applies to records that carry personal-data processing. A pure authority record follows the standard's structure and is 27560-aligned, never more.

The published schema

Schema version overturo-cr/2.13, citing ISO/IEC TS 27560:2023. The schema is published byte-verbatim at its own identifier: https://overturo.co.nz/schemas/receipt/v2/record.json โ€” the same URL every machine-readable receipt carries as its $id-rooted schema_version reference.

Its governed history, versioning policy, interpretation and verification guide are published by the Overturo Geneva Association, which governs the schema; the operating companies issue records under it, and so may anyone. Additive changes are minor versions; a breaking change is a new major at a new identifier, served beside this one for five years.

Discovery endpoints

EndpointPurpose
/schemas/receipt/v2/record.json The published receipt schema, byte-verbatim at its own identifier.
/.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

The full well-known inventory lives on the API Reference tab.

Record kinds

  • consent_record
  • notice_record
  • authorization_record

Consent records carry the granted purposes and the consent lifecycle. Conformance language applies: the record carries personal-data processing.

Notice records cover notice-not-consent mechanisms and anonymous completions (no principal identifier โ€” the record identifier stands in). Conformance language applies to the notice it records; a notice event is never rendered as consent given.

Authorization records are durable evidence of a delegated-authority commitment. Every commitment freezes exactly one record at activation; lifecycle history is derived at render time and never rewrites the frozen record. A held runtime receipt resolves into the same record through the references both already carry (the dispute kit's join slice โ€” see the verification guide), and every agent-side fact is provenance-labeled: platform-attested registration facts versus operator-declared metadata, never blurred. Aligned only, unless the record carries a personal-data slice (a pii_processing section): pure authority semantics are outside the standard's subject matter.

Absence is meaningful throughout: a field the platform cannot state truthfully is omitted, never guessed โ€” each field's absence semantics are documented in the schema itself.

Every withdrawal and objection event names where it was made from: the person's own records, the link in their email, the link in their receipt, the organisation's mailing tool, a newer record from the organisation's form, the organisation or its systems, the end of the term, or the platform's subject sweep. A change made in the organisation's mailing tool is recorded as the person's withdrawal, never as a consent.

Overturo proves when the decision was recorded, which wording was in force, who attested it and that nothing has changed since; for a record an organisation captured in its own form, Overturo does not vouch that the control was shown unticked; for a record captured through the Overturo field, Overturo rendered the control, showed it unticked and recorded the person's own change, and does not vouch for what the page placed around it.

Sections & fields

The schema's own descriptions are the interpretation text:

record

The record's identity: the schema version it follows, the standard it conforms to, the record identifier, the kind, and whether the record is anonymous.

FieldRequiredMeaning
schema_version yes overturo-cr/2.13
conforms_to yes ISO/IEC TS 27560:2023
record_id yes The unique, stable identifier of this record โ€” the receipt's identity across every channel and export flavor, and the record-specific identifier that stands in for the principal on anonymous records.
record_type no The receipt kind. notice_record covers notice-not-consent mechanisms (informed, documented) and anonymous completions; its event section carries notice events, never a consent-given event. Absent on records issued before this field existed: read as consent_record. authorization_record covers delegated-authority commitments: it requires the authority section, carries pii_processing exactly when declared purposes exist, and its event section uses authority states.
anonymous no Kantara ANCR Stage 1 โ€” emitted (true only) when the subject completed without an established identity. The record-specific identifier standing in for the principal is record_id itself; pii_principal_id is FORBIDDEN below.
pii_principal_id no The principal reference. Required for identified records; forbidden for anonymous ones (the conditional below validates the rule โ€” anonymity is enforced, not conventioned).

pii_processing

The granted purposes with their decision-time facts: language and jurisdiction, the notice as presented, the collection method, each purpose's lawful basis, categories, retention, storage and recipients, and how consent is withdrawn.

FieldRequiredMeaning
language yes The language the decision was presented in (locale code). Frozen labels throughout the record are in this language.
jurisdiction yes The jurisdiction governing this processing, as configured by the organization; "unspecified" when none is declared.
privacy_notice no The VERSIONED notice reference (ISO/IEC TS 27560 clause 6.3.4.2/clause 6.2.2.6): the notice as presented, content-addressed. The hashed content set is exactly {locale, presentation title+description, policy_url, flow-visible purpose rows (name/label/description/mechanism/required, sorted by name)} โ€” canonical deep-sorted JSON, sha256; growing the set is a schema minor, never silent. Three shapes coexist: locally-authored records always carry reference+hash (+url when the organization declared a policy URL); cross-region records carry what the authoring region's offer sent; records upgraded from the earlier flat shape carry url only (nothing is fabricated). Absent only for cross-region offers that predate this field and declared no policy URL.
collection_method no How the decision was collected, as shown to the subject (ISO/IEC TS 27560 clause 6.3.4.9) โ€” the human-readable form of extensions.collection_mechanism.
purposes yes The GRANTED purposes only, with their decision-time labels frozen (ISO/IEC TS 27560 clause 6.3.4.5). Purposes declined or later revoked do not appear here; revocations appear in the event lifecycle and the render-time status.
storage_locations no Where the data is stored (ISO country codes) โ€” derived from the platform's residency rules (where the subject's data lives), never declared by hand.
processing_locations no Present only when recorded lawful-transfer bases exist for the flow: their destination jurisdictions. Absent means no recorded cross-border processing.
withdrawal_method no The stable, credential-free entry point for withdrawing consent (27560 6.3.4.19). Never a minted access token: tokens expire, receipts do not.
recipient_third_parties no Party ids of third-party recipients (each has a corresponding party_identification row โ€” 27560 6.3.4.18). An EMPTY array asserts nothing is shared; an ABSENT key means the platform cannot enumerate recipients completely (e.g. externally-configured webhooks) and stays silent rather than implying completeness.
privacy_rights no Where the subject exercises their rights (27560 6.3.4.20): the stable access entry and, for full accounts, the self-service export. String encoding per the standard; labels live in the rendering layer.
processing_method no How the PII is processed (27560 6.3.4.11): the ENFORCED capability identifiers from the activation-frozen composition snapshot (signed with the flow). Identifiers only, never configuration. Absent for flows without a composition.
impact_assessment no Reference to the organization's completed legitimate-interest assessment (27560 6.3.4.22), emitted only when a granted purpose runs on legitimate interest and exactly one active assessment exists.

party_identification

The controller (declared legal identity when the org completed its controller identity record โ€” provenance 'declared'; display-name fallback otherwise), the platform processor row, and the catalogued supervisory authority. Identity data is DECLARED by the organization, never verified by the platform. party_role values in use: controller, processor, supervisory_authority (string-typed โ€” later members may add roles).

FieldRequiredMeaning
party_id yes The party's stable public identifier โ€” for the controller, the application identifier the transparency record is keyed by; for recipients, the pointer recipient_third_parties references.
party_name yes The party's name: the declared legal name where an identity record exists (provenance "declared"), else the display name.
party_role yes The party's role in this processing: controller, processor, supervisory_authority, recipient - or, on authorization records, principal (a co-signing grantor) and agent (the authorized software agent).
party_type no Whether the party is an organization, a natural person, or a software_agent. Persons are never emitted as recipients.
provenance no One of: display_name, declared
party_address no โ€”
party_email no The party's declared contact email for privacy matters (ISO/IEC TS 27560 clause 6.3.6; the Kantara CR v1.1 required contact).
party_phone no The party's declared contact phone for privacy matters.
party_url no The party's declared contact or informational URL.

authority

Authorization records only. States the enforced grant verbatim: each bound dimension appears exactly when the platform enforces it - absence means not configured, never 'unlimited'. Bound-dimension inner shapes are owned by the platform's authority-bounds vocabulary; this schema constrains presence, not structure.

FieldRequiredMeaning
scope_bounds no Which data scopes the agent may access or share (allowed/denied lists), as enforced.
action_bounds no Which operations the agent may perform (allowed/denied verb lists), as enforced.
value_bounds no Financial ceilings โ€” per transaction and cumulative, with currency โ€” as enforced.
time_bounds no The operating window (active days/hours) and hard expiry, as enforced.
escalation_bounds no Thresholds that force the agent to defer to its principal (approval triggers), as enforced.
reputation_bounds no Minimum counterparty trust requirements the agent must respect, as enforced.
jurisdiction_bounds no The jurisdictions the agent may act toward, as enforced at authorization time.
sequence_bounds no The eighth enforced dimension, carried on its own grant column; present only when configured.
valid_from no The declared start of the authority (ISO 8601).
valid_until no The declared end of the authority (ISO 8601); absent for open-ended grants.
cooling_off no ISO 8601 duration between commitment and activation; present only when an activation delay existed.
withdrawal_method yes Stable revocation entry URL; never embeds a credential - the principal authenticates on arrival.

event

The lifecycle as a projection of the platform's existing evidence (tamper-evident audit-trail records, transaction timestamps) โ€” never a new event store. Consent records freeze the pre-decision prefix (notice shown, consent requested) plus the given event, in canonical lifecycle order; notice records freeze notice vocabulary instead โ€” see event_state. Renders append post-decision transitions (withdrawn โ€” optionally scoped by purpose_id โ€” and expired) derived at read time. Arrays are heterogeneous by construction: flows whose delivery path does not record an opening start at "consent requested"; records issued before the lifecycle section existed start at "consent given"; system-mediated records are notice records carrying the single constructive "notice shown". "consent refused" is defined but unissued in v1 (records exist only for completed decisions; refusal evidence belongs to the notice-receipt track). States "consent invalidated" and "consent halted" remain reserved: no platform transition claims them yet. Actor attribution: the principal for given/withdrawn/acknowledged (anonymous records substitute record_id); the controller (application) for notice shown/requested; the platform processor for expiry. Authorization records: the frozen prefix carries 'authority requested' and 'authority granted'; renders append the lifecycle derived from existing evidence (the grant's chronicle trail, the receipt-consumption aggregate) โ€” 'authority exercised' rows are AGGREGATES (see occurrences), suspension/resumption pairs are dated history, approvals carry the human decision with real attribution, and 'authority expired' is suppressed after a revocation. Records frozen before lifecycle events were recorded start at 'authority granted' (frozen means frozen). Histories without audit-trail records emit only what their columns can date.

FieldRequiredMeaning
event_time yes When the event occurred (ISO 8601). Times come from platform evidence โ€” never synthesized.
validity_duration no ISO 8601 duration (exact, second precision), present only when the consent carries an expiry.
entity_id yes Who acted: the principal reference for given/withdrawn/acknowledged (anonymous records substitute record_id), the application for notice shown/requested, the platform processor for expiry.
event_type yes One of: explicit, implicit, regular
event_state yes Notice records map evidence to notice vocabulary: interactive flows carry notice shown (from the display evidence) + notice acknowledged (completion, entity = principal or, for anonymous records, record_id); system/documented records carry a single constructive notice shown (entity = the application) and no acknowledged row. Notice records never carry consent given.
purpose_id no Present only on a scoped withdrawal: the withdrawn purpose's purpose_id (matches pii_processing.purposes[].purpose_id). Absent on record-level events.
record_reference no Re-affirmation linkage (Annex B re-confirm): on a given event, the record_id of the predecessor record this consent supersedes. The predecessor's render carries the inverse reference in receipt_metadata (render state, not frozen).
occurrences no Aggregate rows only (authority exercised): the authority was exercised this many times, most recently at event_time. Never present on single-occurrence events.
approval_id no Approval-scoped rows: the public reference of the human decision, matching the oversight evidence package's rows. Requested rows may lack it (the reference exists only once the decision row does).
event_channel no Where the change was made from (overturo-cr/2.13): the person's records, the management link in their email, the record link in their receipt, the organisation's mailing tool, a newer record from the organisation's form, the organisation, its systems, the end of the term, or the platform's subject sweep. Present only on appended withdrawal and objection events; absent on the frozen prefix. 'unspecified' names a change whose evidence predates the channel.

extensions

Documented platform extensions (Kantara CR v1.1 clause 4.2; ISO/IEC TS 27560 clause 6.3.2.2): facts the standard has no slot for. Every key is documented here; nothing standard-named carries non-standard semantics.

FieldRequiredMeaning
collection_mechanism no The raw platform consent-mechanism value; pii_processing.collection_method carries the human label.
delivery_mode no How the decision surface was delivered: redirect, embed, popup, or link.
scopes no The effective granted platform permissions at fulfillment โ€” operational grant state, distinct from the purpose grants above.
sensitive no True when the flow collected at least one field the organization classified at a sensitive level.
consent_flow_id no The stable public identifier of the flow (the versioned notice reference is rooted in it).
grant_integrity no Authorization records only: the reference to the grant's dual signature - content hash + key versions. The signatures themselves are the signed-export payload, never inlined here.
delegation_chain_root no Authorization records only: the root commitment's record reference when this authority was sub-delegated. Absent for root grants.
capability_manifest no Authorization records only: the capability-manifest version the grant was made against โ€” the agent's declaration, content-addressed. The hashed content set is exactly {agent_type, name, delegated_scopes (sorted), and the operator-declared disclosure metadata: capabilities, operator_name, privacy_policy_url, data_storage, retention_period} โ€” canonical deep-sorted JSON, sha256; growing the set is a schema minor, never silent. agent_type/name/delegated_scopes are platform-attested registration facts; the metadata keys are operator-DECLARED (the hash anchors what was declared, not that it is true). A model/version fingerprint has no substrate field today and is documented-absent from the set. Manifest drift is AGENT-side (what the agent declares); composition drift is FLOW-side (what a flow executes) โ€” separate mechanisms, never conflated. Absent on records frozen before manifest pinning existed and when no unique agent resolves. Pinned at freeze; supersession never rewrites it (renders overlay manifest_superseded instead).
accountability_record no Structural slot: the agent accountability record reference in effect at activation. Documented-absent until that member ships.
agent_disclosures no Consent and notice records only: the AI agents disclosed to the human this record belongs to โ€” one entry per agent, EU AI Act Art. 50 aligned. A disclosure entry proves NOTICE (the human was told), never grant or authority: the commitment story lives in authorization records, and this array is schema-forbidden there. Present exactly when the flow disclosed agents (platform provenance) or the operator declared an out-of-band disclosure (declared provenance). Carries agent-side references only โ€” never the counterparty's identity (the anonymous kind stays anonymous through these fields).
signature_evidence no Signing-party verification evidence: one entry per completed evidence envelope anchored to the signing party, sorted by completed_at. Pointer + digest only โ€” verdict/claim CONTENT lives in the agreement export and evidence packages, never here. Present on consent records only, and only when the signing party's transaction carries completed evidence; honest-absent otherwise.
collection_channel no Where and by whom the decision was captured. hosted and embedded are Overturo-rendered; embedded_field and embedded_field_browser are Overturo-rendered controls placed inside the organisation's own form (committed by its server, or by the browser at lower assurance) and carry rendering_evidence; form_capture and form_capture_browser were captured in the organisation's own form by its own control (committed by its server, or by the browser at lower assurance); form_platform_webhook was captured in a hosted form builder and delivered by its signed webhook; controller_attested was posted by the organisation's server; imported was loaded from the organisation's historical records with its original capture time. For hosted, embedded, embedded_field and embedded_field_browser the control was rendered by Overturo. For every other channel Overturo attests when the decision was recorded, which wording was in force, who attested it and that nothing has changed since; it does not attest how the control was shown.
collection_platform no The hosted form platform that delivered a form_platform_webhook decision (for example typeform). Absent for every other channel.
wording_drift no Whether the wording the organisation attested its form displayed matched the wording published for the flow at the time of the decision. Recorded, never refused; absent when nothing was attested.
captured_at no When the organisation captured the decision in its own form, platform or records โ€” the decision event's time for a captured record. An import keeps the organisation's original capture time here.
recorded_at no When Overturo recorded the captured decision (the commit). For an import this is later than captured_at by design; the declared duration counts from captured_at.
presentation_attested no Purpose keys whose presentation (that the control was optional) was attested by the organisation rather than read from the form platform's definition. Absent when every presentation fact was read or observed.
rendering_evidence no Present only for a control Overturo rendered inside the organisation's form (collection_channel embedded_field or embedded_field_browser): who rendered it, the notice version shown, when, the host page's origin as asserted by the browser, whether the person changed the control with a trusted gesture, and the control's visibility when the form was submitted, with the kind of visibility measurement the browser offered. Absent for every other channel.

The decision lifecycle

Event states (evidence-projected โ€” nothing is synthesized):

  • notice shown
  • notice acknowledged
  • consent requested
  • consent given
  • consent refused
  • consent withdrawn
  • consent expired
  • objection recorded
  • authority requested
  • authority granted
  • authority exercised
  • authority suspended
  • authority resumed
  • authority revoked
  • authority expired
  • approval requested
  • approval granted
  • approval denied
  • approval countered

The frozen snapshot carries the pre-decision events; renders append post-decision transitions (withdrawal, expiry) derived from the same evidence at read time. The receipt's status is an accessor over this projection.

How a purpose counts as granted

Per purpose, by whichever shape recorded it: its opt-out result (accepted โ€” a legitimate-interest purpose the person did not object to, listed under that lawful basis), else its consent field (true), else โ€” recorded by neither shape โ€” completing the flow is the grant. Revoking a purpose later never rewrites the frozen record; the withdrawal appears in the lifecycle and the render-time status.

Notice versions

privacy_notice is a versioned reference to the notice as presented: the fingerprint covers exactly the locale, the presentation title and description, the policy URL, and the flow-visible purpose rows (name, label, description, mechanism, required). Same content, same version; any drift supersedes automatically. The exact fingerprint construction is specified in the verification guide.

Export flavors & verification

  • canonical โ€” this schema's document (the default).
  • dpv โ€” JSON-LD over the W3C Data Privacy Vocabulary (27560 Annex A.2). Authorization records that carry no personal-data processing refuse this flavor: pure authority semantics have no DPV vocabulary.
  • signed โ€” the frozen record plus a detached signature, verifiable offline.

Request via ?flavor= on the receipt download, GET /api/v1/consent_receipts/:id, or GET /api/v1/authorization_receipts/:id. The verification procedure โ€” canonicalization, signature encoding, key discovery, the runtime-receipt join, and worked examples in two languages โ€” is specified in the verification guide, published with the schema. The JSON-LD ovt: context IRI is a namespace identifier and is not dereferenceable by design.

Prefer to see it rather than read it? Paste a signed record into the record debugger and watch it verify in your browser โ€” no signup, and the record never leaves the page.

Webhook delivery & retries

When an event fires, we POST a JSON body to each subscribed endpoint and read your response to decide what happens next. This page states exactly what our delivery does โ€” it is generated from the same delivery code that runs in production, so it is always current.

What counts as delivered

A delivery is acknowledged when your endpoint returns any 2xx status within 10s to connect and 15s to read the response. Return quickly and do the real work asynchronously โ€” a slow handler reads as a failed delivery.

Retries

A 4xx response is treated as a permanent failure and is never retried โ€” the request was malformed or rejected, and repeating it cannot help. A 5xx, a timeout, or a network error is retried on this schedule:

5 attempts, with waits of:

  • after 30s
  • after 2m
  • after 15m
  • after 1h

After the final attempt the delivery is marked failed and an entry is written to your audit trail. There is no manual retry queue: a failed delivery stays failed, and you can inspect and replay individual deliveries in the webhook debugger during development.

When a subscription is turned off automatically

To protect a broken endpoint from an endless backlog, a subscription is disabled automatically after 5 consecutive failed deliveries. A successful delivery resets the counter. Test deliveries never count toward this โ€” you can safely test against an endpoint that is temporarily down without disabling a live subscription.

Idempotency

Every delivery carries a stable event identifier in the X-Overturo-Event-ID header. Retries of the same event reuse the same identifier, so treat it as an idempotency key: if you have already processed that identifier, acknowledge with a 2xx and do nothing else.

Verifying the signature

Each request is signed with your subscription's signing secret so you can confirm it really came from us. The X-Overturo-Signature header carries the signature and X-Overturo-Timestamp carries the timestamp that was signed; the event type is echoed in X-Overturo-Event-Type so your handler knows which event it is. Verify the signature โ€” over the timestamp and the raw request body โ€” before acting on any payload. The official SDKs include a one-call helper, and the exact procedure is in the webhook signature-verification guide.

Try it

Send a signed sample delivery to any of your saved endpoints โ€” with a live result, timing, and response excerpt โ€” from the test panel on the Webhooks page. A test fires the real, signed delivery pipeline, so what you see is exactly what a real event will do.

Ready to build?

Get an API token