VMP API v1
Global rules, auth, scopes, endpoint reference, and interactive testing.
Global rules
Standards that apply to every endpoint unless explicitly stated.
Content-Type: application/jsonRFC3339 / ISO8601 UTC (e.g. 2026-01-09T18:12:00Z)event_id, batch_id, receipt_id, key_id: UUID (string)
tenant_id: string (1–128, no whitespace)
scope: string (1–128, must contain ':')
scope_prefix: optional string (1–128, no whitespace, must end with ':')Authentication
Tenant keys for most endpoints, provision token for platform operations, token-only for WebSocket.
Used by all endpoints except /healthz and the WebSocket stream.
Authorization: Bearer <tenant_api_key>401 {"error":"missing Authorization: Bearer <api_key>"}
401 {"error":"invalid api key"}
401 {"error":"api key revoked"}
401 {"error":"api key expired"}
401 {"error":"api key stale", "details": {...}}
403 {"error":"forbidden","details":{"missing_scope":"..."}}
403 {"error":"tenant mismatch"}
403 {"error":"forbidden","details":{"reason":"scope outside key prefix", ...}}
429 {"error":"rate_limited","details":{...}}Authorization: Bearer <VMP_CONSOLE_PROVISION_TOKEN>401 {"error":"invalid provision token"}POST /v1/admin/api-keys
GET /v1/admin/api-keys
POST /v1/admin/api-keys/revoke
POST /v1/admin/pubkeys
PATCH /v1/admin/pubkeys/{pubkey_id}GET /v1/stream/events?token=<JWT>{
"tenant_id": "string",
"scope": "admin.read",
"exp": 123,
"iat": 123
}Any valid tenant key can call this endpoint.
GET /v1/whoamiScopes
Authoritative scope list and matching rules.
admin
admin.read
events.write
events.delete
proofs.read
receipts.read
ws.mint- "admin" satisfies all scopes
- otherwise exact match
- if a key has scope_prefix, any request scope identifier must begin with that prefixPublic endpoints
No authentication required.
Returns a plain text ok response.
okIngest
Append event to the chain for a scope.
Authorization: Bearer <tenant_api_key>Required scope: events.write. Optional idempotency header: x-idempotency-key.
{
"tenant_id": "vmp-tenant",
"scope": "user:123",
"op": "write",
"fields": {},
"meta": {}
}{
"event_id": "uuid",
"event_hash_hex": "32-byte-hex",
"prev_hash_hex": "32-byte-hex|null",
"created_at": "2026-01-09T18:12:00Z"
}Proofs
Merkle proof for an event once batched.
Required scope: proofs.read. If not batched, fields are null.
{
"event_id": "uuid",
"tenant_id": "vmp-tenant",
"scope": "user:123",
"batch_id": "uuid|null",
"leaf_index": 7|null,
"leaf_hex": "32-byte-hex|null",
"root_hex": "32-byte-hex|null",
"path": [
{ "sibling_hex": "32-byte-hex", "sibling_is_left": true }
],
"sig_base64": "base64|null",
"pubkey_id": "dev-key-1|null",
"pubkey_base64": "base64|null",
"pubkey_hex": "hex|null",
"batch_created_at": "2026-01-09T18:12:00Z|null"
}Deletion and receipts
Logical deletion plus verifiable receipts.
Required scope: events.delete. Appends a new delete event.
{
"tenant_id": "vmp-tenant",
"scope": "user:123",
"target_event_id": "uuid",
"meta": {}
}Required scope: receipts.read.
Required scope: receipts.read.
b"vmp-delete:" || delete_event_id_bytes || delete_event_hash_bytesBundles
Self-contained payloads for offline verification.
Required scope: proofs.read.
Required scopes: receipts.read and proofs.read.
Verifier endpoints
Server endpoints used by offline verifiers.
Currently public. Validates recompute, Merkle path, and signature.
Currently public. Validates receipt signature and the delete-event bundle.
Admin surface
Tenant-authenticated admin endpoints.
/v1/admin/events
/v1/admin/events/{event_id}
/v1/admin/ws-token
/v1/admin/batches
/v1/admin/stats
/v1/admin/deletion-receipts
/v1/admin/worker/health
/v1/admin/api-keys/{key_id}/audit-events
/v1/admin/batches/{batch_id}/leaves
/v1/admin/batches/{batch_id}/tree
/v1/admin/dashboardWebSocket stream
Token-authenticated stream of events.
GET /v1/stream/events?token=<jwt>{ "kind": "hello", "data": { "tenant_id": "vmp-tenant", "scope": null } }
{ "kind": "heartbeat", "data": { "ts": "..." } }Provisioning
Console to VMP platform operations (provision token).
POST /v1/admin/api-keys
GET /v1/admin/api-keys
POST /v1/admin/api-keys/revokePubkeys
Pubkey registry and verifier support.
GET /v1/pubkeys/{pubkey_id}
POST /v1/admin/pubkeys
PATCH /v1/admin/pubkeys/{pubkey_id}active: can sign and verify
retired: verify only
revoked: neither sign nor verifyError format
Consistent JSON error objects for clients.
{ "error": "message" }{ "error": "forbidden", "details": { "missing_scope": "..." } }Endpoints index
Tabular view for API consumers and OpenAPI mapping.
OpenAPI-friendly view of method, path, auth, and required scope.
| Method | Path | Auth | Scope |
|---|---|---|---|
| GET | /healthz | none | none |
| POST | /v1/ingest | tenant key | events.write |
| GET | /v1/proof/{event_id} | tenant key | proofs.read |
| POST | /v1/delete | tenant key | events.delete |
| GET | /v1/deletion-receipts/{receipt_id} | tenant key | receipts.read |
| GET | /v1/verify-deletion/{receipt_id} | tenant key | receipts.read |
| GET | /v1/events/{event_id}/bundle | tenant key | proofs.read |
| GET | /v1/deletion-receipts/{receipt_id}/bundle | tenant key | receipts.read + proofs.read |
| POST | /v1/verify/bundle | none (public) | none |
| POST | /v1/verify/deletion-bundle | none (public) | none |
| GET | /v1/admin/events | tenant key | admin.read |
| GET | /v1/admin/events/{event_id} | tenant key | admin.read |
| GET | /v1/admin/batches | tenant key | admin.read |
| GET | /v1/admin/batches/{batch_id}/leaves | tenant key | admin.read |
| GET | /v1/admin/batches/{batch_id}/tree | tenant key | admin.read |
| GET | /v1/admin/stats | tenant key | admin.read |
| GET | /v1/admin/deletion-receipts | tenant key | admin.read |
| GET | /v1/admin/worker/health | tenant key | admin.read |
| GET | /v1/admin/dashboard | tenant key | admin.read |
| POST | /v1/admin/ws-token | tenant key | ws.mint |
| GET | /v1/stream/events?token=<jwt> | ws token | admin.read (in token) |
| GET | /v1/whoami | tenant key | any valid key |
| POST | /v1/admin/api-keys | provision token | n/a |
| GET | /v1/admin/api-keys | provision token | n/a |
| POST | /v1/admin/api-keys/revoke | provision token | n/a |
| GET | /v1/pubkeys/{pubkey_id} | tenant key | proofs.read |
| POST | /v1/admin/pubkeys | provision token | n/a |
| PATCH | /v1/admin/pubkeys/{pubkey_id} | provision token | n/a |
Try it
Interactive calls from your browser.
Runs in your browser. Paste a tenant API key and call /v1/whoami or /v1/ingest.
This key stays in your browser. Do not paste production secrets on untrusted devices.
No response yet.Protocol notes
Details worth making explicit in public docs.
fields -> fields_canon
meta -> meta_canon
commit_fields with tenant salt
c_fields stored as bytes (hex exposed)event_hash = link(prev_hash, tenant_id, scope, op, c_fields, meta_canon)worker batches events -> batch_id + leaf_index
proofs exist only after batchingschema_version must be 1..=BUNDLE_SCHEMA_VERSIONper-key RPM (enforce_key_rpm)
per-IP burst for provisioning endpoints (enforce_ip_burst)