Shopper accounts
Passwordless sign-in on your own store: shoppers see their orders, tracking, downloads, contact details and saved address, with no password anywhere.
Your shoppers can sign in to your store itself — no passwords, no extra app — and see their own orders, tracking, downloads, contact details and saved address. It is on by default on every store, and it needs no setup.
1. How signing in works
A shopper opens /account on your store. They enter their email address, and the store emails them two ways in: a six-digit code they can type, and a one-time link they can click. Either one signs them in for 30 days on that browser.
There is no password to choose, forget, reuse or leak, and there is nothing for you to reset.
The email is a normal store template, so you can edit it like any other: Automations → Email templates → Sign-in link (#automations/templates). It is sent from your store's verified sender address when you have one, and as your store over Formahand's shared store address until then, so shoppers can sign in from your first day.
What the store never says
POST /account/login always answers the same sentence — *"If that address has orders here, we sent a sign-in link and a code to it."* — whether or not the address belongs to a customer. Nobody can use the sign-in form to find out who shops with you.
Limits that keep it safe
- A code and a link expire 20 minutes after they are sent, and each works once.
- Five wrong codes spend the request; the shopper asks for a new one.
- 5 sign-in requests an hour per email address, and 20 an hour per client, per store.
- The session cookie (
fh_shopper) is first-party on your store's own hostname,HttpOnly,SecureandSameSite=Lax, so no other site can read it or ride on it. It lasts 30 days. - Your store keeps only hashes: never the code, never the link secret, never the session token.
2. The pages a shopper gets
They all render in your theme, exactly like a content page.
| Page | What it shows |
|---|---|
/account | Every order the shopper placed, with status, tracking link and any digital downloads |
/account/orders/<id> | One order: the items, totals, tracking and fresh download links |
/account/profile | Name, phone, and a single checkbox for marketing email |
/account/addresses | One saved default address |
A shopper only ever sees rows that belong to their own email address. Signing in as one person never reveals another person's order, whatever id is typed into the address bar.
The marketing checkbox writes exactly what the shopper asked for — subscribed or unsubscribed, for that one contact — and the change is recorded in your event log like any other customer change, so customer.updated flows still fire.
3. In your dashboard
Customers → Shopper accounts (the pill at #customers/accounts) shows how many shoppers have signed in, how many signed in this week, and one switch: Let shoppers sign in. Turning it off hides the account pages and refuses new sign-ins. Nothing is deleted, and turning it back on restores every account.
Each contact in the customer list carries accountStatus (active when they have a live session, none otherwise) and lastSeenAt.
4. For agents
describe_shopper_accounts— how sign-in is set up on this store, the routes, and the counts.revoke_shopper_sessions { customerId }— signs one shopper out everywhere, for example after they tell you a device was lost. Needscustomers:write.list_customersandget_customercarryaccountStatusandlastSeenAt.
No tool can read a sign-in code, a link or a session token, and no tool can sign in as a shopper.
5. For your own front end
If you run your own site on top of Formahand (see Build your own site or plugin), the same account lives behind JSON:
POST /api/storefront/account/login { email }
POST /api/storefront/account/verify { email, code } -> { token, expiresAt, customer }
GET /api/storefront/account (orders + profile)
PUT /api/storefront/account/profile { name?, phone?, marketing? }
POST /api/storefront/account/logout
On your store's own hostname the cookie is enough. From another origin, keep the token that verify returns and send it as `Authorization: Bearer <token>`; the cookie is never sent cross-origin, so a signed-in shopper on your site is always the one holding that token.
When a signed-in shopper starts checkout, pass their address as email in the POST /api/checkout body and the payment page is prefilled for them.
6. What this is not
Shopper accounts are not a login for your dashboard, and they give nobody access to your store's admin. A shopper sees their own orders and nothing else.
See also
- agents.md — the two tools here,
accountStatuson a customer, and what an agent can never read. - checkout-rules.md — a customer group's prices reach a cart only once the shopper is signed in.
- product-kinds.md — a signed-in buyer finds their digital downloads again from their account.
- headless.md §8b — the JSON half, for a front end on your own origin.