Authentication
Store keys identify the store; customer sessions identify the shopper. How both work.
There are two independent kinds of auth in StoreKit. Don't mix them up:
| Identifies | Sent as | Secret? | |
|---|---|---|---|
| Store key | The store | X-Store-Key header | Yes — server-only |
| Customer session | The logged-in shopper | Authorization: Bearer <token> | Per-user |
Store key
Every /v1 request must carry your publishable store key in the X-Store-Key
header. It tells the API which store the request is for, and what it's allowed
to do (via scopes like products:read).
The store key is a server secret. With the SDK's Next.js adapter you never
handle the header yourself — initStorekit() adds it server-side, and the
browser only talks to your same-origin proxy. Never ship the key to the client.
Public catalog endpoints (products, categories, search, store, menu) need only the store key — no customer login.
Customer sessions
Shoppers log in with their phone number and a one-time password (OTP). There's no password to manage.
Request an OTP. POST /v1/auth/otp/request with { phone } sends a code by SMS. (In dev, the code is logged to the API console instead.)
Verify it. POST /v1/auth/otp/verify with { phone, otp } returns a session token and the customer. A new customer record is created on first login.
Use the token. Send it as Authorization: Bearer <token> on customer-scoped requests (cart me, checkout, orders, addresses).
Log out. POST /v1/auth/logout invalidates the token server-side.
Sessions last 7 days. OTPs expire after 5 minutes, allow 3 attempts, and a phone can request at most 5 OTPs per hour.
With the SDK (Next.js)
The adapter stores the token in an httpOnly cookie — invisible to JavaScript — and attaches it automatically. You just call:
import { storekit } from "@/lib/storekit";
await storekit.auth.requestOtp("+919999999999");
const { data, error } = await storekit.auth.verifyOtp("+919999999999", "123456");
// On success the session cookie is set for you.In the browser, the hooks call the proxy and the cookie rides along automatically:
const { data } = await storefront.auth.verifyOtp("+919999999999", "123456");
const { data: session } = storefront.useSession(); // { data: customer | null }Tokens never reach the browser
When verifying through the Next.js proxy, the bearer token is written to the httpOnly cookie and stripped from the response — the browser receives the customer object, not the token.
Guest vs. logged-in
Most catalog and cart operations work for guests (no session). Logging in lets you:
- Claim a guest cart into the customer's account (handled automatically on verify).
- Check out, view orders, and manage saved addresses.
See Carts & checkout for how guest carts merge into customer carts.
Advanced: server-to-server token
For trusted backend-to-backend calls, the API also accepts an internal
X-Internal-Storefront-Token (an HMAC token carrying the store id) in place of
the store key. This is an advanced escape hatch — most apps never need it.