Update 8 documentation files to reflect new URL scheme:
- Dev: /platforms/{code}/storefront/{store_code}/
- Prod: subdomain.platform.lu/ (root path = storefront)
- Rename DEFAULT_PLATFORM_CODE to MAIN_PLATFORM_CODE
- Replace hardcoded platform_id=1 with dynamic values
- Update route examples, middleware descriptions, code samples
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
19 KiB
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:
- Browser makes API call from storefront page (e.g.,
/platforms/oms/storefront/ORION/products) - Browser automatically sends Referer header:
http://localhost:8000/platforms/oms/storefront/ORION/products - Middleware extracts store from Referer path/subdomain/domain
- Middleware sets
request.state.store = <Store: ORION> - API endpoint accesses store:
store = request.state.store - No store_id needed in URL!
Supported Store Detection Methods
- Path-based (dev):
/platforms/oms/storefront/ORION/products→ extractsORION - Subdomain:
orion.platform.com→ extractsorion - 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:
Authorization: Bearer <customer_token>
Or use the HTTP-only cookie (automatically sent by browser):
Cookie: customer_token=<jwt_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:
GET /api/v1/storefront/products?skip=0&limit=20&is_featured=true
Referer: http://localhost:8000/stores/orion/shop/products
Response (200 OK):
{
"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:
GET /api/v1/storefront/products/1
Referer: http://localhost:8000/stores/orion/shop/products
Response (200 OK):
{
"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 active404 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:
GET /api/v1/storefront/cart/session-abc-123
Referer: http://localhost:8000/stores/orion/shop/cart
Response (200 OK):
{
"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:
{
"product_id": 1,
"quantity": 2
}
Response (200 OK): CartOperationResponse
{
"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{ "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{ "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{ "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_priceif available, otherwiseprice) - 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:
{
"quantity": 3
}
Response (200 OK): CartOperationResponse
{
"message": "Cart updated",
"product_id": 1,
"quantity": 3
}
Error Responses:
-
404 Not Found- Cart item not found{ "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
{
"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
{
"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:
{
"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):
{
"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):
{
"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):
{
"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:
{
"email": "customer@example.com",
"password": "SecurePass123!",
"first_name": "John",
"last_name": "Doe",
"phone": "+1234567890"
}
Response (201 Created):
{
"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 registered422 Unprocessable Entity- Validation errors
Customer Login
Authenticate a customer and receive JWT token.
Endpoint: POST /api/v1/storefront/auth/login
Request Body:
{
"email_or_username": "customer@example.com",
"password": "SecurePass123!"
}
Response (200 OK):
{
"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:
Set-Cookie: customer_token=<jwt>; Path=/storefront; HttpOnly; SameSite=Lax; Secure
Error Responses:
401 Unauthorized- Invalid credentials404 Not Found- Customer not found for this store
Customer Logout
Clear customer session.
Endpoint: POST /api/v1/storefront/auth/logout
Response (200 OK):
{
"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):
[
{
"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):
{
"slug": "about",
"title": "About Us",
"content": "<p>Welcome to our store...</p>",
"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
{
"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:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1700000000
Migration from Old API
Old Pattern (Deprecated):
GET /api/v1/platform/stores/{store_id}/products
POST /api/v1/platform/stores/auth/{store_id}/customers/login
New Pattern (Current):
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
Examples
Complete Add to Cart Flow
// 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
// 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
- ReDoc: http://localhost:8000/redoc
- OpenAPI Spec: http://localhost:8000/openapi.json
Questions? See the API Migration Status or Storefront Architecture Guide.