Card snapshot
witness.getvda.ai
·
2026-08-18 20:35:07 UTC
·
582120a763369a148eb3f7d61ab54df92dcb837e924a3126f84b872855dff28a
This is a frozen copy of the agent's agent-card.json as we observed it at the timestamp above. We capture a new snapshot every time the card's content hash changes. Useful for: forensic drift analysis, verifying downstream callers see the right version, reproducing routing decisions made historically.
{
"$comment": "VDA Witness \u2014 the evidence layer of Verified Digital Agents (VDA), a sibling of C2MD under the VDA umbrella. Ed25519-signed, self-contained A2A discovery card.",
"name": "VDA Witness",
"description": "Seals governed AI-agent decisions into tamper-evident, Ed25519-signed, independently-verifiable records, and produces EU AI Act Article 12 evidence from the sealed trail. Part of the VDA platform.",
"url": "https://witness.getvda.ai",
"documentationUrl": "https://witness.getvda.ai/docs",
"openapiUrl": "https://witness.getvda.ai/openapi.json",
"version": "1.0.0",
"protocolVersion": "0.2.5",
"preferredTransport": "HTTP+JSON",
"provider": {
"organization": "Verified Digital Agents (VDA)",
"url": "https://getvda.ai"
},
"boundaries": {
"fundamental": "Witness seals ASSERTED facts and makes them tamper-evident, independently verifiable, and (on the Anchored tier) externally anchored. It does NOT authenticate the person or agent named in a record, nor verify the runtime it ran on \u2014 those identities/contexts are asserted by the sealing credential, and Witness records the claim. A sealed record proves 'at time T, this account asserted X', not 'X is true of the real world.'",
"holder_binding": "A credential id / seal ref is a BEARER handle. check_valid tells you a credential is valid and names a subject; it does NOT prove the party presenting it IS that subject. Binding the presenter to the subject (e.g. a DID challenge) is the ENFORCER's responsibility.",
"custody": "signer.custody is 'custodial' (Witness's platform DID key signs \u2014 integrity/anchoring, but issuer-authenticity NOT established) or 'customer-managed' (the issuer's own key signs \u2014 provable even against Witness). check_valid's issuer_verified reflects this per record.",
"grouping": "chainKey IS the grouping primitive \u2014 there is deliberately no bundle_id/group_id/parent_id. Every seal takes an optional top-level chainKey; records sharing one are hash-linked into a set retrievable and provable AS A SET (GET /chains/{chainKey}/proof, list_records?chainKey=, report{chainKey}). Give one workflow/bundle/case a stable shared key ('c2md:bundle:<id>'). Do NOT seal a manifest record listing sibling record IDs under `evidence` \u2014 an evidence hash is the CONTENT hash of external material, and a record id is not its content, so such a manifest carries a hash no auditor can reproduce. Chains are independent and cheap: each (account, chainKey) anchors on its own cursor, so a finished group anchors once and goes quiet."
},
"capabilities": {
"streaming": false,
"independentVerification": true
},
"defaultInputModes": [
"application/json"
],
"defaultOutputModes": [
"application/json"
],
"securitySchemes": {
"witnessApiKey": {
"type": "http",
"scheme": "bearer",
"description": "VDA Witness API key as a Bearer token (`Authorization: Bearer <key>`; the `x-witness-key` header also works). Self-serve a free SEALED-tier key IN-BAND \u2014 see `provisioning.selfServeKey` (or the MCP `get_test_key` tool). Keys are short-lived; RENEW a fresh one for your OWN account forever with no human via controller-key challenge-response \u2014 see `provisioning.renewKey`. `verify` needs no key."
}
},
"security": [
{
"witnessApiKey": []
}
],
"provisioning": {
"selfServeKey": {
"description": "Self-issue a SEALED-tier API key in-band, no human. `verify` is keyless (verify a record first); get a key to `seal`.",
"issuanceUrl": "https://witness.getvda.ai/api/witness/test-key",
"method": "POST",
"requestBody": {
"email": "optional string (follow-up only)"
},
"returns": {
"apiKey": "wtn.<id>.<secret>",
"accountId": "acct_<ULID>",
"sealed": true,
"anchored": false,
"sealedState": "not_anchored",
"tier": "test (DEPRECATED alias \u2014 read sealedState)",
"compliance": false,
"expiresInDays": 7
},
"tierLabel": "Sealed",
"sealed": true,
"anchored": false,
"sealedState": "not_anchored",
"sealedStateReason": "anchoring_not_enabled_for_account",
"tier": "test",
"compliance": false,
"label": "SEALED tier \u2014 every record is Ed25519-signed + hash-chained (tamper-evident) and independently verifiable OFFLINE, but NOT externally anchored (not committed to Rekor/TSAs). This is TERMINAL for this tier \u2014 records stay sealed, they do not become anchored \u2014 so \"provable even against VDA\" does NOT apply here.",
"upgradeToAnchored": "The Anchored tier externally commits your trail (Rekor + at least one TSA, quorum met) \u2014 the only tier where \"provable even against us\" is true. It is CONCIERGE-provisioned (contact), NOT self-serve.",
"alsoViaMcp": "MCP tool `get_test_key` at https://witness.getvda.ai/api/witness/mcp",
"durableAccount": "THE provisioning model \u2014 do this, don't rely on a standing key: generate an Ed25519 keypair and bind its PUBLIC JWK as `controllerPublicKeyJwk`. Your account is then durable and you mint your OWN short-lived keys by renewing against your controller (see `provisioning.renewKey`) \u2014 no human, no permanent secret. Concierge/service provisioning goes further: supply a controller and NO bootstrap key is issued at all \u2014 your FIRST key comes from a renewal, so there is never a standing credential to leak. A returned key without a controller is the exception (a quick-start, short-lived), and a non-expiring key is an explicit, audited exception \u2014 not the norm. (Durable is about the ACCOUNT surviving; it does not anchor the trail \u2014 that is the Anchored tier.)",
"docs": "https://witness.getvda.ai/docs"
},
"renewKey": {
"description": "Renew a fresh short-TTL API key for an account you already control \u2014 no human, no standing secret. Challenge-response with the Ed25519 controller key you bound at creation (provisioning.selfServeKey.controllerPublicKeyJwk).",
"step1_challenge": {
"url": "https://witness.getvda.ai/api/witness/renew/challenge",
"method": "POST",
"requestBody": {
"accountId": "acct_<ULID>"
},
"returns": {
"nonce": "string",
"expiresInSec": 300,
"sign_payload": "the EXACT string to Ed25519-sign (substituted)",
"renew": "{ method:'POST', endpoint:'/api/witness/renew' } \u2014 structured routing"
}
},
"step2_renew": {
"url": "https://witness.getvda.ai/api/witness/renew",
"method": "POST",
"sign": "Ed25519-sign the exact UTF-8 string `vda.witness.renew/1|<accountId>|<nonce>` with your controller PRIVATE key",
"requestBody": {
"accountId": "acct_<ULID>",
"nonce": "from step 1",
"signature": "base64url Ed25519 signature"
},
"returns": {
"apiKey": "wtn.<id>.<secret>",
"accountId": "acct_<ULID>",
"keyExpiresAt": "ISO-8601",
"keyTtlSec": 86400
}
},
"properties": "Same account \u21d2 same chains \u21d2 seals continue at the next seq, prev-hash unbroken. Prior keys stay valid until they expire (overlap = zero-downtime rotation). Rate-limited per account; NOT subject to the anonymous-mint caps (you proved ownership).",
"alsoViaMcp": "MCP tools `renew_challenge` then `renew_key` at https://witness.getvda.ai/api/witness/mcp",
"docs": "https://witness.getvda.ai/docs"
}
},
"terms": {
"description": "Commercial terms, discoverable before you commit. Sealed is free + self-serve; Anchored (externally committed \u2014 provable even against VDA) is contact-gated today; billing is NOT yet live (anchoring is enabled manually).",
"billingLive": false,
"tiers": [
{
"tier": "Sealed",
"price": "free",
"sealsPerMonth": 5000,
"access": "self-serve, instant, no human",
"anchored": false,
"get": "Ed25519-signed, hash-chained, tamper-evident, independently verifiable OFFLINE \u2014 but NOT externally anchored (terminal; 'provable even against VDA' does NOT apply)."
},
{
"tier": "Anchored",
"price": "from EUR 50 / month",
"sealsPerMonth": 5000,
"access": "contact us to enable (concierge today \u2014 anchoring is enabled manually)",
"anchored": true,
"get": "Everything in Sealed + externally anchored (Rekor + DigiCert + Sectigo, quorum) \u2014 provable even against VDA."
},
{
"tier": "Enterprise",
"price": "talk to us",
"sealsPerMonth": "volume-negotiated",
"access": "contact us",
"get": "Volume, SLA, higher-assurance tier (customer-held signing key)."
}
],
"usage": {
"includedSealsPerMonth": 5000,
"enforcement": "measured_not_billed",
"note": "Seal usage is counted + surfaced on every seal response (usage block); over the allowance it WARNS but never blocks \u2014 an evidence trail is never silently dropped."
},
"sealedIsNotAnchored": "A Sealed record is signed + independently verifiable but NOT externally anchored; only Anchored records back the 'provable even against us' claim.",
"upgradePreservesEverything": "Upgrading Sealed -> Anchored keeps the SAME account id and the SAME chains; anchoringBeganSeq records where anchoring began; earlier records stay honestly sealed-not-anchored.",
"contact": "https://witness.getvda.ai/#get-key",
"termsUrl": "https://witness.getvda.ai/docs#pricing"
},
"suiteServices": {
"contract": "CONTRACT A \u2014 suite auth via whoami. Bearer credentials issued by Witness (Authorization: Bearer wtn.<id>.<secret>) can be presented to sanctioned sibling getvda.ai services; a sibling validates by calling GET /api/witness/whoami, which returns {account_id, tier, scopes, compliance, revoked, expires_at}. This is the sanctioned cross-service auth path. There is NO privileged service-to-service credential \u2014 the sibling acts on behalf of whichever key its caller presented, and can only resolve THAT key.",
"description": "A sibling getvda.ai service that accepts `Authorization: Bearer wtn.<id>.<secret>` from its own caller validates that key by calling whoami \u2014 Witness is the single source of truth for its own keys; no sibling replicates the key store.",
"whoami": {
"url": "https://witness.getvda.ai/api/witness/whoami",
"method": "GET",
"mcpTool": "whoami",
"auth": "Authorization: Bearer wtn.<id>.<secret> (the end-user key being validated)",
"returns": {
"account_id": "acct_<ULID>",
"tier": "SEALED | ANCHORED (from ACCOUNT state, not the key)",
"scopes": [
"seal",
"read"
],
"compliance": "boolean (true iff ANCHORED)",
"key_id": "<keyId>",
"revoked": false,
"expires_at": "ISO-8601 or null (non-expiring) \u2014 the key validity window"
},
"caching": "200 -> Cache-Control: private, max-age=60 (revocations may lag by up to ~1 min); 401 -> no-store",
"errors": "401 (generic 'unknown or invalid API key' \u2014 no existence leak) on any missing/malformed/unknown/wrong-secret/revoked key; 429 when rate-limited (per IP and per account)",
"scope": "No elevated scope; a key may only ask about ITSELF and cannot enumerate other accounts.",
"docs": "https://witness.getvda.ai/docs#whoami"
}
},
"customerManaged": {
"description": "Seal shaped records signed by YOUR OWN key (custody customer-managed) so issuer-authenticity is provable even against Witness \u2014 while keeping shape enforcement. Prepare (Witness assembles, returns bytes) \u2192 sign locally (raw Ed25519) \u2192 submit. Works for issue_admission_credential, revoke_admission_credential, seal_agent_action, seal_attestation, seal_hitl_decision.",
"prepare": {
"url": "https://witness.getvda.ai/api/witness/prepare",
"method": "POST",
"auth": "your account API key",
"requestBody": {
"skill": "issue_admission_credential | revoke_admission_credential | seal_agent_action | seal_attestation | seal_hitl_decision",
"params": "the shaped params",
"signingPublicKeyJwk": "your Ed25519 record-signing PUBLIC JWK {kty:'OKP',crv:'Ed25519',x}",
"chainKey": "optional"
},
"returns": {
"record": "the UNSIGNED body",
"canonicalBytes": "the exact UTF-8 bytes to sign",
"seq": "int",
"prevHash": "string|null",
"submit": "STRUCTURED routing { method:'POST', endpoint:'/api/witness/\u2026' } \u2014 POST your signed record HERE (do NOT parse the prose in submitInstructions as a path)",
"submitInstructions": "human-readable prose (NOT a path)"
},
"note": "STATELESS \u2014 stores nothing."
},
"sign": "Ed25519-sign canonicalBytes with your record-signing PRIVATE key (raw Ed25519, signature base64url). This key is DISTINCT from your account controller key (renewal) and from any did:web card-signing key.",
"submitInstructions": "Attach proof:{algorithm:'Ed25519', signature, created:record.issuedAt} to `record`; POST { record } to prepare's `submit.endpoint` (structured routing \u2014 never guess). Witness verifies the signature, enforces the shape, checks chain position (409 if the chain advanced \u2014 re-prepare), and stores. Custody = customer-managed.",
"submitEndpoints": {
"seal_hitl_decision": "/api/witness/seal/hitl-decision",
"seal_agent_action": "/api/witness/seal/agent-action",
"seal_attestation": "/api/witness/seal/attestation",
"issue_admission_credential": "/api/witness/credentials/issue",
"revoke_admission_credential": "/api/witness/credentials/{credential_id}/revoke"
},
"issuerAuthenticity": "Publish your record-signing PUBLIC key at your issuer did:web (e.g. did:web:onboard.getvda.ai) so check_valid returns issuer_verified: 'verified'. Without it, check_valid reports 'did_unresolvable' (customer-managed) or 'custodial' (Witness-signed).",
"convention": "Once you go customer-managed for real credentials, seal customer-managed for ALL seals on that chain (intake, gates, credential, revocation) \u2014 uniform issuer-authenticity across the whole admission trail, so an auditor need not check per-record. Records still hash-chain + anchor, so you keep BOTH issuer-authenticity AND tamper-evidence.",
"docs": "https://witness.getvda.ai/docs#customer-managed"
},
"mcp": {
"endpoint": "https://witness.getvda.ai/api/witness/mcp",
"tools": [
"get_test_key",
"renew_challenge",
"renew_key",
"seal",
"seal_hitl_decision",
"seal_agent_action",
"seal_attestation",
"issue_admission_credential",
"check_valid",
"revoke_admission_credential",
"verify_record_issuer",
"whoami",
"list_records",
"get_record",
"verify",
"report"
]
},
"skills": [
{
"id": "seal_hitl_decision",
"name": "Seal a human-in-the-loop decision",
"description": "Seals a governance record for a HUMAN decision. Content-based and auditor-reconstructable: it captures the deciding human's identity and role, the disposition and rationale, the policies/SOPs cited AS THE BASIS (by reference, and where possible captured text or hash \u2014 policies drift, so an auditor needs the version in force), and content-addressable references (with sha256 hashes) to the system-of-record artifacts the decider SAW at decision time \u2014 reservation records, folios, inventory state \u2014 NOT the action produced. From the sealed record alone an auditor can verify exactly what state the decider was looking at. Witness seals the ASSERTED actor identity; it does not authenticate the person. Use evidence[].inline to embed a snapshot (\u2264100KB; bytes are verified against the hash at seal time) when upstream availability isn't guaranteed. If a decision genuinely has no evidentiary basis, state it in evidence_omitted_reason rather than omitting silently. See the MCP `seal_hitl_decision` inputSchema for required fields.",
"endpoint": "POST https://witness.getvda.ai/api/witness/seal/hitl-decision",
"tags": [
"evidence",
"hitl",
"governance",
"audit"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [
{
"witnessApiKey": []
}
],
"examples": [
{
"request": {
"actor": {
"id": "jane.ops@stay",
"type": "human",
"role": "duty-manager"
},
"decision": {
"disposition": "approved",
"statement": "Waived the late-cancel fee; guest showed a flight-cancellation notice."
},
"governing_clauses": [
{
"ref": "SOP.cancellations#3.2",
"text": "Duty managers MAY waive late-cancel fees on documented travel disruption.",
"hash": "sha256:\u2026"
}
],
"evidence": [
{
"ref": "pms://reservation/RES-88421",
"hash": "sha256:\u2026",
"media_type": "application/json",
"captured_at": "2026-07-15T09:41:00Z"
},
{
"ref": "upload://flight-cancel-notice.pdf",
"hash": "sha256:\u2026",
"inline": {
"encoding": "base64",
"bytes": "\u2026"
}
}
],
"basis_captured_at": "2026-07-15T09:41:00Z"
},
"response": {
"record": {
"schema": "vda.witness.record/1",
"account": "acct_<ULID>",
"seq": 0,
"decision": {
"inputs": {
"record_type": "hitl_decision",
"\u2026": "signed evidentiary basis"
}
},
"\u2026": "proof"
},
"bodyHash": "sha256:\u2026",
"stored": true
}
}
]
},
{
"id": "seal_agent_action",
"name": "Seal an autonomous agent action",
"description": "Seals a record for an action an agent took AUTONOMOUSLY under a governing rule. Records what the agent consumed as two fields split by provenance: `evidence` is EXTERNAL material it saw (content-addressed + hashed \u2014 e.g. a whoami response from another service), `parameters` is the COMPUTED arguments it was passed (self-contained, no hash \u2014 e.g. requested scopes, jurisdictions). One-question test: exists outside this record? \u2192 evidence (hash it); computed/passed as an argument? \u2192 parameters. At least one is required (or evidence_omitted_reason). Optionally include agent_context \u2014 free-form execution-substrate hints (cloud_run_revision, model_name, region) so an auditor can ask 'was this at a known-buggy revision?'; asserted by you, not verified by Witness. For a human decision use seal_hitl_decision.",
"endpoint": "POST https://witness.getvda.ai/api/witness/seal/agent-action",
"tags": [
"evidence",
"agent",
"governance",
"audit"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [
{
"witnessApiKey": []
}
],
"examples": [
{
"request": {
"actor": {
"id": "c2md-classifier",
"type": "agent"
},
"action": {
"statement": "Classified the agent as high-risk under EU AI Act Annex III.",
"outcome": "high_risk"
},
"governing_rule": {
"ref": "eu-ai-act#annex-III",
"text": "\u2026"
},
"evidence": [
{
"ref": "witness://record/rec_\u2026",
"hash": "sha256:\u2026",
"description": "whoami resolution for the requesting credential"
}
],
"parameters": {
"jurisdictions": [
"EU"
],
"data_categories": [
"biometric"
],
"agent_description": "\u2026"
},
"agent_context": {
"cloud_run_revision": "c2md-api-00042-abc",
"model_name": "gemini-2.5-flash",
"region": "europe-west1"
}
},
"response": {
"record": {
"decision": {
"inputs": {
"record_type": "agent_action",
"\u2026": "evidence + parameters + agent_context"
}
},
"\u2026": "proof"
},
"bodyHash": "sha256:\u2026",
"stored": true
}
}
]
},
{
"id": "seal_attestation",
"name": "Seal an attestation",
"description": "Seals an assertion that a fact or state held AS OF a point in time \u2014 a model passed an evaluation, a card was issued, a key was rotated, a config was live. Not for decisions (use the decision skills). Captures the asserting party, the claim, and the as-of time; supporting external references (evidence) are optional but strengthen it. Cite the framework via governing_basis, or it is sealed as attested-by-the-signing-party, not independently verified by Witness.",
"endpoint": "POST https://witness.getvda.ai/api/witness/seal/attestation",
"tags": [
"attestation",
"ed25519",
"audit"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [
{
"witnessApiKey": []
}
],
"examples": [
{
"request": {
"actor": {
"id": "c2md-issuer",
"type": "system"
},
"claim": "Issued agent card did:web:example#key-3 to tenant acct_\u2026",
"as_of": "2026-07-15T10:00:00Z"
},
"response": {
"record": {
"decision": {
"inputs": {
"record_type": "attestation",
"\u2026": "claim + as_of"
}
},
"verdict": "ATTESTED",
"\u2026": "proof"
},
"bodyHash": "sha256:\u2026",
"stored": true
}
}
]
},
{
"id": "issue_admission_credential",
"name": "Issue an admission credential",
"description": "Issue an agent admission credential \u2014 a sealed attestation admitting an agent (subject_did) to a customer environment with a scope, valid until expiry. The Witness record IS the credential (no separate document): enforcers hold only the credential id and check it via check_valid (Contract B); it is revocable via revoke_admission_credential. Sealed by the issuer's Witness account \u2014 only that account can revoke. subject_did / environment_id / scope / compliance_mappings are ASSERTED, not authenticated by Witness (the admission workflow establishes them; Witness records the claim). Records are immutable \u2014 validity is computed at verify time, never mutated. HOLDER-BINDING: the credential id is a bearer handle \u2014 issuing it does not bind the holder; the ENFORCER must separately prove the presenter controls subject_did (see check_valid). PROVISIONAL is representable honestly: sandbox_result.pass may be false and score null ('not run'), and evidence_seal_ref may point at an honest stub \u2014 a credential can truthfully record a conditional / not-yet-passed admission rather than forcing a passing claim.",
"endpoint": "POST https://witness.getvda.ai/api/witness/credentials/issue",
"tags": [
"credential",
"admission",
"attestation",
"ed25519"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [
{
"witnessApiKey": []
}
],
"examples": [
{
"request": {
"subject_did": "did:web:agent.example.com",
"issuer_did": "did:web:onboard.getvda.ai",
"environment_id": "env_citizenm_prod",
"scope": [
"reservation.read",
"folio.settle"
],
"governance_files_hash": "sha256:\u2026",
"sandbox_result": {
"pass": true,
"score": 0.97,
"evidence_seal_ref": "rec_\u2026"
},
"impact_delta_ref": "rec_\u2026",
"expires_at": "2027-07-16T00:00:00Z",
"compliance_mappings": [
{
"framework": "eu_ai_act",
"article_ref": "Article 13",
"claim": "\u2026"
}
]
},
"response": {
"credential_id": "rec_\u2026",
"record": {
"decision": {
"inputs": {
"record_type": "attestation",
"attestation_type": "admission_credential",
"\u2026": "credential fields"
}
},
"\u2026": "proof"
},
"stored": true
}
}
]
},
{
"id": "check_valid",
"name": "Check a credential's validity",
"description": "CONTRACT B \u2014 the public credential-verification endpoint. Is this admission credential currently valid? PUBLIC, no key: input is a credential_id; anyone holding one can verify it, which is what makes issuer-issued credentials verifiable-by-anyone (their whole value). Witness returns the single canonical verdict \u2014 issued \u2227 signature verifies \u2227 not revoked by the issuer \u2227 not expired \u2014 so every enforcer is identically correct and the meaning of 'valid' evolves in ONE place, never drifting across independent implementations (the reason this is a skill and not a two-call recipe). Response {valid, code, subject, issuer, environment, scope, expires_at, revoked, revoked_at?, reason_code?, issuer_verified, issuer_verification}; code is 'valid' | 'revoked' | 'expired' | 'not_found' | 'not_credential'. PUBLIC \u2014 no key (a credential id is not a secret; CRL/OCSP posture). Cache-Control: private, max-age=60. issuer_verified is a DISTINCT signal from the lifecycle verdict \u2014 issuer-authenticity: 'verified' (customer-managed record signed by a key published in issuer_did's DID document \u2014 provable even against Witness), 'key_not_in_did_doc' (LOUD: claims an issuer but signed by a key not in its DID \u2014 suspicious), 'custodial' (Witness-signed; issuer-authenticity not established, only integrity/anchoring), or 'did_unresolvable' (the check did not complete; not verified AND not forged). Set your own bar: a high-stakes environment may require issuer_verified:true; a low-stakes one may accept custodial. TWO enforcer disciplines the schema teaches: (1) a credential id is a BEARER handle \u2014 a valid credential is necessary, NOT sufficient; you MUST separately challenge the presenter to prove control of `subject` (a DID challenge), because validity is not proof the presenter is the subject. (2) Record each check_valid response in YOUR OWN audit log (timestamp, credential_id, response) \u2014 Witness's cache and logs tell you what was returned, but only your local record proves what YOU acted on and when, when a regulator later asks how you knew the credential was valid at time T.",
"endpoint": "GET https://witness.getvda.ai/api/witness/credentials/{credential_id}",
"tags": [
"credential",
"verification",
"revocation",
"enforcement"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [],
"examples": [
{
"request": {
"credential_id": "rec_\u2026"
},
"response": {
"valid": true,
"code": "valid",
"subject": "did:web:agent.example.com",
"issuer": "did:web:onboard.getvda.ai",
"environment": "env_citizenm_prod",
"scope": [
"reservation.read"
],
"expires_at": "2027-07-16T00:00:00Z",
"revoked": false
}
},
{
"request": {
"credential_id": "rec_revoked"
},
"response": {
"valid": false,
"code": "revoked",
"subject": "did:web:agent.example.com",
"environment": "env_citizenm_prod",
"revoked": true,
"revoked_at": "\u2026",
"reason_code": "compromised"
}
},
{
"request": {
"credential_id": "typo"
},
"response": {
"valid": false,
"code": "not_found",
"revoked": false
}
}
]
},
{
"id": "verify_record_issuer",
"name": "Verify a record's issuer-authenticity (verdict only)",
"description": "Is a record's signature by the issuer it claims? PUBLIC, no key. Input is a record_id; the answer is a VERDICT plus the issuer DID and the public key it resolved against \u2014 NEVER the record body, decision.inputs, or evidence. General records (attestations, agent_actions) are account-private; this is the ONLY thing about them that is publicly checkable, and the surface is exactly as wide as the question. This is how an enforcer of a privilege-widening event \u2014 e.g. a service sealing a genesis authority registration or a baseline promotion ABOUT ITSELF, customer-managed \u2014 confirms the issuer signed it, not merely that Witness recorded it, without holding the account's key or seeing the record. Response {record_id, signature_valid, issuer_verified, issuer_verification, issuer_did, signer_key}. Same four states as check_valid's issuer_verified: 'verified' (signing key IS published in the claimed issuer's did:web) | 'key_not_in_did_doc' (LOUD \u2014 signed by a key NOT in that DID; suspicious) | 'custodial' (Witness's own key signed it \u2014 issuer-authenticity is not the applicable question, a different custody model, not a failure) | 'did_unresolvable' (issuer DID unreachable \u2014 the check did NOT complete; treat as neither verified nor forged). The issuer DID is taken from the credential's issuer_did or, for a general record, from the signer's did:web keyId. Cache-Control: public, max-age=60 (no-store while did_unresolvable). ids are unguessable UUIDs; unknown \u2192 not_found.",
"endpoint": "GET https://witness.getvda.ai/api/witness/records/{record_id}/issuer",
"tags": [
"verification",
"issuer-authenticity",
"enforcement",
"custody"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [],
"examples": [
{
"request": {
"record_id": "rec_genesis"
},
"response": {
"record_id": "rec_genesis",
"signature_valid": true,
"issuer_verified": true,
"issuer_verification": "verified",
"issuer_did": "did:web:hitl.getvda.ai",
"signer_key": {
"kty": "OKP",
"crv": "Ed25519",
"x": "\u2026"
}
}
},
{
"request": {
"record_id": "rec_custodial"
},
"response": {
"record_id": "rec_custodial",
"signature_valid": true,
"issuer_verified": null,
"issuer_verification": "custodial",
"issuer_did": null,
"signer_key": null
}
}
]
},
{
"id": "revoke_admission_credential",
"name": "Revoke an admission credential",
"description": "Revoke an admission credential your account issued. Only the issuing account may revoke (enforced structurally by comparing sealing accounts, not a DID string). Terminal \u2014 re-admission is a NEW credential. Produces a new immutable revocation attestation that supersedes the credential; verify_admission_credential reflects it within the ~60s cache window. Customers who want a credential revoked call the issuing service (onboard.getvda.ai), which owns revocation policy and executes here \u2014 customers do not call Witness directly.",
"endpoint": "POST https://witness.getvda.ai/api/witness/credentials/{credential_id}/revoke",
"tags": [
"credential",
"revocation",
"attestation"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [
{
"witnessApiKey": []
}
],
"examples": [
{
"request": {
"credential_id": "rec_\u2026",
"reason_code": "compromised",
"revoked_by": "did:web:onboard.getvda.ai",
"reason_text": "Subject key rotation detected out-of-band."
},
"response": {
"revoked_credential_id": "rec_\u2026",
"revocation_id": "rec_\u2026",
"record": {
"decision": {
"inputs": {
"attestation_type": "admission_revocation",
"supersedes": "rec_\u2026"
}
}
}
}
}
]
},
{
"id": "revoke_api_key",
"name": "Revoke an account API key (sealed as an event)",
"description": "Revoke one of your account's API keys \u2014 the key stops authenticating at once, and the revocation is itself sealed as a key_revocation attestation on your chain. TWO authorities: (1) CONTROLLER-AUTHORIZED, owner self-service, no operator \u2014 prepare a key_revocation via /prepare {skill:'revoke_api_key', params:{key_id, revoked_by:{type:'controller'}, reason_code}}, sign the canonical bytes with your BOUND CONTROLLER key, and POST { record } here. Witness verifies the signer IS your bound controller and that the key is yours; the record is customer-managed (owner-signed) so the revocation is provable AGAINST Witness, not merely asserted by it. (2) ADMIN break-glass (operator, x-witness-admin, { key_id }) \u2014 sealed custodially, revoked_by.type='operator'; the record honestly shows Witness asserted it. revoked_by.type is the trust distinction, not a code path. Revocation is TOTAL: a revoked key 401s at auth AND its account's records become unfetchable with it \u2014 there is NO operator backdoor to read a revoked account's records. Reflected in whoami: a revoked key 401s immediately (no-store); a sibling's \u226460s cached 200 is the only propagation lag.",
"endpoint": "POST https://witness.getvda.ai/api/witness/keys/revoke",
"tags": [
"revocation",
"key-management",
"custody",
"security"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [
{
"witnessApiKey": []
}
],
"examples": [
{
"request": {
"record": {
"decision": {
"inputs": {
"attestation_type": "key_revocation",
"revoked_key_id": "<keyId>",
"revoked_by": {
"type": "controller"
},
"reason_code": "exposed"
}
},
"signer": {
"custody": "customer-managed"
},
"proof": {
"algorithm": "Ed25519",
"signature": "\u2026"
}
}
},
"response": {
"revoked": true,
"key_id": "<keyId>",
"revoked_by": {
"type": "controller"
}
}
}
]
},
{
"id": "whoami",
"name": "Resolve a Witness key \u2014 cross-service auth (Contract A)",
"description": "The SANCTIONED cross-service authorization endpoint. A sibling getvda.ai service that accepts `Authorization: Bearer wtn.<id>.<secret>` from ITS caller validates that key by calling whoami \u2014 Witness is the sole source of truth for its own keys, so no sibling replicates the key store. Returns everything a sibling needs to authorize on IDENTITY, not just presence: account_id (WHO), tier (SEALED|ANCHORED), scopes (WHAT it may do), compliance, key_id, revoked (always false on 200), and expires_at (validity window; null = non-expiring \u2014 a revoked or expired key 401s before reaching here). A key can only ever resolve ITSELF \u2014 it cannot enumerate other accounts. Any missing/invalid key returns a uniform 401 'unknown or invalid API key' (no existence leak). Available as REST (GET) and as the MCP `whoami` tool; permissively rate-limited per IP and per account.",
"endpoint": "GET https://witness.getvda.ai/api/witness/whoami",
"tags": [
"auth",
"cross-service",
"composition",
"suite"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [
{
"witnessApiKey": []
}
],
"examples": [
{
"request": {
"headers": {
"authorization": "Bearer wtn.<id>.<secret>"
}
},
"response": {
"account_id": "acct_<ULID>",
"tier": "ANCHORED",
"scopes": [
"seal",
"read"
],
"compliance": true,
"key_id": "<id>",
"revoked": false,
"expires_at": null
}
}
]
},
{
"id": "seal",
"name": "Seal (general-purpose / compatibility)",
"description": "General-purpose seal, retained for backward compatibility and for records that fit none of the shaped skills. PREFER a shaped skill so your record is auditor-reconstructable: seal_hitl_decision (a human decided), seal_agent_action (an agent acted), seal_attestation (you assert a fact/state). Records sealed here carry no record_type and report as unstructured. API key required; no key? see `provisioning.selfServeKey` or the MCP `get_test_key` tool.",
"endpoint": "POST https://witness.getvda.ai/api/witness/seal",
"tags": [
"evidence",
"ed25519",
"audit",
"compatibility"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [
{
"witnessApiKey": []
}
],
"examples": [
{
"request": {
"decision": {
"agent": "refund-bot",
"inputs": {
"amountEur": 150
},
"verdict": "PASS",
"reasoning": "<= 200 and account in good standing"
},
"governingRule": {
"ruleId": "refund.auto",
"ruleText": "Agents MAY auto-approve refunds up to EUR200."
},
"chainKey": "default",
"decisionId": "optional-idempotency-key"
},
"response": {
"record": {
"schema": "vda.witness.record/1",
"account": "acct_<ULID>",
"seq": 0,
"\u2026": "signed body + proof"
},
"bodyHash": "sha256:\u2026",
"stored": true,
"chainKey": "default"
}
}
]
},
{
"id": "read",
"name": "Read your own records",
"description": "Read back what you've sealed (operational query, account-scoped by your key \u2014 never an accountId param). `list_records` returns paginated summaries + the set of chainKeys in your account (discover your own chains); `get_record` returns the FULL signed body of one record. For OFFLINE verification of chain continuity with ZERO calls back to Witness, fetch a self-contained proof bundle: `GET /records/{recordId}?proof=chain` (record + full chain + anchor attestation + did.json) or `GET /chains/{chainKey}/proof` (whole trail). Feed it to `offlineVerify({record, chain, anchor, didDocument})` \u2014 verdicts: ANCHORED_VALID / SIGNED_PENDING / BROKEN / INSUFFICIENT_PROOF (the last means proof material was missing, NOT tampering). Distinct from `report` (Article-12 artefact). Foreign/unknown id \u2192 not-found (no leak).",
"endpoint": "GET https://witness.getvda.ai/api/witness/records \u00b7 GET https://witness.getvda.ai/api/witness/records/{recordId}?proof=chain \u00b7 GET https://witness.getvda.ai/api/witness/chains/{chainKey}/proof",
"tags": [
"read",
"query",
"audit-trail"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [
{
"witnessApiKey": []
}
],
"examples": [
{
"request": {
"list_records": {
"chainKey": "default",
"limit": 50
}
},
"response": {
"account": "acct_<ULID>",
"chainKeys": [
"default",
"\u2026"
],
"records": [
{
"recordId": "\u2026",
"chainKey": "default",
"seq": 0,
"verdict": "PASS",
"agent": "refund-bot",
"ruleId": "refund.auto",
"bodyHash": "sha256:\u2026",
"sealedAt": "\u2026",
"anchorState": "SIGNED_PENDING"
}
],
"nextCursor": null
}
},
{
"request": {
"get_record": {
"recordId": "\u2026"
}
},
"response": {
"record": {
"schema": "vda.witness.record/1",
"decision": {
"agent": "refund-bot",
"verdict": "PASS",
"reasoning": "\u2026full reasoning\u2026",
"inputs": {},
"actionProposed": "\u2026"
},
"governingRule": {
"ruleId": "refund.auto",
"ruleText": "\u2026"
},
"seq": 0,
"prevHash": null,
"proof": {
"signature": "\u2026"
}
},
"chainKey": "default",
"anchorState": "ANCHORED_VALID",
"anchor": {
"head": "sha256:\u2026",
"seq": 0
}
}
}
]
},
{
"id": "provision",
"name": "Self-provision an account (no human)",
"description": "Self-issue a SEALED-tier API key in-band with no human. Pass an Ed25519 controller PUBLIC key (`controllerPublicKeyJwk`) to claim a DURABLE, self-renewable account bound to a key you hold \u2014 otherwise the account auto-expires ~7 days. Sealed tier = signed + hash-chained + independently verifiable OFFLINE, but NOT externally anchored (terminal \u2014 it stays sealed). The Anchored tier (externally committed, \"provable even against us\") is concierge-provisioned separately. Then `renew` re-keys the same account forever. See also `provisioning.selfServeKey`.",
"endpoint": "POST https://witness.getvda.ai/api/witness/test-key",
"tags": [
"provisioning",
"self-serve",
"ed25519"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [],
"examples": [
{
"request": {
"controllerPublicKeyJwk": {
"kty": "OKP",
"crv": "Ed25519",
"x": "<base64url>"
}
},
"response": {
"apiKey": "wtn.<id>.<secret>",
"accountId": "acct_<ULID>",
"tier": "test",
"compliance": false,
"durable": true,
"controllerBound": true,
"keyExpiresAt": "2026-\u2026Z",
"keyTtlSec": 86400
}
}
]
},
{
"id": "renew",
"name": "Renew a key (agent-provable, no human)",
"description": "Get a fresh short-TTL API key for an account you ALREADY control \u2014 indefinitely, with no human and no permanent secret. Two steps: POST /renew/challenge {accountId} \u2192 nonce; Ed25519-sign `vda.witness.renew/1|<accountId>|<nonce>` with your controller key; POST /renew {accountId, nonce, signature} \u2192 fresh key bound to the SAME account (chains continue, prev-hash unbroken). Bind the controller key at creation via `provisioning.selfServeKey.controllerPublicKeyJwk`.",
"endpoint": "POST https://witness.getvda.ai/api/witness/renew",
"tags": [
"credential",
"renewal",
"ed25519",
"self-serve"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [],
"examples": [
{
"request": {
"accountId": "acct_<ULID>",
"nonce": "<from /renew/challenge>",
"signature": "<base64url Ed25519 over 'vda.witness.renew/1|<accountId>|<nonce>'>"
},
"response": {
"apiKey": "wtn.<id>.<secret>",
"accountId": "acct_<ULID>",
"keyExpiresAt": "2026-07-13T00:00:00Z",
"keyTtlSec": 86400
}
}
]
},
{
"id": "verify",
"name": "Verify a record",
"description": "Independently verify a record or chain (Ed25519 signature + hash-chain). No auth.",
"endpoint": "POST https://witness.getvda.ai/api/witness/verify",
"tags": [
"verification",
"tamper-evidence"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [],
"examples": [
{
"request": {
"record": {
"schema": "vda.witness.record/1",
"\u2026": "a full witness record incl. proof"
}
},
"response": {
"ok": true,
"bodyHash": "sha256:\u2026",
"signatureValid": true,
"errors": []
}
}
]
},
{
"id": "report",
"name": "Article 12 Evidence Report",
"description": "Generate an EU AI Act Article 12 evidence report from the sealed trail (API key required).",
"endpoint": "POST https://witness.getvda.ai/api/witness/report",
"tags": [
"eu-ai-act",
"art-12",
"evidence"
],
"inputModes": [
"application/json"
],
"outputModes": [
"application/json"
],
"security": [
{
"witnessApiKey": []
}
],
"examples": [
{
"request": {},
"response": {
"reportType": "vda.witness.art12-evidence/1",
"lifecycle": "DEMO_DATA",
"generatedAt": "\u2026",
"entries": [
"\u2026 one entry per sealed decision, each traceable to a signed record"
]
}
}
]
}
],
"proof": {
"type": "Ed25519",
"algorithm": "Ed25519",
"created": "2026-08-18T20:35:06.937Z",
"verificationMethod": "did:web:witness.getvda.ai#key-1",
"did": "did:web:witness.getvda.ai",
"signature": "gptG7en9vVZyJcH_dzgBv_05nKfcZ0pUdO5nPk49fso293WGon6zehFmfhAA9-gVYiXeXlTmLTvu8TyxAmhRAw",
"publicKeyJwk": {
"crv": "Ed25519",
"x": "n7CC3oT05X6d13f-0hOTab8jeLx4YYwJG4iPcCJiP7c",
"kty": "OKP"
},
"verificationNote": "Resolve did:web:witness.getvda.ai (/.well-known/did.json), take key-1's publicKeyJwk, and verify Ed25519 over canonicalize(card without `proof`). The embedded publicKeyJwk must equal key-1."
}
}