# Storefront API Reference **Last Updated:** 2026-01-29 **API Version:** v1 **Base Path:** `/api/v1/storefront` --- ## Overview The Storefront API provides customer-facing endpoints for browsing products, managing cart, placing orders, and customer authentication. All endpoints use **middleware-based store context** - no store ID in URLs! ### Key Features ✅ **Automatic Store Detection** - Store extracted from Referer header via middleware ✅ **Multi-Tenant** - Each store has isolated customer data ✅ **Session-Based Cart** - No authentication required for browsing/cart ✅ **Secure Authentication** - JWT tokens with HTTP-only cookies (path=/storefront) ✅ **RESTful Design** - Standard HTTP methods and status codes --- ## How Store Context Works All Storefront API endpoints automatically receive store context from the `StoreContextMiddleware`: 1. **Browser makes API call** from storefront page (e.g., `/stores/orion/storefront/products`) 2. **Browser automatically sends Referer header**: `http://localhost:8000/stores/orion/storefront/products` 3. **Middleware extracts store** from Referer path/subdomain/domain 4. **Middleware sets** `request.state.store = ` 5. **API endpoint accesses store**: `store = request.state.store` 6. **No store_id needed in URL!** ### Supported Store Detection Methods - **Path-based**: `/stores/orion/storefront/products` → extracts `orion` - **Subdomain**: `orion.platform.com` → extracts `orion` - **Custom domain**: `customshop.com` → looks up store by domain --- ## Authentication ### Public Endpoints (No Auth Required) - Product catalog - Product details - Product search - Cart operations - CMS content pages ### Authenticated Endpoints (Customer Token Required) - Place order - Order history - Order details ### Authentication Headers For authenticated requests, include the JWT token: ```http Authorization: Bearer ``` Or use the HTTP-only cookie (automatically sent by browser): ```http Cookie: customer_token= ``` --- ## Products ### Get Product Catalog Get paginated list of products for current store. **Endpoint:** `GET /api/v1/storefront/products` **Query Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `skip` | integer | 0 | Number of products to skip (pagination) | | `limit` | integer | 100 | Maximum products to return (max 1000) | | `search` | string | null | Search products by name/description | | `is_featured` | boolean | null | Filter by featured products only | **Request Example:** ```http GET /api/v1/storefront/products?skip=0&limit=20&is_featured=true Referer: http://localhost:8000/stores/orion/shop/products ``` **Response (200 OK):** ```json { "products": [ { "id": 1, "store_id": 1, "product_id": "PROD-001", "price": 29.99, "sale_price": null, "currency": "EUR", "availability": "in stock", "is_featured": true, "is_active": true, "marketplace_product": { "title": "Sample Product", "description": "Product description...", "image_link": "https://example.com/image.jpg", "brand": "Brand Name" } } ], "total": 50, "skip": 0, "limit": 20 } ``` --- ### Get Product Details Get detailed information for a specific product. **Endpoint:** `GET /api/v1/storefront/products/{product_id}` **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `product_id` | integer | Product ID | **Request Example:** ```http GET /api/v1/storefront/products/1 Referer: http://localhost:8000/stores/orion/shop/products ``` **Response (200 OK):** ```json { "id": 1, "store_id": 1, "product_id": "PROD-001", "price": 29.99, "sale_price": 24.99, "currency": "EUR", "availability": "in stock", "condition": "new", "is_featured": true, "is_active": true, "min_quantity": 1, "max_quantity": 10, "total_inventory": 100, "available_inventory": 95, "marketplace_product": { "title": "Sample Product", "description": "Full product description...", "image_link": "https://example.com/image.jpg", "brand": "Brand Name", "gtin": "1234567890123" } } ``` **Error Responses:** - `404 Not Found` - Product not found or not active - `404 Not Found` - Store not found (missing/invalid Referer) --- ## Shopping Cart Cart operations use session-based tracking (no authentication required). ### Get Cart Retrieve cart contents for a session. **Endpoint:** `GET /api/v1/storefront/cart/{session_id}` **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `session_id` | string | Unique session identifier | **Request Example:** ```http GET /api/v1/storefront/cart/session-abc-123 Referer: http://localhost:8000/stores/orion/shop/cart ``` **Response (200 OK):** ```json { "store_id": 1, "session_id": "session-abc-123", "items": [ { "product_id": 1, "product_name": "Sample Product", "quantity": 2, "price": 29.99, "line_total": 59.98, "image_url": "https://example.com/image.jpg" } ], "subtotal": 59.98, "total": 59.98, "item_count": 1 } ``` **Response Schema:** `CartResponse` | Field | Type | Description | |-------|------|-------------| | `store_id` | integer | Store ID | | `session_id` | string | Shopping session ID | | `items` | array | List of cart items (see CartItemResponse below) | | `subtotal` | float | Subtotal of all items | | `total` | float | Total amount (currently same as subtotal) | | `item_count` | integer | Total number of unique items in cart | **CartItemResponse Schema:** | Field | Type | Description | |-------|------|-------------| | `product_id` | integer | Product ID | | `product_name` | string | Product name | | `quantity` | integer | Quantity in cart | | `price` | float | Price per unit (captured when added to cart) | | `line_total` | float | Total for this line (price × quantity) | | `image_url` | string \| null | Product image URL | --- ### Add to Cart Add a product to the cart. If the product already exists in the cart, the quantity will be incremented. **Endpoint:** `POST /api/v1/storefront/cart/{session_id}/items` **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `session_id` | string | Unique session identifier | **Request Body Schema:** `AddToCartRequest` | Field | Type | Required | Description | |-------|------|----------|-------------| | `product_id` | integer | Yes | Product ID to add (must be > 0) | | `quantity` | integer | No | Quantity to add (default: 1, must be >= 1) | **Request Example:** ```json { "product_id": 1, "quantity": 2 } ``` **Response (200 OK):** `CartOperationResponse` ```json { "message": "Product added to cart", "product_id": 1, "quantity": 2 } ``` **Response Schema:** | Field | Type | Description | |-------|------|-------------| | `message` | string | Operation result message | | `product_id` | integer | Product ID affected | | `quantity` | integer | New quantity in cart | **Error Responses:** - `404 Not Found` - Product not found or store not found ```json { "error_code": "PRODUCT_NOT_FOUND", "message": "Product with ID '123' not found in store 1 catalog", "status_code": 404, "details": { "product_id": 123, "store_id": 1 } } ``` - `400 Bad Request` - Insufficient inventory ```json { "error_code": "INSUFFICIENT_INVENTORY_FOR_CART", "message": "Insufficient inventory for product 'Sample Product'. Requested: 10, Available: 5", "status_code": 400, "details": { "product_id": 1, "product_name": "Sample Product", "requested_quantity": 10, "available_quantity": 5 } } ``` - `422 Unprocessable Entity` - Validation error ```json { "error_code": "INVALID_CART_QUANTITY", "message": "Quantity must be at least 1", "status_code": 422, "details": { "field": "quantity", "quantity": 0, "min_quantity": 1 } } ``` **Implementation Notes:** - Cart items are stored in the database with persistent storage - Price is captured at time of adding to cart (uses `sale_price` if available, otherwise `price`) - If product already exists in cart, quantity is incremented (duplicate prevention) - Inventory is validated before adding items --- ### Update Cart Item Update the quantity of an existing item in the cart. **Endpoint:** `PUT /api/v1/storefront/cart/{session_id}/items/{product_id}` **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `session_id` | string | Unique session identifier | | `product_id` | integer | Product ID to update (must be > 0) | **Request Body Schema:** `UpdateCartItemRequest` | Field | Type | Required | Description | |-------|------|----------|-------------| | `quantity` | integer | Yes | New quantity (must be >= 1) | **Request Example:** ```json { "quantity": 3 } ``` **Response (200 OK):** `CartOperationResponse` ```json { "message": "Cart updated", "product_id": 1, "quantity": 3 } ``` **Error Responses:** - `404 Not Found` - Cart item not found ```json { "error_code": "CART_ITEM_NOT_FOUND", "message": "Product 123 not found in cart", "status_code": 404, "details": { "product_id": 123, "session_id": "session-abc-123" } } ``` - `400 Bad Request` - Insufficient inventory for new quantity - `422 Unprocessable Entity` - Invalid quantity (less than 1) --- ### Remove from Cart Remove a specific item from the cart. **Endpoint:** `DELETE /api/v1/storefront/cart/{session_id}/items/{product_id}` **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `session_id` | string | Unique session identifier | | `product_id` | integer | Product ID to remove (must be > 0) | **Response (200 OK):** `CartOperationResponse` ```json { "message": "Item removed from cart", "product_id": 1 } ``` **Error Responses:** - `404 Not Found` - Cart item not found (product not in cart) --- ### Clear Cart Remove all items from the cart. **Endpoint:** `DELETE /api/v1/storefront/cart/{session_id}` **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `session_id` | string | Unique session identifier | **Response (200 OK):** `ClearCartResponse` ```json { "message": "Cart cleared", "items_removed": 3 } ``` **Response Schema:** | Field | Type | Description | |-------|------|-------------| | `message` | string | Operation result message | | `items_removed` | integer | Number of items that were removed from cart | --- ## Orders Order endpoints require customer authentication. ### Place Order Create a new order (authenticated). **Endpoint:** `POST /api/v1/storefront/orders` **Authentication:** Required (customer token) **Request Body:** ```json { "session_id": "session-abc-123", "shipping_address": { "street": "123 Main St", "city": "City", "postal_code": "12345", "country": "US" }, "billing_address": { "street": "123 Main St", "city": "City", "postal_code": "12345", "country": "US" }, "payment_method": "stripe", "notes": "Please deliver before 5pm" } ``` **Response (201 Created):** ```json { "id": 123, "order_number": "ORD-2025-001", "store_id": 1, "customer_id": 456, "total": 59.98, "status": "pending", "created_at": "2025-11-22T10:00:00Z", "items": [...], "shipping_address": {...}, "billing_address": {...} } ``` --- ### Get Order History Get list of customer's orders (authenticated). **Endpoint:** `GET /api/v1/storefront/orders` **Authentication:** Required **Query Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `skip` | integer | 0 | Pagination offset | | `limit` | integer | 20 | Max orders to return | **Response (200 OK):** ```json { "orders": [ { "id": 123, "order_number": "ORD-2025-001", "total": 59.98, "status": "completed", "created_at": "2025-11-22T10:00:00Z" } ], "total": 5, "skip": 0, "limit": 20 } ``` --- ### Get Order Details Get details of a specific order (authenticated). **Endpoint:** `GET /api/v1/storefront/orders/{order_id}` **Authentication:** Required **Response (200 OK):** ```json { "id": 123, "order_number": "ORD-2025-001", "store_id": 1, "customer_id": 456, "total": 59.98, "status": "completed", "created_at": "2025-11-22T10:00:00Z", "items": [...], "shipping_address": {...}, "billing_address": {...}, "tracking_number": "TRACK-123" } ``` **Error Responses:** - `404 Not Found` - Order not found or doesn't belong to customer --- ## Authentication ### Register Customer Create a new customer account. **Endpoint:** `POST /api/v1/storefront/auth/register` **Request Body:** ```json { "email": "customer@example.com", "password": "SecurePass123!", "first_name": "John", "last_name": "Doe", "phone": "+1234567890" } ``` **Response (201 Created):** ```json { "id": 456, "email": "customer@example.com", "first_name": "John", "last_name": "Doe", "phone": "+1234567890", "is_active": true, "store_id": 1 } ``` **Error Responses:** - `400 Bad Request` - Email already registered - `422 Unprocessable Entity` - Validation errors --- ### Customer Login Authenticate a customer and receive JWT token. **Endpoint:** `POST /api/v1/storefront/auth/login` **Request Body:** ```json { "email_or_username": "customer@example.com", "password": "SecurePass123!" } ``` **Response (200 OK):** ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "bearer", "expires_in": 86400, "user": { "id": 456, "email": "customer@example.com", "first_name": "John", "last_name": "Doe" } } ``` **Cookie Set:** The endpoint also sets an HTTP-only cookie: ```http Set-Cookie: customer_token=; Path=/storefront; HttpOnly; SameSite=Lax; Secure ``` **Error Responses:** - `401 Unauthorized` - Invalid credentials - `404 Not Found` - Customer not found for this store --- ### Customer Logout Clear customer session. **Endpoint:** `POST /api/v1/storefront/auth/logout` **Response (200 OK):** ```json { "message": "Logged out successfully" } ``` The endpoint clears the `customer_token` cookie. --- ## Content Pages (CMS) ### Get Navigation Links Get CMS pages for header/footer navigation. **Endpoint:** `GET /api/v1/storefront/content-pages/navigation` **Response (200 OK):** ```json [ { "slug": "about", "title": "About Us", "show_in_header": true, "show_in_footer": true, "display_order": 1 }, { "slug": "contact", "title": "Contact Us", "show_in_header": true, "show_in_footer": true, "display_order": 2 } ] ``` --- ### Get Content Page Get full content for a specific page. **Endpoint:** `GET /api/v1/storefront/content-pages/{slug}` **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `slug` | string | Page slug (e.g., "about", "faq") | **Response (200 OK):** ```json { "slug": "about", "title": "About Us", "content": "

Welcome to our store...

", "meta_description": "Learn about our story", "is_published": true, "show_in_header": true, "show_in_footer": true, "display_order": 1 } ``` **Error Responses:** - `404 Not Found` - Page not found --- ## Error Handling All endpoints follow standard HTTP error responses: ### Common Error Response Format ```json { "error_code": "VALIDATION_ERROR", "message": "Request validation failed", "status_code": 422, "details": { "validation_errors": [ { "loc": ["body", "email"], "msg": "Invalid email format", "type": "value_error.email" } ] } } ``` ### HTTP Status Codes | Code | Meaning | When Used | |------|---------|-----------| | 200 | OK | Successful GET/PUT/DELETE | | 201 | Created | Successful POST (resource created) | | 400 | Bad Request | Invalid request data | | 401 | Unauthorized | Authentication required/failed | | 403 | Forbidden | Insufficient permissions | | 404 | Not Found | Resource not found (product, store, order) | | 422 | Unprocessable Entity | Validation errors | | 500 | Internal Server Error | Server error | --- ## Rate Limiting All Storefront API endpoints are rate limited: - **Public endpoints**: 100 requests/minute per IP - **Authenticated endpoints**: 200 requests/minute per customer - **Search endpoints**: 20 requests/minute per IP Rate limit headers included in responses: ```http X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1700000000 ``` --- ## Migration from Old API **Old Pattern (Deprecated):** ```http GET /api/v1/platform/stores/{store_id}/products POST /api/v1/platform/stores/auth/{store_id}/customers/login ``` **New Pattern (Current):** ```http GET /api/v1/storefront/products POST /api/v1/storefront/auth/login ``` **Key Changes:** - ✅ Removed `{store_id}` from URLs - ✅ Store extracted from Referer header automatically - ✅ Cleaner URLs (~40% shorter) - ✅ Same functionality, better architecture **See:** [API Migration Status](../architecture/api-migration-status.md) --- ## Examples ### Complete Add to Cart Flow ```javascript // 1. Get session ID (generate or retrieve from localStorage) const sessionId = localStorage.getItem('session_id') || crypto.randomUUID(); localStorage.setItem('session_id', sessionId); // 2. Add product to cart const response = await fetch(`/api/v1/storefront/cart/${sessionId}/items`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ product_id: 1, quantity: 2 }) }); const result = await response.json(); console.log('Cart updated:', result.cart); ``` ### Complete Checkout Flow ```javascript // 1. Customer logs in const loginResponse = await fetch('/api/v1/storefront/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email_or_username: 'customer@example.com', password: 'password123' }) }); const { access_token } = await loginResponse.json(); localStorage.setItem('customer_token', access_token); // 2. Place order const orderResponse = await fetch('/api/v1/storefront/orders', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${access_token}` }, body: JSON.stringify({ session_id: sessionId, shipping_address: {...}, billing_address: {...}, payment_method: 'stripe' }) }); const order = await orderResponse.json(); console.log('Order created:', order.order_number); ``` --- ## Interactive Documentation For live API testing and exploration: - **Swagger UI**: [http://localhost:8000/docs](http://localhost:8000/docs) - **ReDoc**: [http://localhost:8000/redoc](http://localhost:8000/redoc) - **OpenAPI Spec**: [http://localhost:8000/openapi.json](http://localhost:8000/openapi.json) --- **Questions?** See the [API Migration Status](../architecture/api-migration-status.md) or [Storefront Architecture Guide](../frontend/storefront/architecture.md).