{"$comment":"VDA Witness — 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 — 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 — integrity/anchoring, but issuer-authenticity NOT established) or 'customer-managed' (the issuer's own key signs — provable even against Witness). check_valid's issuer_verified reflects this per record."},"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 — 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 — 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 — 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 — 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 — records stay sealed, they do not become anchored — so \"provable even against VDA\" does NOT apply here.","upgradeToAnchored":"The Anchored tier externally commits your trail (Rekor + at least one TSA, quorum met) — 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 — 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`) — no human, no permanent secret. Concierge/service provisioning goes further: supply a controller and NO bootstrap key is issued at all — 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 — not the norm. (Durable is about the ACCOUNT surviving; it does not anchor the trail — 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 — 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' } — 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 ⇒ same chains ⇒ 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 — 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 — 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 — anchoring is enabled manually)","anchored":true,"get":"Everything in Sealed + externally anchored (Rekor + DigiCert + Sectigo, quorum) — 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 — 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 — 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 — 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 — 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) — 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' — 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 — while keeping shape enforcement. Prepare (Witness assembles, returns bytes) → sign locally (raw Ed25519) → 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/…' } — POST your signed record HERE (do NOT parse the prose in submitInstructions as a path)","submitInstructions":"human-readable prose (NOT a path)"},"note":"STATELESS — 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 — never guess). Witness verifies the signature, enforces the shape, checks chain position (409 if the chain advanced — 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) — 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 — 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 — reservation records, folios, inventory state — 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 (≤100KB; 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:…"}],"evidence":[{"ref":"pms://reservation/RES-88421","hash":"sha256:…","media_type":"application/json","captured_at":"2026-07-15T09:41:00Z"},{"ref":"upload://flight-cancel-notice.pdf","hash":"sha256:…","inline":{"encoding":"base64","bytes":"…"}}],"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","…":"signed evidentiary basis"}},"…":"proof"},"bodyHash":"sha256:…","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 — e.g. a whoami response from another service), `parameters` is the COMPUTED arguments it was passed (self-contained, no hash — e.g. requested scopes, jurisdictions). One-question test: exists outside this record? → evidence (hash it); computed/passed as an argument? → parameters. At least one is required (or evidence_omitted_reason). Optionally include agent_context — 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":"…"},"evidence":[{"ref":"witness://record/rec_…","hash":"sha256:…","description":"whoami resolution for the requesting credential"}],"parameters":{"jurisdictions":["EU"],"data_categories":["biometric"],"agent_description":"…"},"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","…":"evidence + parameters + agent_context"}},"…":"proof"},"bodyHash":"sha256:…","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 — 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_…","as_of":"2026-07-15T10:00:00Z"},"response":{"record":{"decision":{"inputs":{"record_type":"attestation","…":"claim + as_of"}},"verdict":"ATTESTED","…":"proof"},"bodyHash":"sha256:…","stored":true}}]},{"id":"issue_admission_credential","name":"Issue an admission credential","description":"Issue an agent admission credential — 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 — 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 — validity is computed at verify time, never mutated. HOLDER-BINDING: the credential id is a bearer handle — 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 — 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:…","sandbox_result":{"pass":true,"score":0.97,"evidence_seal_ref":"rec_…"},"impact_delta_ref":"rec_…","expires_at":"2027-07-16T00:00:00Z","compliance_mappings":[{"framework":"eu_ai_act","article_ref":"Article 13","claim":"…"}]},"response":{"credential_id":"rec_…","record":{"decision":{"inputs":{"record_type":"attestation","attestation_type":"admission_credential","…":"credential fields"}},"…":"proof"},"stored":true}}]},{"id":"check_valid","name":"Check a credential's validity","description":"CONTRACT B — 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 — issued ∧ signature verifies ∧ not revoked by the issuer ∧ not expired — 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 — 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 — issuer-authenticity: 'verified' (customer-managed record signed by a key published in issuer_did's DID document — provable even against Witness), 'key_not_in_did_doc' (LOUD: claims an issuer but signed by a key not in its DID — 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 — 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) — 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_…"},"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":"…","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 — 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 — e.g. a service sealing a genesis authority registration or a baseline promotion ABOUT ITSELF, customer-managed — 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 — signed by a key NOT in that DID; suspicious) | 'custodial' (Witness's own key signed it — issuer-authenticity is not the applicable question, a different custody model, not a failure) | 'did_unresolvable' (issuer DID unreachable — 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 → 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":"…"}}},{"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 — 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 — 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_…","reason_code":"compromised","revoked_by":"did:web:onboard.getvda.ai","reason_text":"Subject key rotation detected out-of-band."},"response":{"revoked_credential_id":"rec_…","revocation_id":"rec_…","record":{"decision":{"inputs":{"attestation_type":"admission_revocation","supersedes":"rec_…"}}}}}]},{"id":"revoke_api_key","name":"Revoke an account API key (sealed as an event)","description":"Revoke one of your account's API keys — 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 — 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 }) — 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 — 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 ≤60s 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":"…"}}},"response":{"revoked":true,"key_id":"<keyId>","revoked_by":{"type":"controller"}}}]},{"id":"whoami","name":"Resolve a Witness key — 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 — 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 — a revoked or expired key 401s before reaching here). A key can only ever resolve ITSELF — 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,"…":"signed body + proof"},"bodyHash":"sha256:…","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 — 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})` — 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 → not-found (no leak).","endpoint":"GET https://witness.getvda.ai/api/witness/records  ·  GET https://witness.getvda.ai/api/witness/records/{recordId}?proof=chain  ·  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","…"],"records":[{"recordId":"…","chainKey":"default","seq":0,"verdict":"PASS","agent":"refund-bot","ruleId":"refund.auto","bodyHash":"sha256:…","sealedAt":"…","anchorState":"SIGNED_PENDING"}],"nextCursor":null}},{"request":{"get_record":{"recordId":"…"}},"response":{"record":{"schema":"vda.witness.record/1","decision":{"agent":"refund-bot","verdict":"PASS","reasoning":"…full reasoning…","inputs":{},"actionProposed":"…"},"governingRule":{"ruleId":"refund.auto","ruleText":"…"},"seq":0,"prevHash":null,"proof":{"signature":"…"}},"chainKey":"default","anchorState":"ANCHORED_VALID","anchor":{"head":"sha256:…","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 — otherwise the account auto-expires ~7 days. Sealed tier = signed + hash-chained + independently verifiable OFFLINE, but NOT externally anchored (terminal — 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-…Z","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 — indefinitely, with no human and no permanent secret. Two steps: POST /renew/challenge {accountId} → nonce; Ed25519-sign `vda.witness.renew/1|<accountId>|<nonce>` with your controller key; POST /renew {accountId, nonce, signature} → 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","…":"a full witness record incl. proof"}},"response":{"ok":true,"bodyHash":"sha256:…","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":"…","entries":["… one entry per sealed decision, each traceable to a signed record"]}}]}],"proof":{"type":"Ed25519","algorithm":"Ed25519","created":"2026-07-26T20:16:28.964Z","verificationMethod":"did:web:witness.getvda.ai#key-1","did":"did:web:witness.getvda.ai","signature":"N6A_Pt0ENQGmmTzW8h__8p3XVVxHgGwz3-6EQohwvdmO8tm71I0Mgfwe6cZYhco_x8UnNrWyLHxdTLpQT2hABw","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."}}