POST /v2/storefront/preview-token mints a short-lived signed token that the Template Designer Live Preview iframe uses to render the merchant’s draft storefront. The token is bound to a specific shop and expires after 10 minutes.
Closes Phase 3 of DEV-AUDIT #199. The Template Designer Live Preview surface in the shop-builder dashboard calls this endpoint, embeds the returned
previewUrl into an iframe, and the storefront verifier (separate ticket) HMAC-checks the token before rendering the draft.When to use this
- The Template Designer’s “Live Preview” CTA — mint a token, embed
previewUrlinto the iframesrc - Any merchant-facing tool that needs a time-bounded preview link to a draft storefront
- Do NOT use this for production publishing — published storefronts don’t need a token
Authentication
Merchant JWT (MerchantOnlyJwtGuard). The endpoint also asserts req.user.shopId === body.shopId at the controller layer — a merchant can only mint preview tokens for their own shop. Cross-shop mint attempts return 403.
Request
Curl example
Response (200)
Field reference
Token format
<payload-b64url>.<sig-b64url> where:
payload— UTF-8 JSON{ shopId: string, expiresAt: ISO-8601 string, kind: "storefront-preview" }then base64url-encodedsig— HMAC-SHA-256 of the payload bytes underPREVIEW_TOKEN_SECRET(orJWT_SECRETfallback for dev), then base64url-encoded
. separator. Shopfront verifier:
Why HMAC over JWT
The shopfront verifier is a single shared-secret check at the storefront edge. HMAC avoids:- JWKS round-trips (no key fetch from a JWKS endpoint at request time)
- Asymmetric key rotation surface (no private/public key pair, no kid)
- Edge-cache invalidation on key rotation
- The full jose library dependency at the storefront edge
{shopId, expiresAt, kind}) is also stricter than typical JWT claim taxonomy — iss/aud/jti/sub buy nothing for a 10-minute single-purpose token. If we ever need rotation, audience-narrowing, or a kid, the swap is one file behind PreviewTokenService.mint — the controller response shape does not change.
TTL + rate limiting
- TTL: 10 minutes from mint. Hard-coded constant
PREVIEW_TOKEN_TTL_MSin the service. - Rate limit: not applied at v1. The cross-shop guard limits abuse blast radius to “self-DoS your own preview.” Revisit if cross-merchant abuse signal appears.
- Single chokepoint: all minting goes through
PreviewTokenService.mintso audit-log + rotation hooks can be added in one place.
Error responses
Related
- Get abandoned cart details — sibling merchant-scoped surface
- Platform fees revenue rollup (admin) — sibling admin operator surface
- Trust fabric stats — sibling public read surface