Litestore · Developer docs
API

REST

Some of Litestore's REST routes are stable for outside callers, and each group of routes checks the caller in its own way.

Route handlers under app/api serve callers that can't use a server action or an oRPC procedure. A payment provider posting a webhook, a browser uploading a file and an AI agent reading the catalog all reach Litestore this way.

Request

GET/api/products?q=linen&limit=12
AccessNo key needed
NotesDescribed by /api/openapi.json
A call to /api/products changes as you add or remove query parameters such as q, category and limit.

Litestore publishes an OpenAPI document at app/api/openapi.json that describes some of these routes. We wrote it for AI agents and outside integrations, and it deliberately covers fewer routes than the folder contains.

Keep this difference in mind. A route under app/api is not public just because it is there. The contract lists the routes we invite outside callers to depend on. Every other route is internal code that still happens to be reachable over HTTP.

OpenAPI contract

app/api/openapi.json/route.ts builds the spec inside its GET handler instead of serving a static file. It does this so the servers entry can use the deployment's own base URL, which it reads from env.NEXT_PUBLIC_SITE_URL. A static file would need a hardcoded host, and that host would be wrong in every environment but one.

The document calls itself the AI Commerce API:

app/api/openapi.json/route.ts
const spec = {
  openapi: "3.0.3",
  info: {
    title: `${config.site.name} AI Commerce API`,
    version: "1.0.0",
  },
  servers: [{ url: baseUrl, description: "Production server" }],
  tags: [
    { name: "Discovery", description: "API discovery and capabilities" },
    { name: "Products", description: "Product catalog operations" },
    { name: "Recommendations", description: "Product recommendations" },
    { name: "Tracking", description: "Event tracking" },
    { name: "AI", description: "AI-powered content generation" },
  ],
  // …
}

Five tags group everything the document advertises:

TagWhat it covers
DiscoveryAPI discovery and capabilities
ProductsProduct catalog operations
RecommendationsProduct recommendations
TrackingEvent tracking
AIAI-powered content generation

The document currently describes these paths:

/.well-known/ai-commerce
/api/capabilities
/api/products
/api/products/feed
/api/products/{id}
/api/recommendations
/api/track
/ai/v1/suggest-categories

The last path is stale: app/ai/v1/ now holds only admin-chat and fill-product, and the spec lists neither.

The spec is hand-written

The document is a literal inside the route handler. Nothing generates it from the handlers it describes, and only a comment says to keep it in sync. A new endpoint won't appear here until someone adds it.

Discovery

We designed the API so that an agent can arrive at the store's domain and work out the endpoints by itself, without a person handing it a URL.

/.well-known/ai-commerce

This route lists the endpoints, the capabilities and the authentication each one needs. An agent should try it first, and it links on to the OpenAPI document.

/api/capabilities

This route reports whether the platform is ready, which AI providers are configured, which features are on and which currencies the store uses.

/api/capabilities needs a signed-in admin session. The spec lists it under Discovery, but it is the one discovery route that isn't open. It shows how the operator configured the store, and shoppers don't need that.

Route groups

The route tree splits into seven groups. They differ mostly in who may call them and how the caller proves it.

GroupRoutesHow the caller proves who it is
Commerce for agentsproducts, recommendations, cart, trackNone for public reads (rate limited per IP); the cart routes use the shopper's cart session or a service key with a cart scope. We wrote the OpenAPI document for these routes.
Discovery.well-known/ai-commerce, .well-known/ucp, capabilities, openapi.jsonNone, except capabilities, which needs an admin session.
MCPmcp, mcp/adminThe shopper route is public and rate limited per IP. The admin route needs a service key with the admin:mcp scope, sent as a Bearer token.
Webhook managementwebhooks, webhooks/[id], webhooks/eventsA service key with the read or write:webhooks scope.
Inbound webhooksstripe/webhooks, webhooks/resendThe provider's signature on the request body. No Litestore credential is involved.
Platforminngest, health, upload, media, messages/upload, reviews/upload, locale, unsubscribe, feed-eventsEach handler does it differently.
Admin and AIadmin, aiAn admin session. data serves static tag and option presets without auth, and rpc (oRPC) takes a service key.

The platform routes exist because the platform needs an HTTP endpoint, not because an integrator asked for one. Each of them handles auth differently, so read the handler instead of assuming a rule for the group.

For the inbound webhooks, Stripe or Resend calls the route, and the signature they put on the request body proves who sent it.

Other routes

The MCP routes, the webhook management routes and the inbound provider webhooks appear in neither the OpenAPI contract nor the oRPC router. We left them out on purpose. Each one has its own kind of authentication, and a shared contract would describe them wrongly.

Integrators don't call a webhook route. A specific provider posts to it, Litestore checks that provider's signature, and the provider determines the shape of the request.

The MCP routes speak JSON-RPC 2.0 instead of REST. An MCP client finds their tools through tools/list, not through an OpenAPI document, so no MCP client would read an OpenAPI description of them.

Adding routes

Use a server action when Litestore's own admin or storefront UI is the caller.

For outside integrators to depend on the new route, writing the handler is only half the work, because the hand-written spec won't pick it up by itself.

A route becomes part of the published contract when someone adds it to app/api/openapi.json/route.ts, and not before.

On this page