API (api-v2)

Catalogue modules

Shops, categories, products, search, custom fields, modifiers, media and storefront sections. Route guards are shown in brackets. Every route is POST.


Guard legend

perm = requirePermission · wide = requireShopWidePermission · at = requirePermissionAt · owner = ownerOnly · oScoped = ownerScoped (closed to staff today). See Guards.

Shop

Tables: shop, shop_localization

RouteGuard
/merchant/shop/v1/get-shops, get-shop-localization, create-shop, get-user-shops, delete-shop-imagesoScoped
update-shop, delete-shopowner
  • Fields: title, description, logo, cover, contact email and phone, language, currency, country, weight unit, flatShippingRate, tax_rate, taxes_included.
  • create-shop takes no fields. In one transaction it creates the shop, a first location ("Your Location"), and the shop's ledger.
  • Shops are soft-deleted.

Category

Tables: category, category_localization, shop_category

RouteGuard
/merchant/auth/v1/shop-categoriesperm products.view
get-categories, get-category-localizationoScoped
create-category, update-category, delete-categorywide categories.write

A localized tree. This is the reference module to copy.

Product & tags

Tables:

  • product, product_localization, product_category, product_image, product_option*, product_variant, product_metafield
  • tag, tag_localization, product_tag
RouteGuard
get-shop-products, get-shop-tagsperm products.view
get-products, get-product-localizationoScoped
set-productwide products.write
delete-productwide products.delete

set-product is a desired-state upsert. It covers options, variants, inventory, tags, custom field values, images and categories. Anything omitted is deleted, so clients must always send the whole product.

Variant fields: price, compare-at, SKU, barcode, weight, taxable, trackQuantity, continue-selling-when-out-of-stock, physical, country of origin, preorder flag and available-on date.

Multiple categories: product_category is many-to-many, with position 0 as the primary category. product.category_id is kept as a denormalised copy of the primary.

Tables: search_document, search_attribute, search_product_stock. This projection can be rebuilt.

RouteGuard
/merchant/shop/v1/search-products, search-product-facet-values, search-product-filters, search-product-filter-options, get-product-filters, search (global: products, orders, categories, sections)perm products.view
  • SEARCH_BACKEND=postgres is the default. Algolia is kept as a rollback, and writers dual-write to both.
  • Stock tabs (out, low, in stock) come from search_product_stock, which search.stock.changed keeps up to date.
  • Filterable custom fields become facets.

Custom fields ("entities")

Tables: entity_type, entity, field, field_value (+ localizations)

RouteGuard
get-category-fields, get-entity-types, get-entities, get-entityperm products.view
set-entity-type, delete-entity-type, set-entity, delete-entity, set-field, delete-fieldwide products.write
  • Fields are defined on a category and inherited by every descendant.
  • Entity types are reference lists (contributors, publishers, series, bindings).
  • Field values are exposed on the storefront product detail page.

Books aren't special-cased

There's no book code path. Books are a Books category tree with about 28 inherited fields (ISBN-13, copy type, condition, contributors…). Café items are ordinary products with modifiers and till sections.

Modifiers

Tables:

  • modifier_group: selection min/max
  • modifier_option: price delta in USD, direct cost, ticket label, default, active
  • product_modifier_group
  • order_line_item_modifier: a snapshot taken at sale time
RouteGuard
get-modifier-groups, get-product-modifier-groupsperm products.view
create/update/delete-modifier-group, create/update/delete-modifier-option, set-product-modifier-groupswide products.write
/pos/v1/get-modifier-groupspos.operate

At create-order the server validates every selection:

  • the option belongs to the shop and is active
  • its group is assigned to the product
  • each group's min/max is satisfied

Deltas are folded into the unit price, and each line can carry a note.

Not done yet (.scratch/cafe-modifiers/phases.md): Phase 0 (client pricing decisions) and Phase 4 (barista ticket / kitchen display).

Media

Table: image

RouteGuard
create-media-upload, register-mediarequireAnyPermission over the catalogue write codes
  • Uploads are signed for Cloudinary: the server mints the public_id and signature, the browser uploads directly, and the secret never leaves the server.
  • Size limits come from MEDIA_MAX_IMAGE_BYTES and MEDIA_MAX_VIDEO_BYTES.

Storefront sections

Tables: section, section_item (an item is a product, a category or another section), section_image, shop_sections

RouteGuard
get-sections, get-section-localization, update-sectionoScoped
/merchant/auth/v1/shop-sectionsperm products.view
get-section-item-types, get-section-display-typesauthenticated
create-section, delete-section, update-section-orderwide sections.write

These are distinct from till sections; see POS & shifts.

Previous
Database & migrations