API Endpoints
The drs-verify HTTP server exposes these endpoints.
POST /verify
Verify a DRS bundle. This is the primary endpoint.
Request:
POST /verify
Content-Type: application/json
{
"bundle_version": "4.0",
"invocation": "<invocation-receipt-jwt>",
"receipts": ["<root-dr-jwt>", "<sub-dr-jwt>"]
}
Body is capped at MAX_BODY_BYTES (default 1 MiB).
Response — valid chain (200):
{
"valid": true,
"context": {
"root_principal": "did:key:z6MkHuman...",
"chain_depth": 2,
"leaf_policy": {
"max_cost_usd": 0.10,
"allowed_tools": ["web_search"]
}
}
}
Response — invalid chain (200):
/verifyalways returns HTTP 200. Check thevalidfield to determine the outcome. HTTP 403 is only returned by the MCP/A2A middleware routes, not by/verifydirectly.
{
"valid": false,
"error": {
"code": "CHAIN_HASH_MISMATCH",
"message": "prev_dr_hash mismatch at chain index 1",
"suggestion": "Ensure receipts are in root-first order and were not modified after signing"
}
}
Response — malformed input (400):
{"error": "invalid character 'x' looking for beginning of value"}
POST /mcp/* (middleware)
Implementation status: The MCP and A2A middleware routes are registered but the reverse proxy mode (
DRS_UPSTREAM) and bundle-required enforcement (DRS_REQUIRE_BUNDLE) are not implemented in the current release. The endpoint currently accepts requests and returns 200 after verifying theX-DRS-Bundleheader. The proxy and enforcement features are roadmap items.
When DRS_UPSTREAM is configured, drs-verify acts as a reverse proxy. All requests to /mcp/* are intercepted, the X-DRS-Bundle header is verified, and the request is proxied upstream if valid.
Request: Any MCP request with X-DRS-Bundle header:
POST /mcp/tools/call
X-DRS-Bundle: <base64url bundle>
Content-Type: application/json
On valid bundle: Proxied to $DRS_UPSTREAM/mcp/tools/call.
On invalid bundle (403):
{
"error": "SIGNATURE_INVALID",
"block": "C",
"message": "Ed25519 signature verification failed for issuer did:key:z6Mk..."
}
On missing bundle (403, when DRS_REQUIRE_BUNDLE=true):
{
"error": "BUNDLE_MISSING",
"message": "X-DRS-Bundle header is required"
}
GET /healthz
Liveness check.
GET /healthz
Response (200):
{"status": "ok"}
Returns 503 only if the server cannot handle requests (e.g., during shutdown).
GET /readyz
Readiness check. Returns 200 when the server is fully initialised and ready to handle verification requests; 503 when not ready.
GET /readyz
Response (200 — ready):
{"status": "ready"}
Response (503 — not ready):
{"status": "not_ready", "reason": "status_list_not_fetched"}
Use /readyz for Kubernetes readiness probes. Use /healthz for liveness probes.
Note: If
STATUS_LIST_BASE_URLis not configured, the status list cache is skipped and/readyzalways returns 200 immediately.
POST /admin/revoke
Mark a delegation receipt as locally revoked by its status list index. Takes effect immediately — does not wait for the remote Bitstring Status List to refresh.
DRS_ADMIN_TOKEN must be set as an environment variable. If not set, the endpoint responds 503.
POST /admin/revoke
Authorization: Bearer <DRS_ADMIN_TOKEN>
Content-Type: application/json
{"status_list_index": 42}
Body is capped at 1 KiB.
Response (200):
{"revoked": true, "status_list_index": 42}
Response — admin not configured (503):
{"error": "admin endpoint not configured — set DRS_ADMIN_TOKEN"}
Response — wrong or missing token (401):
{"error": "unauthorized"}
The local revocation store is in-memory only. Revocations do not survive process restart. For durable revocation, update the W3C Bitstring Status List at your
STATUS_LIST_BASE_URLendpoint.