Skip to content

The API

The API is Express 5 under /api. Everything returns JSON except the CSV exports, the Stripe webhook, /sitemap.xml and /robots.txt. Money is integer cents in every field, named *Cents.

  • Admin: a session cookie set by POST /api/session, 24 hours, SameSite=Lax, Secure in production. GET /api/session returns { isAdmin, csrfToken, isConfigured }. Every mutation sends the token as x-csrf-token.
  • Customer: a separate session on /api/account/session, with the same CSRF rule. A customer session is refused by every admin route.
  • Storefront password: when visibility is password, everything below the gate returns 401 until POST /api/storefront/unlock succeeds. The admin’s own session always passes.

The login route answers identically for a known and an unknown email, and hashes a decoy password so the two branches cost the same.

Route Returns
GET /api/store The whole store snapshot: settings, theme, hero, collections, page summaries, live products. Capped at STORE_SNAPSHOT_LIMIT (200) products. 503 with needsSetup: true before setup; 401 when locked.
GET /api/collections Collections with rendered introductions.
GET /api/products?collection=&search=&limit=&offset= Paginated live products, for catalogues too large for the snapshot.
GET /api/products/:slug One live product.
GET /api/pages, GET /api/pages/:slug Published pages; bodies rendered from Markdown on the server.
POST /api/shipping/quote Rates for a cart and a destination country.
POST /api/checkout Creates a Stripe Checkout Session from { lines: [{ productId, variantId, quantity, options }], shipToCountry }. Returns the redirect URL.
GET /api/checkout/:sessionId Order status for the confirmation page.
POST /api/cart/sync, /recover, /unsubscribe Abandoned-cart machinery.
POST /api/storefront/unlock Storefront password or share-link token.
POST /api/webhooks/stripe Stripe’s inbound webhook. Raw body, signature-verified.
GET /sitemap.xml, GET /robots.txt Live products and collections; empty when locked.

/api/account/register, /verify, /session (POST and DELETE), /password/forgot, /password/reset, and under /api/account/me: the profile, /orders, /orders/:id, /addresses with create, update and delete.

GET | POST | DELETE /api/session, POST /api/invites/accept, POST /api/session/forgot-password, POST /api/session/reset-password, GET | POST /api/setup.

All under /api/admin, all behind the session and CSRF.

Area Routes
Environment GET /environment, POST /email/test
Products GET /products, GET /products/full, GET /products/:slug, POST /products, PUT /products/:id, DELETE /products/:id, POST /products/reorder, POST /products/:id/publish
Product images POST /products/:id/images, PUT /products/:id/images (alt and variant), DELETE /products/:id/images, POST /products/:id/images/reorder
Catalogue CSV GET /products.csv, POST /products/import/validate, POST /products/import/commit
Collections GET, POST, PUT /:id, DELETE /:id, POST /:id/cover, POST /reorder
Pages GET, POST, PUT /:id, DELETE /:id, POST /preview, POST /reorder
Settings GET /settings, PUT /settings, POST /settings/logo, POST /settings/hero-image
Storefront `GET
Shipping `GET
Orders GET /orders, GET /orders.csv, GET /orders/:id, PUT /orders/:id, POST /orders/:id/refund
Staff GET /users, POST /users (invite), DELETE /users/:id, DELETE /users/invites/:id, PUT /users/me/password
Webhooks GET, POST, PUT /:id, POST /:id/secret, DELETE /:id, GET /:id/deliveries, POST /deliveries/:id/redeliver

Every one of these is listed in server/security.test.ts, which is what guarantees the table above is complete and protected.

Request and response shapes are the zod schemas in shared/schema.ts, shared/api.ts, shared/orders.ts, shared/shipping.ts and shared/webhooks.ts. They are the documentation; a shape that is not in shared/ is not part of the contract.

There is no API key. A script signs in with an administrator’s email and password, keeps the cookie, fetches the CSRF token, and sends it on every write. Scripting the admin API is a working example.