Initiate a step-up flow for a given scope. The SDK handles challenge token caching, DPoP proofs, and automatic session refresh on completion.
import { PrldErrors } from "@prelude.so/js-sdk";try { const { status } = await client.requestStepUp({ scope: "transfer:write", metadata: { amount: "500", currency: "USD" }, onChallenge: (info) => { // Called when a challenge is created or the scope is granted console.log(info.currentStep); // current step key, or "completed" console.log(info.steps); // array of { order, key, done, expirationDuration } console.log(info.challengeId); // use this to drive OTP and continue flows }, }); if (status === "block") { // Scope denied by your backend } // "continue" → scope granted immediately (session refreshed automatically) // "review" → challenge created, complete the steps below} catch (error) { if (error instanceof PrldErrors.ScopeNotAllowed) { // Scope is not in the allowed_scopes configuration }}
Field
Required
Description
scope
Yes
The scope to request. Must match an allowed_scopes entry.
metadata
No
Key-value pairs forwarded to your hook (e.g. transaction details).
onChallenge
No
Callback receiving challenge info after each step transition.
The onChallenge callback receives a StepUpChallengeInfo object:
Field
Type
Description
currentStep
string
The key of the current step, or "completed" when all steps are done.
scopeRequested
string
The scope being requested.
challengeId
string
The challenge ID — pass this to startOTP and checkOTP.
steps
array
All steps with order, key, done, and expirationDuration.
When the current step is verify_sms or verify_email, use the OTP methods with the challengeId from onChallenge:
// Send the OTPawait client.startOTP({ challengeId: challengeId });// Verify the code — advances to the next step automaticallyawait client.checkOTP({ code: "123456", challengeId: challengeId, onChallenge: (info) => { console.log(info.currentStep); // next step key, or "completed" },});
For custom steps (e.g. kyc_review), your backend issues a verification token after the user completes the step on your side. Pass it to the SDK:
// verificationToken is the RS256 JWT issued by your backendawait client.continueStepUp(verificationToken, (info) => { console.log(info.currentStep); // next step key, or "completed"});
The SDK extracts the challenge_id from the verification token, retrieves the cached challenge token, and sends both to Prelude.
When the last step is completed, the SDK automatically:
Refreshes the session with the challenge token
Clears the step-up cache for that challenge
Calls your onChallenge callback with currentStep: "completed"
The new access token from client.refresh() will include the granted scope. No manual refresh call is needed.
Try it
This example builds on the project from Introduction. Make sure you have a working OTP login first (OTP Login).1. Create a mock hookGo to mockerapi.com and create a new mock API that returns the following JSON on POST:
Copy the generated mock URL (e.g. https://free.mockerapi.com/mock/xxxxxxxx).2. Configure step-upCreate a step-up configuration pointing to your mock hook. The jwks_url can be any valid URL since we only use managed steps here:
Run npm run dev, log in with your phone number, then click Request transfer:write scope. You’ll receive a second OTP to complete the step-up challenge. After verification, the access token will include the transfer:write scope.