OTP Email Verification with SLAS
Starting with B2C Commerce 26.6, use the Shopper Login and API Access Service (SLAS) to verify registered shopper identities by using a one-time password (OTP). This feature enables email verification during shopper registration, email updates, and other identity confirmation flows. Integrate OTP email verification with SLAS from any headless application. Currently, only registered user flows support SLAS OTP email verification.
OTPs provide a time-limited, single-use code delivered to a shopper’s email address. When /otp/request is called, SLAS fetches the shopper’s profile from B2C Commerce, checks the emailVerified status, and, if verification is valid, generates an OTP and delivers it by email. After the shopper submits the code, a call to /otp/verify causes SLAS to validate it and mark the shopper’s email as verified in B2C Commerce.
SLAS doesn’t support SMS delivery of OTP. To have the OTP sent by SMS you will need to use mode=callback and manage the SMS delivery from your callback. See Passwordless Login with One-Time Passwords Using Callback URI. OTP Email verification only supports mode=email.
For SLAS to have the required shopper information to send to the callback, the shopper must have logged in at least one time before attempting OTP email verification.
Before you integrate OTP-based email verification, make sure that you have:
-
Email verification site preference enabled: The B2C Commerce Enable Email Verification preference must be turned on from Business Manager. When disabled,
emailVerifiedis null on all profiles.To enable email verification in Business Manager:
- Click App Launcher, and then select Merchant Tools | site | Storefront Login Preferences.
- Turn on Enable Email Verification.
- Click Apply.

-
A SLAS private client configured for passwordless login: OTP email verification requires a SLAS private client. If you don’t have one, create it before proceeding. See Configure a SLAS Private Client. Configure the client with
sfcc.pwdless_loginin its allowed scopes (configured in SLAS Admin under Clients → Scopes). Requests from clients that lack this scope are rejected with a 403. -
A client channel: The
channel_idmust be registered to the client. Thechannel_idin every OTP request must match a channel registered to the authenticating client. Configure in the SLAS Admin UI athttps://{short-code}.api.commercecloud.salesforce.com/shopper/auth-admin/v1/ui/. Go to Clients and click Edit next to the channel you want to configure. -
An OTP email template: Configure an OTP email template for the client channel. The template receives the shopper’s first name and the OTP code. See Configure Email Templates.
-
A valid shopper access token: Before you call the OTP email verification endpoints, obtain a SLAS shopper JSON Web Token (JWT) by using the standard authorization code or guest token flow. All OTP endpoints require a SLAS shopper bearer JWT in the authorization header. For more information, see Private SLAS Client Use Cases.
This diagram shows the end-to-end sequence for email verification after shopper registration.

The SLAS OTP email verification process has two primary flows: OTP request and OTP verification.
The OTP request flow initiates delivery of a one-time password to the shopper. SLAS handles the emailVerified check internally — the caller doesn’t need to inspect this field before calling the endpoint.

- Call POST /organizations/{organizationId}/oauth2/otp/request, passing the shopper’s
user_id,channel_id,client_id,email, and a valid shopper bearer token in the authorization header. - SLAS authenticates the OAuth client and validates the bearer JWT, confirming that
client_id,channel_id, anduser_idall match the token’s subject claims. A mismatch results in HTTP 400. - SLAS verifies the client has the sfcc.pwdless_login permission scope.
- SLAS applies a bot-backoff and per-user rate limiting to prevent OTP flooding.
- SLAS fetches the shopper’s customer profile from B2C Commerce and reads the
emailVerifiedfield:null— The email verification site preference is disabled. SLAS treats the email as verified, proceeds to generate and send the OTP, and returns 202.true— The email is already verified. SLAS proceeds to generate and send the OTP and returns 202.false— The email is explicitly unverified. SLAS doesn’t send the OTP and returns 400.
- SLAS generates an OTP token and computes a hash verifier. The entry expires after 10 minutes.
- SLAS sends the OTP to the shopper’s email address by using an email service with the configured email template.
SLAS automatically extracts the usid (unique shopper ID) from the bearer token’s subject claims. You don’t need to include it in the request body.
The OTP verification flow confirms that the shopper received and submitted the correct code.

-
Call POST /organizations/{organizationId}/oauth2/otp/verify, passing the
pwd_action_token(the code the shopper received),client_id,channel_id,user_id,usid, and the shopper bearer token. -
SLAS authenticates the OAuth client and validates the bearer JWT, confirming that the
client_id,channel_id, anduser_idmatch the token claims. -
SLAS applies a bot-backoff rate limiting to guard against brute-force guessing.
-
SLAS verifies the client has the
sfcc.pwdless_loginpermission scope. -
SLAS looks up the OTP entry by reconstructing the key from the supplied fields. The entry is atomically consumed (deleted) on first successful lookup — the token cannot be reused.
-
SLAS validates the hash verifier. A mismatch results in HTTP 400.
-
SLAS makes an internal call to the B2C Commerce
update-profileendpoint to setemailVerifiedtotrue. This call is best-effort: a failure does not block the 204 response from being returned. -
SLAS responds with HTTP 204 No Content.
- If the hash verifier fails or any parameters are invalid, SLAS responds with HTTP 400 Bad Request.
- If the authorization header, shopper JWT fails validation, or token validation fails**,** SLAS responds with HTTP 401 Unauthorized.
- If the scope validation fails, SLAS responds with HTTP 403 Forbidden.
Before calling the OTP endpoints, obtain a SLAS shopper bearer JWT. The shopper must authenticate before making any OTP requests.
Use the access_token from the shopper authentication response as the bearer token in the OTP calls below.
| Bash |
|---|
| curl -X POST \ “https://{shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/{organizationId}/oauth2/otp/request” \ -H “Authorization: Bearer {shopper_access_token}” \ -H “Content-Type: application/x-www-form-urlencoded” \ -d “client_id={client_id}&channel_id={channel_id}&user_id=shopper.example.com&mode=email |
Response: 202 Accepted
No response body is returned. If emailVerified is true or null on the shopper profile, the OTP is sent to the shopper’s email. If emailVerified is false, no email is sent.
| Bash |
|---|
| curl -X POST \ “https://{shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/{organizationId}/oauth2/otp/verify” \ -H “Authorization: Bearer {shopper_access_token}” \ -H “Content-Type: application/x-www-form-urlencoded” \ -d “pwd_action_token=85353215&client_id={client_id}&channel_id={channel_id}&user_id=shopper.example.com |
Response: 204 No Content
No response body is returned. The OTP is valid and the shopper’s emailVerified status is updated to true in B2C Commerce.
| Field | Required | Constraints | Notes |
|---|---|---|---|
client_id | Yes | Exactly 36 chars, base64url characters only | Must match a client registered for the tenant |
channel_id | Yes | Max 100 chars | Must be a channel registered to the client |
user_id | Yes | Max 128 chars | The shopper’s login ID or email address. If user_id is an email then user_id will be used to send the email, if not the email query parameter will be used. |
mode | Yes | — | Must be set to email |
email | No | — | The email address to deliver the OTP to. This email address will be used if the user_id isn’t a valid email address. |
locale | No | — | Defaults to en-us; controls the email template locale |
| Field | Required | Constraints | Notes |
|---|---|---|---|
pwd_action_token | Yes | Passwordless token format | The OTP code received by the shopper |
client_id | Yes | Exactly 36 chars, base64url characters only | Must match the client_id used in /otp/request |
channel_id | Yes | Max 100 chars, base64url characters only | Must match the channel_id used in /otp/request |
user_id | Yes | Max 128 chars | Must match the user_id used in /otp/request |
Both endpoints require a SLAS shopper bearer JWT in the authorization header. All values in the request body must exactly match the corresponding claims in the token. Any mismatch results in HTTP 400. SLAS also checks that the shopper session hasn’t been revoked before proceeding.
After a successful OTP lookup, SLAS validates a hash verifier to confirm the token wasn’t tampered with. The verifier is computed during /otp/request and re-validated during /otp/verify using a key. A mismatch results in HTTP 400.
Both endpoints enforce per-tenant bot-backoff rate limiting to prevent automated flooding and brute-force OTP guessing. When the threshold is exceeded, SLAS returns HTTP 429.
SLAS checks per-user OTP request rate limits before generating a new code. This prevents resource exhaustion from repeated delivery attempts for the same user.
OTP tokens are stored in Redis with a configurable time to live (TTL). The default TTL is 600 seconds (10 minutes). Tokens not consumed within this window expire automatically. Attempting to verify an expired token results in HTTP 403. Requesting a new OTP token invalidates any pending, unverified tokens for that user.
| HTTP Status | Scenario |
|---|---|
| 400 Bad Request | Invalid or missing request parameters; emailVerified is false; JWT claims mismatch between request body and token |
| 401 Unauthorized | Client authentication failed; invalid JWT (expired); OTP validation failed (expired, already used); failed hash verifier |
| 403 Forbidden | Client doesn’t have the sfcc.pwdless_login permission scope |
| 429 Too Many Requests | Bot-backoff threshold exceeded for the tenant |
To prevent information leakage, SLAS returns a generic 400 for all OTP verification failures, regardless of the specific reason (invalid/missing parameters, hash mismatch, and emailValidate is false). SLAS returns a generic 401 for client authenticate failure, expired token, failed hash verifier, and expired Shopper JWT. Don’t attempt to differentiate failure reasons based on the error response message.