Skip to content

Shop-scoped endpoints

Almost every resource in ShopVirge belongs to a specific shop. Most of those endpoints are implemented in server/api/endpoints/shop_endpoints/ and mounted under the /shops/{shop_id}/... prefix when registered in server/api/api.py.

The pattern

/shops/{shop_id}/<resource>
/shops/{shop_id}/<resource>/{id}
/shops/{shop_id}/<resource>/<sub-resource>/{id}

Every handler that accepts a shop_id path parameter is gated by an auth dependency (auth_required or auth_required_any). Which shops a caller may access is determined by their Cognito group membership — see Authentication.

CRUDs for shop-owned resources use the shop-aware helpers on CRUDBase:

  • get_id_by_shop_id(id, shop_id) — returns 404 if the row exists but belongs to a different shop.
  • get_multi_by_shop_id(shop_id, ...) — list with filters/sort/pagination scoped to the shop.
  • create_by_shop_id(shop_id, obj_in) — write with automatic shop linkage.
  • delete_by_shop_id(shop_id, id) — scoped delete.

Resource list

The files under server/api/endpoints/shop_endpoints/:

File Resource
orders.py Orders — checkout-facing order creation and status management. Implemented in shop_endpoints/, but mounted at /orders instead of /shops/{shop_id}/orders. PATCH /{order_id} transitions an order to complete or cancelled: triggers stock deduction (if enabled), a Discord webhook notification, and an order confirmation email.
products.py Products (public router split out for unauthenticated catalog browsing). When the shop config toggle force_unique_product_names is enabled, POST and PUT reject duplicate main_name values with HTTP 409. Each product also carries a short_id (12-char UUID prefix) and an optional sku for stable, collision-proof PDP URLs.
categories.py Categories (public router split out similarly).
tags.py Tags.
attributes.py Attributes (e.g. "size"). GET /{category_id}/available-attributes returns option counts for a category; pass ?option_id=<uuid> (repeatable) to filter counts to products matching all selected options (AND logic).
attribute_options.py Attribute options (e.g. "Small").
product_attribute_values.py Per-product attribute assignments.
products_to_tags.py Product ↔ tag links.
prices.py Price management.
accounts.py Shop-level customer/vendor accounts.
stripe.py Stripe integration for one-time PaymentIntents and subscription create/cancel.
api_keys.py Per-shop API key management (mint, list, revoke). Keys are accepted on MCP-exposed routes via auth_required_any.
category_images.py Category image uploads.
images.py Generic shop image uploads.
product_images.py Per-product image uploads.
info_request.py Incoming info requests. The file also exposes the public POST /info-request/form endpoint, which uses pydantic-forms; see Forms.
shipping.py Shipping cost calculation.

Public sub-routers

Some resources expose a dedicated public router for unauthenticated reads (products, categories), so a storefront can render a catalogue without a session. These are mounted alongside the primary router in server/api/api.py.