Appearance
Login flow (web)
Four doors into the same session. Each one ends with a customer token, sent as Authorization: Bearer … on every customer call afterwards. They differ in what they prove and in one thing that matters for new customers: three of them verify an unverified account, and the password door refuses one.
Play the flow
shop.example.com/
shop · rewards · scan
everything from batch, in one place
shop, scan a product, or check your rewards
log in
Page not designed yet — drawn from what the documented flow requires.
States
This journey is one path through the authentication statechart, which traces every edge to code.
There is no logout endpoint: the customer token is a JWT, and signing out means the page forgets it. Which account states each door accepts is under Lifecycle.
API calls by door
| Door | Calls | Verifies an unverified account? |
|---|---|---|
| Password | POST /api/v1/login with password | No — refused until the account is verified |
| Emailed code | POST /api/v1/send-otp, then login with otp | Yes |
| Magic link | POST /api/v1/send/magic/link, then POST /api/v1/magic/login/:magicToken | Yes |
POST /api/v1/user/register/google-oauth — email and oauth_access_token | Creates or reuses the account; returns existingCustomer |
All of them need Origin + Brand key, except magic/login, which needs only Origin — the token in the link identifies the customer.
What the API forces onto this page
- Failures come back as HTTP 200. A wrong password or code is
success: falsein the body. Only the auth middleware answers 401. - reCAPTCHA is conditional — required on
loginandsend-otpfor website brands that are neither webflow nor marketplace, and checked whenever it is sent. - The magic link points at the storefront, at
/magic/login/<token>. That page must read the token from its own URL and post it to the API. Thedevice_idcookie thatsend/magic/linksets is not read by the login call. - A banned account is refused by the password, code and magic-link doors, with a message to contact support.
- If both
passwordandotpare sent, the password is checked.