Acme Store Docs
API Reference

API Reference Overview

The public Store REST API and the authenticated Commerce RPC API at a glance.

The boilerplate exposes two distinct API families, plus a handful of optional, feature-gated endpoints. Understanding which family you are talking to is the key to using the API correctly:

  • The Store REST API (/api/store/*) is a public, read-only, cache-friendly view of the catalog. It is meant for external clients — native apps, alternate frontends, integrations — that need to read products and categories without touching internal storage details.
  • The Commerce RPC API (/api/commerce/*) is the authenticated backend the app itself calls when it runs on a SQL backend (Postgres/Prisma or Supabase). It is an internal transport, not a public API, and every non-public method requires a Firebase ID token.

The default quote-based store runs entirely on Firebase and mostly bypasses the Commerce RPC bridge (the client talks to Firestore directly). The RPC API matters when you swap in a SQL backend. See backends for how the commerce layer is wired.

The two API families

Store REST APICommerce RPC API
Base path/api/store/api/commerce/<service>/<method>
StyleRESTful, resource URLsRPC over POST
MethodGET (read-only)POST
AuthNone (public)Firebase Bearer token (public/user/admin)
AudienceExternal clients, native appsThe app's own client, SQL backends
CachingCDN Cache-Control headersNone (force-dynamic, live data)
Versionv1Unversioned internal bridge
WritesNeverYes (carts, quotes, orders, admin)

Store REST API

The Store API reshapes data that is already publicly readable (products, categories) into a stable wire format so callers never depend on internal storage details. Inventory counts, for example, are collapsed into an inStock flag so exact stock levels are never exposed.

GET /api/store is a discovery document that lists the available endpoints and the API version:

Terminal
curl https://your-store.example.com/api/store
{
  "name": "Your Store",
  "version": "v1",
  "endpoints": [
    "/api/store/products",
    "/api/store/products/{id}",
    "/api/store/categories"
  ]
}

Key characteristics:

  • Read-only. Every endpoint is a GET. There are no public write paths.
  • Cursor pagination. GET /api/store/products returns { products, nextCursor, totalCount }; pass the returned nextCursor back as ?cursor= to page forward. See Store: Products.
  • CDN cache headers. Responses carry a public Cache-Control header so a CDN or the browser can cache them (details below).
  • Stable wire shape. Products are returned as a narrowed PublicProduct (id, productName, brand, price, categories, description, primaryImageURL, pdfLink, inStock).

Commerce RPC API

POST /api/commerce/<service>/<method> is an RPC-style bridge from the client to a server-side commerce adapter. The client posts one request per commerce call:

POST /api/commerce/products/list
Authorization: Bearer <firebase-id-token>
Content-Type: application/json

{ "args": [] }

and receives { "result": <return value> }, or { "error": string } with a 4xx/5xx status. Dates in the result serialize to ISO strings and are revived by the client.

Access levels

Firebase Auth is the identity provider. Each whitelisted method carries one of three access levels:

LevelRequirement
publicNo auth. Catalog reads and discount lookup.
userValid token required; the method's uid argument (args[0]) must equal the token's uid, so a signed-in user can only act on their own cart/quotes/orders/profile.
adminValid token required and admin.isAdmin(uid) must return true.

quotes.submit is the one exception to the user rule: it allows a null userId for guest submissions (no token needed then), but a non-null userId must still match the token.

A method that is not on the whitelist responds 404, indistinguishable from a non-existent route. See Commerce RPC for the full whitelist and per-service argument shapes.

The Commerce RPC route runs on the Node.js runtime and requires a configured SQL backend (getServerCommerce()); if the backend is not configured, every call responds 500 with { "error": "Commerce backend is not configured." }.

Optional feature endpoints

These endpoints back optional, env-gated features. They keep working as no-ops (or return 503) when their integration is not configured, so the default quote-based store runs without any of them.

EndpointMethodFeatureWhen unconfigured
/api/checkoutPOSTStripe Checkout (Checkout)503 { "error": "checkout not configured" } without STRIPE_SECRET_KEY
/api/webhooks/stripePOSTStripe order recording (Webhooks)Inactive without Stripe configured
/api/notifications/statusPOSTResend status emails (Notifications)200 { "skipped": true } without RESEND_API_KEY

See environment variables for the flags that switch these on.

Common error shape

Both API families return errors as a single JSON object:

{ "error": "Human-readable message" }

The Store API uses jsonError(status, message) and the Commerce RPC route uses an equivalent error(message, status) helper, so the shape is identical across the surface. Request data is never echoed back in error messages.

Standard status codes

StatusMeaning
400Bad request — malformed input (e.g. invalid cursor, unknown category, short search term, or a body that is not { args: [...] }).
401Authentication required — a protected Commerce RPC method was called without a valid token.
403Forbidden — token is valid but not permitted (acting on another user's data, or missing admin privileges).
404Not found — no such product, or an unknown Commerce RPC service.method.
429Too many requests — rate limit hit (checkout and notifications endpoints). A Retry-After header is included.
500Internal server error — unexpected failure, or the Commerce backend is not configured.
503Service unavailable — an optional feature (e.g. Stripe checkout) is not configured.

Cache-Control policies

The Store API sets one of two Cache-Control headers depending on how quickly the underlying data changes. Both are defined in src/lib/api/store.ts:

src/lib/api/store.ts
/** Cache-Control for product endpoints (list, search, detail). */
export const PRODUCT_CACHE_CONTROL =
  "public, s-maxage=60, stale-while-revalidate=300";

/** Cache-Control for slow-changing config-backed endpoints (categories). */
export const CONFIG_CACHE_CONTROL =
  "public, s-maxage=3600, stale-while-revalidate=86400";
PolicyHeaderApplied to
PRODUCT_CACHE_CONTROLpublic, s-maxage=60, stale-while-revalidate=300/api/store/products, /api/store/products/{id} (product list, search, and detail)
CONFIG_CACHE_CONTROLpublic, s-maxage=3600, stale-while-revalidate=86400/api/store (discovery) and /api/store/categories

s-maxage is the shared-cache (CDN) freshness window in seconds; stale-while-revalidate lets a CDN serve stale content for that many additional seconds while it revalidates in the background. Products stay fresh for 60s; category and config data, which changes rarely, for an hour.

The Commerce RPC route is declared force-dynamic and sets no cache headers — every RPC call resolves live data.

Explore the endpoints

On this page