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
Create the Relay session before starting your selected client-side integration. Store the Relay token and Relay secret on your server — never expose the Relay secret to the client. Return the Relay session access token to your client.
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.
Issuing a Privacy Pass
A Privacy Pass is the billing unit for Relay. It is billed on creation and redeemed to fetch the claim result. The issuance request uses your API key and identifies your platform to Persona for billing. Redemption uses the Privacy Pass token instead of your API key.
Issuance depends only on the claim-type — it is not tied to a specific relay, so you can request a challenge and issue a pass at any time, even before the relay is created.
A Privacy Pass expires 90 days after issuance, so avoid issuing too far ahead of when you expect to redeem it.
Get the challenge
Request a challenge for the claim type you want to verify. This call is unauthenticated and returns the values (challenge, token-key, token-key-id) needed to construct a blinded Privacy Pass token.
Issue against the same claim type as your relay
The token-key you receive is determined by the claim-type in your request, so the resulting Privacy Pass is bound to that claim type. A pass can only redeem a relay created with the same claim type — if they don’t match, the pass won’t be able to redeem the claim. Always request the challenge with the same claim type you used to create the relay.
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
Begin claim retrieval when your client-side integration indicates that the user-facing flow is complete:
- Embedded Widget: Call your server from
onComplete. - Hosted Flow: Begin polling your backend when the user launches Hosted Flow.
- iOS SDK and Android SDK: Call your server after the platform’s normal Inquiry completion mechanism reports that the user-facing experience ended.
Redeem the claim from your server using the stored Relay token, Relay secret, and Privacy Pass token. Never expose the Relay secret or Privacy Pass token to the client.
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.

