API (api-v2)

Storefront, internal & ops

Beyond the dashboard and the till, api-v2 serves merchant-run websites, internal tooling and one-off ops jobs. It also manages per-shop app installs.


Storefront

For merchant-run websites. Shoppers authenticate as Customer-type users.

Table: shop_cart_item (a server-side cart)

Routes, all under /storefront/v1/:

GroupRoutes
Authsend-otp, login, refresh-token, logout, handoff, handoff/exchange
Catalogueget-shop, get-categories, get-products, get-product, search-products, search-product-facet-values, get-product-filters, get-sections, get-section-display-types
Cart & checkoutsave-cart-item, delete-cart-item, get-cart, place-order
Accountget-orders, get-order, get-customer
  • Orders are placed as pending. There's no online payment gateway.
  • Pickup, or delivery at the shop's flat shipping rate.
  • Promo codes are supported. Orders are auto-routed to a location, with preorder availability status.
  • requireShopCustomerAccess guards shopper-scoped data.
  • Swagger at /storefront/docs. Generate the spec with npm run docs:openapi:storefront.

Internal API

Mounted only when INTERNAL_SECRET_KEY is set. Docs are public at /internal/docs.

RoutePurpose
/internal/shop/v1/search-shops, get-shop-statusesShop admin
/internal/product/v1/search-products, get-product-statusesCatalogue admin
/internal/order/v1/search-ordersOrder admin
/internal/user/v1/search-customersCustomer admin
/internal/merchant/*The whole merchant API, re-pathed and reusing the merchant handlers, acting as owner (require-internal-acting-as.ts)

Owner authority over every shop

The internal key grants owner-level access to every shop through /internal/merchant/* (docs/openapi/AUTH.md). That was accepted only for a non-production deployment. See Known gaps.

Ops: catalogue import

RouteAuth
POST /internal/ops/v1/import-book-catalogueBearer OPS_TOKEN (routes don't exist without it)
GET job statusBearer OPS_TOKEN
  • Also available as a script: scripts/import-book-catalogue.ts.
  • Dry run by default. Idempotent: products are matched on barcode/ISBN.
  • Writes only through the owning services, so cache, search and the ledger stay consistent.
  • Skipped rows and warnings are reported.
  • Production runs imported 4,356 books (shop 2) and 23,870 (shop 3).
  • Companion scripts:
    • upload-catalogue-to-cloudinary.mjs
    • link-product-images.mjs: SKU-named images go to Cloudinary and are attached to products

Apps

Tables: app, shop_app_install

RouteGuard
get-apps, get-installed-appsany view code
install-app, uninstall-appowner
  • Today's apps are reports, jarde and shelves.
  • Installation is per shop and is not a plan entitlement.
  • requireAppInstalled returns app_not_installed (403), and the dashboard hides app nav items until get-installed-apps confirms them.

Scripts & seeds

KindScripts
Repair & backfillbackfill-order-payments.mjs (missing ledger payment entries), backfill-search.ts, reindex-products.ts (Algolia), backfill-contributor-roles.ts, module-boundaries-baseline.ts
Dev seedsseed-pos-demo.mjs, seed-dashboard-demo.mjs, seed-pos-sections.mjs, seed-dev-plans.mjs
Client data (run against prod via .env.prod)seed-cafe-modifiers.mjs (--assign attaches groups to drinks), seed-cafe-sections.mjs, seed-book-sections.mjs, fix-cafe-drink-products.mjs, add-cafe-missing-drinks.mjs
Toolingreset-test-db.mjs, dump-schema.mjs, wait-for-postgres.mjs, kill-port.sh, OpenAPI generators → docs/openapi/{merchant,storefront,internal}-api.json

All seed scripts are dry-run by default and write only with --apply.

backfill-search.ts is a hand-kept copy

backfill-search.ts duplicates the search indexer's logic. Change both in the same commit.

Previous
Ledger, FX & reports