Proof of Resolution
A Proof of Resolution is the artifact Q produces when a user resolves a Q card with a biometric. It is a JSON Web Token signed with ES256 (EC P-256, asymmetric) — a portable, action-scoped, independently verifiable attestation of the user’s verdict. Because it is signed with a private key whose public counterpart is published as a JWKS, an agent or your backend can verify a Proof of Resolution locally, with no round-trip to BotShield.The token carries a verdict of
approve or denied — both are signed. An expired or cancelled card produces no token at all. See Resolution Flow.Claims
The token’s header carriesalg: ES256 and kid: v2. The payload claims:
Verify locally with the JWKS
BotShield publishes the Resolution public key as a JWK Set at:kid in the JWKS matches the JWT header, so verification is the standard JWKS flow. This is the recommended path — no network call to BotShield per verification.
Node
verdict === "approve" before acting — a denied token is a valid, signed refusal, not an authorization.
Verify via the SDK (coming)
A round-trip verifier —sdk/verify-resolution, for callers who prefer not to pin a JWKS client — is planned but not yet available. It will perform the same ES256 verification server-side (rejecting bad signatures, wrong issuer, expired tokens, or an invalid verdict) and return the decoded claims.
Until it ships, local JWKS verification (above) is the supported path — and it is sufficient: it enforces signature, issuer, audience, expiry, and algorithm entirely client-side with no network round-trip.
Key rotation
Thekid header tags the key version (today v2). When BotShield rotates keys, the new key is appended to the JWKS under a new kid; the old key is retained until all tokens signed with it expire (24-hour TTL). A correct JWKS verifier selects the key by kid automatically — no action required on rotation. Pin algorithms: ["ES256"] to reject any token that claims a different algorithm.
Webhook events
In addition to (or instead of) polling, Q delivers events to your endpoint via Svix. Each delivery is signed with your endpoint’swhsec_ secret and carries the svix-id / svix-timestamp / svix-signature headers — verify exactly as described in Webhooks.
q.card.proposed
q.resolution.confirmed / q.resolution.denied
outcome is confirmed for q.resolution.confirmed and denied for q.resolution.denied. The proof_token field carries the Proof of Resolution JWT — verify it as above before acting. When several cards were confirmed in one ceremony, each fires its own message, all sharing one ceremony_id in metadata.
q.resolution.expired
proof_token — there is no signed verdict on an expiry. Treat it as “stand down.”
Authenticity comes from the Svix envelope signature, verified with your
whsec_ endpoint secret — not from a token inside the webhook. For confirmed/denied events you then independently verify the proof_token JWT. Dedupe on the Svix message id for idempotency; BotShield may retry.Next steps
Q API Reference
Every
agentlink/* operation with request and response shapes.Webhooks
The shared Svix signature-verification pattern, with per-language snippets.