API Usage
The API integration gives you full control over the Relay flow. Unlike the Server SDK and Gateway Service, you are responsible for implementing the Privacy Pass protocol yourself.
If your language or runtime has library support for blind RSA cryptography (e.g. @cloudflare/blindrsa-ts), this integration path gives you the most control. Otherwise, consider the Gateway Service to avoid implementing the protocol yourself.
Creating a Relay session
Call this before rendering the widget. Store relay-token and relay-secret securely on your server — never expose these to the client. Return relay-session-access-token to your frontend to initialize the widget.
Recommended: Encrypt your claim payload
We recommend generating an asymmetric key pair so that the claim payload is encrypted and only decryptable by your server.
Once your frontend has the access token, initialize the widget and wait for onComplete before proceeding. See Widget usage.
Issuing a Privacy Pass
A Privacy Pass is the billing unit for Relay — billed on creation and redeemed to fetch the claim result. Since issuance uses your API key, Persona knows your platform identity at this point for billing purposes. Redemption remains fully anonymous.
A relay must exist before a Privacy Pass can be issued — the challenge is obtained through the relay’s endpoint. Issuing immediately after creating the relay, before the widget completes, is recommended: it makes it harder for Persona to correlate your platform identity to a relay via timing.
Get the challenge
Call generate-claim without a PrivateToken header. Persona responds with a 401 containing a WWW-Authenticate header — this is expected, not an error. The values in this header (challenge, token-key, token-key-id) are needed to construct a blinded Privacy Pass token.
Blind, sign, and unblind
Construct your privacy-pass-token using the parsed challenge values. See Challenge Mechanism for the full protocol — including how to parse the challenge, construct the token input, blind the message, and assemble the final token.
Submit the blinded token for signing. This step requires your Persona API key — you can find it in the Persona Dashboard under API Keys.
Unblind blind-sig using your blinding inverse inv and assemble the final privacy-pass-token. See Challenge Mechanism → Step 4.
Redeeming the claim
onComplete should trigger a call to your server to redeem the Privacy Pass.
The Privacy Pass token is consumed atomically on a successful claim — once redeemed, it cannot be used again. This creates a risk: if a successful redemption request drops mid-flight and your server never receives the response, retrying with the same token would normally result in a double-spend error.
Using idempotency keys
Pass an Idempotency-Key header on every redemption request. Persona caches the result of the first successful request against that key — if you retry with the same key, the original response is returned without attempting to re-redeem the token.
Recommendations:
- Generate the idempotency key once before the first attempt and reuse it on every retry for the same redemption request
- Use a UUID or other cryptographically random string — do not reuse keys across different relay redemptions
- Keys are pruned after 24 hours — if you retry after that window, a new request is generated
Idempotency keys have no effect if the original request failed before the token was consumed. In that case, token-consumed will be false in the response and it is safe to retry without a new token. See Idempotence for how Persona’s idempotency layer works in general.
Parsing the claim payload
If you opted out of encryption, parse claim-payload directly:
If you provided an encryptionKeyPem, decrypt the payload with your private key first:
See the claim payload schema for the full type definition.

