Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
openapi: 3.2.0
info:
title: VITNA API (Phase 1) Consent API
version: 1.1.0
description: 'The compliance evidence layer for AI agents. Pre-flight compliance checks agents call before they act (consent, breach reportability, EU AI Act classification, cross-border), and audit-grade, signed records of what they did. Phase 1 surface covers region routing labels (one database region is in use today), DPIA/ROPA generation, Data Subject Rights (scoped: answerable only for events linked to a subject via principal_ref at ingest, from the point linkage begins; earlier events are permanently unattributable and cannot be linked retroactively), consent lifecycle, Sahamati AA integration, breach notification, and INR/Razorpay billing for DPDP-IN owners (switched off pending merchant onboarding). Also includes one-command agent self-provisioning (/api/setup, mints a restricted trial key with no signup), a destructive-action safety pre-flight (/api/preflight/action-check), scoped engagement sessions with signed evidence bundles (/api/engagement/*), and account claim by a person, from the claim link (/claim).'
contact:
name: COSTRINITY
email: dpo@costrinity.xyz
url: https://www.costrinity.xyz/
license:
name: Proprietary
url: https://vitna.costrinity.xyz/terms
servers:
- url: https://vitna.costrinity.xyz
description: Production
- url: https://vigil-staging.vercel.app
description: Staging (preview branch)
tags:
- name: consent
paths:
/api/consent/request:
post:
summary: Record a new consent artifact
description: 'Signed-in owner only (since 2026-11-01). With an API key (x-vitna-key, x-vigil-key, x-api-key or Authorization: Bearer, a restricted trial key included, even beside a session cookie) it is refused 403 session_required before the body is read, and nothing is written: a consent a key recorded would pass its own consent check and any other agent''s, with the record crediting the operator. Signed artifacts from an account aggregator arrive at /api/sahamati/consent-handle.'
tags:
- consent
security:
- Session: []
responses:
'200':
description: OK
'401':
description: 'Unauthorized: no signed-in session, or owner_id names another account'
'403':
description: 'session_required: the request carried an API key'
operationId: postApiConsentRequest
x-operation-id-source: derived
/api/consent/revoke:
post:
summary: Revoke a specific consent artifact
tags:
- consent
security:
- Session: []
responses:
'200':
description: OK
operationId: postApiConsentRevoke
x-operation-id-source: derived
/api/consent/active:
get:
summary: List active (unrevoked, unexpired) consents
description: 'Signed-in owner only (since 2026-11-01). With an API key (x-vitna-key, x-vigil-key, x-api-key or Authorization: Bearer, a restricted trial key included, even beside a session cookie) it is refused 403 session_required before anything is read, because these records are not tied to a session. An agent asks whether one processing is allowed with /api/consent/check.'
tags:
- consent
security:
- Session: []
responses:
'200':
description: OK
'401':
description: 'Unauthorized: no signed-in session, or owner_id names another account'
'403':
description: 'session_required: the request carried an API key'
operationId: getApiConsentActive
x-operation-id-source: derived
/api/consent/check:
get:
summary: Pre-flight enforcement, is processing allowed?
tags:
- consent
security:
- Session: []
parameters:
- name: principal_id
in: query
schema:
type: string
format: uuid
- name: principal_ref
in: query
schema:
type: string
- name: purpose
in: query
required: true
schema:
type: string
- name: category
in: query
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ConsentCheckResult'
operationId: getApiConsentCheck
x-operation-id-source: derived
post:
summary: Pre-flight enforcement (POST variant)
tags:
- consent
security:
- Session: []
responses:
'200':
description: OK
operationId: postApiConsentCheck
x-operation-id-source: derived
/api/consent/renew:
post:
summary: Extend an active consent's expiry
description: 'Signed-in owner only (since 2026-11-01). With an API key (x-vitna-key, x-vigil-key, x-api-key or Authorization: Bearer, a restricted trial key included, even beside a session cookie) it is refused 403 session_required before the body is read, and nothing is written: an extended consent would pass its own consent check and any other agent''s.'
tags:
- consent
security:
- Session: []
responses:
'200':
description: OK
'401':
description: 'Unauthorized: no signed-in session, or owner_id names another account'
'403':
description: 'session_required: the request carried an API key'
operationId: postApiConsentRenew
x-operation-id-source: derived
/api/consent/bulk-revoke:
post:
summary: Revoke ALL active consents for a principal
tags:
- consent
security:
- Session: []
responses:
'200':
description: OK
operationId: postApiConsentBulkRevoke
x-operation-id-source: derived
/api/consent/receipt:
get:
summary: Downloadable Kantara-shaped consent receipt
description: 'Signed-in owner only (since 2026-11-01). With an API key (x-vitna-key, x-vigil-key, x-api-key or Authorization: Bearer, a restricted trial key included, even beside a session cookie) it is refused 403 session_required before anything is read, because these records are not tied to a session.'
tags:
- consent
security:
- Session: []
parameters:
- name: consent_id
in: query
required: true
schema:
type: string
format: uuid
- name: format
in: query
schema:
type: string
enum:
- md
- json
responses:
'200':
description: OK
'401':
description: 'Unauthorized: no signed-in session, or owner_id names another account'
'403':
description: 'session_required: the request carried an API key'
operationId: getApiConsentReceipt
x-operation-id-source: derived
/api/sahamati/consent-handle:
post:
summary: Sahamati Account Aggregator consent webhook
tags:
- consent
parameters:
- name: x-sahamati-signature
in: header
schema:
type: string
description: JWS header.payload.signature (RS256). Verified against SAHAMATI_PUBKEY_PEM when set.
responses:
'200':
description: OK
'401':
description: Signature invalid
operationId: postApiSahamatiConsentHandle
x-operation-id-source: derived
components:
schemas:
ConsentCheckResult:
type: object
required:
- allowed
- reason
- matching_consent_id
- principal_id
properties:
allowed:
type: boolean
reason:
type:
- string
- 'null'
enum:
- null
- no_principal
- no_active_consent
- purpose_mismatch
- category_overflow
matching_consent_id:
type:
- string
- 'null'
format: uuid
principal_id:
type:
- string
- 'null'
format: uuid
securitySchemes:
ApiKey:
type: apiKey
in: header
name: x-vigil-key
description: 'Per-agent API key. Format: vitna_<64-char-hex>; legacy vigil_ keys remain valid. Send on /api/ingest.'
Bearer:
type: http
scheme: bearer
bearerFormat: vitna_<key> (legacy vigil_<key> accepted)
description: 'Same key as x-vigil-key, sent as `Authorization: Bearer <key>`.'
Session:
type: apiKey
in: cookie
name: vigil_session
description: 'Operator dashboard session cookie. Set by a sign-in: the second step of a password sign-in (/api/auth/verify-pin), or, for an address whose mail Google handles (Gmail or a Google Workspace domain) while VITNA''s mail cannot reach Gmail, the password alone (/api/owners); GitHub; a passkey; or a claim.'
CronSecret:
type: http
scheme: bearer
description: Bearer ${CRON_SECRET}. Only the Vercel cron infrastructure should hold this.