verify_sms, verify_email), you can define custom steps that your own backend handles — KYC review, biometric verification, document upload, or any other process. When a custom step is reached, your backend verifies the user and issues a signed token that Prelude validates to advance the challenge.
Make sure you are familiar with the Step-Up Authentication flow before reading this page.
Prerequisites
- A working step-up configuration
- An RSA key pair for signing verification tokens
- A public JWKS endpoint exposing your public keys
Setup
1. Register your custom step keys
Add your custom step keys and JWKS URL to the step-up configuration:a-z, A-Z, 0-9, and .-_:.
2. Expose a JWKS endpoint
Yourjwks_url must serve a standard RFC 7517 JSON Web Key Set containing the RSA public keys used to verify your verification tokens. Each key must include a kid (key ID).
3. Return custom steps from your hook
In your hook response, include your custom step keys alongside any managed steps:Completing a custom step
When the challenge reaches a custom step, the user completes it on your side (your UI, your backend logic). Once verified, your backend issues a verification token and the user sends it to Prelude to advance the challenge.1. Issue a verification token
Your backend creates an RS256 JWT signed with your private key:Token fields
| Field | Type | Description |
|---|---|---|
sub | string | The Prelude user ID. Must match the challenge token’s sub. |
exp | integer | Expiration time (Unix timestamp). |
nbf | integer | Not-before time (Unix timestamp). |
iat | integer | Issued-at time (Unix timestamp). |
jti | string | A unique JWT ID. Must be unique across all tokens. Prelude rejects reused JTIs. |
challenge_id | string | The challenge ID from the challenge token. Must match exactly. |
key | string | The step key being completed. Must match the current step in the challenge. |
status | string | Must be "completed". |
Token requirements
| Rule | Detail |
|---|---|
| Algorithm | RS256 only |
JWT header kid | Required — must match a key ID in your JWKS endpoint |
| JTI uniqueness | Each jti can only be used once. Replayed tokens are rejected with token_reused. |
| Field matching | sub and challenge_id must match the challenge token. The key must correspond to the current step. |
Example (Node.js)
2. Advance the challenge
Once your backend issues the verification token, the frontend SDK advances the challenge:- Fetching your public keys from your JWKS endpoint
- Verifying the RS256 signature and expiration
- Checking that
sub,challenge_id, andkeymatch the challenge token - Checking the
jtihas not been used before
Validation errors
| Error | Status | Description |
|---|---|---|
invalid_verification_token | 400 | The verification token is malformed, expired, or the signature is invalid. |
token_mismatch | 400 | sub, challenge_id, or key in the verification token does not match the challenge. |
step_not_completed | 400 | The step’s status is not "completed". |
step_bypassed | 400 | A previous step in the sequence has not been completed. |
step_not_found | 404 | The step key from the verification token does not exist in the challenge. |
token_reused | 409 | The verification token’s jti has already been used. |