Endpoint reference

/api/verify

Read or write a verification row by transaction id. POST creates or re-mints a bearer through the x402 settlement by default; GET returns the row's current status. A verified POST may return an issued bearer, but responses do not echo submitted proof or provider evidence fields.

Methods

GET reads the current status for a transactionId (no DB writes). POST mints a verification under the x402 settlement — on a fresh transactionId the row is created and marked verified; on an existing verified row the bearer is re-minted to the same 'sub'.

Auth

Public. No API key required. POST uses the x402 settlement proof unless the verifier proof prototype is explicitly enabled and a proof envelope is supplied. GET returns 400 / 404 with the same canonical envelope for missing input or unknown transactionId.

Verifier-side authorization prototype

The optional POST body field is proof: { scheme: "zkp-prototype-v1", transactionId, nonce, expiresAt, proof }. VERIFY_ZK_PROOF_PROTOTYPE_ENABLED defaults to false. While false, proof input is ignored and the existing X-PAYMENT x402 gate, including its HMAC checks, remains required. When explicitly enabled with a proof envelope, the prototype checks strict envelope shape, transactionId equality, a URL-safe nonce of 8–128 characters, an integer expiry after the current Unix time, and an exact deterministic fixture match against zkp-prototype-fixture-v1. The serialized proof envelope is limited to 4,096 UTF-8 bytes; a larger envelope returns 413 { error: "proof_envelope_too_large", maxBytes: 4096 } before verification. When the feature flag is enabled, every proof request must use a fresh nonce that has not already been accepted. Once accepted, a nonce cannot be reused: a later enabled proof request with that nonce is rejected with HTTP 409 and { error: "zkp_prototype_nonce_reused" }, including for another transactionId. An accepted response includes authorization: { mode: "zkp-prototype", status: "accepted" }; a invalid prototype envelopes return 400. This fixture adapter is not cryptographic zero-knowledge verification and provides no production proof soundness.

Cache posture

Public, edge-cacheable header set is NOT enforced on /api/verify — the route stamps no-store headers so a verified row does not leak through a downstream CDN between a re-mint from the partner side and a fresh fetch from another actor.

Request

POST
/api/verify

Body: { transactionId: string (1-200), platform?: string [a-z0-9_-]{1,80}, proof?: { scheme: "zkp-prototype-v1", transactionId, nonce, expiresAt, proof } }. Headers: X-PAYMENT (required unless an enabled prototype proof is accepted — base64url-encoded JSON with { transactionId, sender, payTo, amount, signature } — see /docs/x402). The optional proof field is ignored while VERIFY_ZK_PROOF_PROTOTYPE_ENABLED=false, so the x402/HMAC gate remains required. When enabled, only the JSON-serialized proof value is measured against the 4,096-byte limit; other request fields and headers are not included.

Body / parameters

{
  "transactionId": "tx_3f8a1b22c4d5e6f7",
  "platform": "openlaw"
}

Response

200 (verified), 400 (including rejected prototype proof), 402, 404, 409, 413 (oversized enabled proof), 500

{ status: "verified"|"pending"|"rejected", verificationId: string, transactionId: string, token?: string (signed bearer; verified branch only), sessionUrl?: string, authorization?: { mode: "zkp-prototype", status: "accepted" } (only when the prototype accepts a supplied proof) }. 402 envelope: { resource: "/api/verify", accepts: [{ asset, network, amount, payTo, facilitator? }, ...] }. 409 envelope (replay): { ..., error: "payment_id_already_settled" }. 413 oversized-proof response: { error: "proof_envelope_too_large", maxBytes: 4096 }. Reusing a nonce from an accepted enabled proof returns 409 { error: "zkp_prototype_nonce_reused" }.

Try it

Copy-paste curl

Paste the snippet into a terminal or the embedded sandbox panel below. The sandbox runs against the same /api/docs/<endpoint> handler so the wire shape on this page matches the response the evaluator returns.

curl

curl -X POST https://provably-2.polsia.app/api/verify \
  -H "content-type: application/json" \
  -H 'X-PAYMENT: <base64url( {sender, payTo, amount, transactionId, signature}> > \
  -d '{"transactionId":"tx_3f8a1b22c4d5e6f7","platform":"openlaw"}'

Response

{
  "status": "verified",
  "verificationId": "clwxyz...",
  "transactionId": "tx_3f8a1b22c4d5e6f7",
  "token": "<compact HS256 JWT — sub===verificationId>"
}
Try it

Embedded sandbox

Fire a same-origin call against /api/docs/verify and read the canonical wire shape back. No external tools, no auth — the docs surface is public.

transactionId ready

Awaiting call. Send a request to render the wire shape back.

Ready to integrate the real endpoint?

Request a sandbox API key →