{"openapi":"3.1.0","info":{"title":"Stockpilot API","description":"\n# Stockpilot API Documentation\n\nREST API for multi-channel e-commerce operations.\n\n## Authentication\n\nAll API endpoints require authentication via API credentials.\n\n### Authentication Method\n- **Type**: Custom API Key Authentication  \n- **Headers Required**:\n  - `X-CLIENT-ID`: Your client ID\n  - `X-CLIENT-SECRET`: Your client secret\n\n### Error Responses\n- **401 Unauthorized**: Invalid or missing credentials\n\n## API Overview\n\n### Core Features\n- **Inventory Management** - CRUD operations with location and threshold management\n- **Order Processing** - Complete order lifecycle from creation to fulfillment\n- **Returns Management** - Paginated returns with status and channel filtering\n- **Product Management** - Product catalog and image management\n- **Sales Analytics** - Comprehensive sales reporting and forecasting\n- **Channel Integrations** - Multi-platform sales channel management\n- **Shipping Operations** - Label generation and fulfillment tracking\n- **Purchase Orders** - Supplier management and procurement workflows\n- **Webhooks** - Outbound event delivery with signed payloads\n\n## Data Formats\n\n### Pagination\nMost list endpoints use page-based pagination:\n- **Default page size**: 100 items\n- **Max page size**: 100 items\n- **Query parameters**: `page` (default: 1), `page_size` (default: 100)\n\n### Date Formats\n- **Date fields**: ISO format (YYYY-MM-DD)\n- **DateTime fields**: ISO format with timezone (YYYY-MM-DDTHH:MM:SSZ)\n\n### Location Format\nBin locations use hierarchical format:\n- `\"A1-001-01\"` - Path A1, Rack 001, Shelf 01\n- `\"B2-003-05-02\"` - Path B2, Rack 003, Shelf 05, Bin 02\n\n### Threshold Format\nStock thresholds support multiple formats:\n- `\"5u\"` - Alert when stock drops below 5 units\n- `\"33w\"` - Alert when stock estimated to run out in 33 weeks\n\n## Product Identification\n\nMultiple endpoints support flexible product lookup:\n- **id**: Database primary key (integer)\n- **sku**: Stock Keeping Unit (string)\n- **barcode**: Product barcode/EAN (string)\n\n**Note**: Exactly one identifier must be provided per request.\n\n## Error Handling\n\n### Standard HTTP Status Codes\n- **200**: Success\n- **201**: Created\n- **400**: Bad Request (validation error)\n- **401**: Unauthorized (authentication failed)\n- **404**: Not Found\n- **500**: Internal Server Error\n\n### Error Response Format\n```json\n{\n  \"detail\": \"Error description\",\n  \"error\": \"Error message\"\n}\n```\n\n## Rate Limiting\n\nLimits are applied **per API key** (per `X-CLIENT-ID` / `X-CLIENT-SECRET` pair) in fixed\n60 second windows:\n\n- **Single-resource reads** (`/inventory/get`, `/orders/get-single`): 300 requests per minute\n- **List endpoints** (`/inventory`, `/orders`, `/products`): 120 requests per minute\n- **Create/update/delete operations**: 60 requests per minute\n- **Heavy operations** (analytics, label generation, channel sync, invoice send): 20 requests per minute\n\n### Rate Limit Headers\nEvery response reports your position in the current window:\n- `X-RateLimit-Limit`: Requests allowed per window\n- `X-RateLimit-Remaining`: Requests remaining in current window\n- `X-RateLimit-Reset`: Unix timestamp (UTC seconds) when the window resets\n- `X-RateLimit-Policy`: Which bucket the request was counted against\n\n**These limits are advisory today** - no request is rejected with `429`. The headers let\nyou pace an integration ahead of enforcement, which will be announced in advance.\n\n### Idempotency\nThere is no `Idempotency-Key` header. `GET`s and absolute-value updates are safe to\nretry; creates are not. See **Rate Limits & Idempotency** for the full table.\n\n### Additional Considerations\n- External API rate limits (Shopify, bol.com, etc.) may further restrict operations\n- Bulk operations and large datasets may have additional performance considerations\n- Background task processing respects queue capacity limits\n\n## Async Operations\nSeveral endpoints use background processing for:\n- Purchase order recommendations\n- Shipping label generation\n- Analytics calculations\n- Inventory synchronization\n\nUse provided task IDs to monitor completion status.\n\n## Field Organization\n\n### Inventory Items\nResponses organized in logical groups:\n- **Product Identification**: id, product_id, item_name, sku, barcode, image\n- **Location & Threshold**: bin_location, threshold, stock_threshold\n- **Product Details**: condition, weight, dimensions, hs_code, country_origin, vat_class\n- **Pricing**: purchase_price, wholesale_price, base_price, retail_price\n- **Stock Management**: quantity, moq, reserved_quantity, incoming_quantity, backorder_amount\n- **Status & Timestamps**: is_active, created_at, updated_at\n\n### Order Management\n- **Order Status**: open, pending, on-hold, completed, cancelled\n- **Order Deletion**: Remove individual items or entire orders with inventory book-back\n- **Fulfillment**: shipping carriers, tracking codes, delivery confirmations\n- **Customer Details**: shipping addresses, contact information, special instructions\n\n### Returns\n- **Return Status**: `REQUESTED` (nothing handled), `PARTLY_ACCEPTED` (some lines handled), `RETURN_ACCEPTED` (fully handled)\n- **Status Filtering**: filter values are lowercase - `requested`, `partly_accepted`, `accepted` (maps to `RETURN_ACCEPTED`)\n- **Channel Filtering**: `handle` and `channel_id` must be supplied together\n- **Pagination**: `next`/`previous` are booleans - use `current_page` and `total_pages` to page through results\n\n### Analytics Metrics\n- **Sales Data**: total_orders, total_items, revenue\n- **Forecasting**: daily, weekly, monthly projections\n- **Pricing**: average selling price, price ranges\n- **Channel Breakdown**: per-platform sales analysis\n\n## Integration Examples\n\n### Basic Authentication\n```python\nheaders = {\n    \"X-CLIENT-ID\": \"your_client_id\",\n    \"X-CLIENT-SECRET\": \"your_client_secret\"\n}\n```\n\n### Pagination\n```python\nparams = {\n    \"page\": 1,\n    \"page_size\": 100\n}\n```\n\n### Product Lookup\n```python\n# By ID\nparams = {\"id\": 123}\n# By SKU  \nparams = {\"sku\": \"PROD-123\"}\n# By Barcode\nparams = {\"barcode\": \"1234567890\"}\n```\n\nVisit `/redoc` for detailed interactive documentation.\n    ","version":"1.1.0"},"paths":{"/auth/who-is":{"get":{"tags":["Authentication"],"summary":"Verify API credentials","description":"Verify the provided API credentials and return organization details and feature flags.\n\nThis endpoint can be used to test if your API credentials are valid and retrieve \norganization information and enabled features.\n\n## cURL Example:\n```bash\ncurl -X GET \"https://api.stockpilot.dev/auth/who-is\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n```\n\n## Response Example:\n```json\n{\n  \"id\": 123,\n  \"organization_name\": \"Acme Corp\",\n  \"unique_id\": \"acme-corp-uuid\",\n  \"features\": {\n    \"best_before_alerts\": true,\n    \"purchase_order_management\": true,\n    \"warehouse_management\": false,\n    \"create_picking_batch\": true,\n    \"api_access\": true,\n    \"b2b_portal\": false,\n    \"email_campaign\": true,\n    \"product_feed\": false,\n    \"accounting_connector\": true,\n    \"odoo_connector\": false\n  }\n}\n```","operationId":"who_is_auth_who_is_get","parameters":[{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Successful authentication","content":{"application/json":{"schema":{},"example":{"id":123,"organization_name":"Acme Corp","unique_id":"acme-corp-uuid","features":{"best_before_alerts":true,"purchase_order_management":true,"warehouse_management":false,"create_picking_batch":true,"api_access":true,"b2b_portal":false,"email_campaign":true,"product_feed":false,"accounting_connector":true,"odoo_connector":false}}}}},"401":{"description":"Missing API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"403":{"description":"Invalid credentials","content":{"application/json":{"example":{"error":"Invalid credentials"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/customers":{"get":{"tags":["Customers"],"summary":"List customers","description":"Retrieve a list of all customers with their complete details.\n\n## cURL Example:\n```bash\ncurl -X GET \"https://api.stockpilot.dev/customers\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n```\n\n## Response Example:\n```json\n{\n  \"success\": true,\n  \"customers\": [\n    {\n      \"id\": 123,\n      \"customer_code\": \"CUST-001\",\n      \"business_name\": \"Acme Corp\",\n      \"invoice_email\": \"billing@acme.com\",\n      \"orders_email\": \"orders@acme.com\",\n      \"phone_number\": \"+31 20 123 4567\",\n      \"website_url\": \"https://acme.com\",\n      \"invoice_street\": \"123 Business St\",\n      \"invoice_city\": \"Amsterdam\",\n      \"invoice_country\": \"NL\",\n      \"vat_number\": \"NL123456789B01\",\n      \"payment_terms\": \"30\"\n    }\n  ],\n  \"count\": 1\n}\n```\n\n## Returns\nA JSON object containing:\n* **success**: Boolean indicating if the request was successful\n* **customers**: List of customer objects with all their details\n* **count**: Total number of customers\n\nEach customer object includes:\n* **Basic info**: id, customer_code, business_name\n* **Contact info**: invoice_email, orders_email, phone_number, website_url\n* **Invoice address**: invoice_street, invoice_house_num, invoice_suffix, invoice_zip, invoice_city, invoice_country\n* **Shipping address**: shipping_name, shipping_street, shipping_house_num, shipping_suffix, shipping_zip, shipping_city, shipping_country\n* **Financial info**: vat_number, bank_name, bank_number, payment_terms\n* **Notes**: special_notes","operationId":"list_customers_customers_get","parameters":[{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Customers list retrieved successfully","content":{"application/json":{"schema":{},"example":{"success":true,"customers":[{"id":123,"customer_code":"CUST-001","business_name":"Acme Corp","invoice_email":"billing@acme.com","orders_email":"orders@acme.com","phone_number":"+31 20 123 4567","website_url":"https://acme.com","invoice_street":"123 Business St","invoice_house_num":"123","invoice_suffix":"A","invoice_zip":"1000AA","invoice_city":"Amsterdam","invoice_country":"NL","shipping_name":"Acme Warehouse","shipping_street":"456 Warehouse Rd","shipping_house_num":"456","shipping_suffix":"","shipping_zip":"3000BB","shipping_city":"Rotterdam","shipping_country":"NL","vat_number":"NL123456789B01","bank_name":"ING Bank","bank_number":"NL91ABNA0417164300","payment_terms":"30","special_notes":"Rush orders only"}],"count":1}}}},"401":{"description":"Missing API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream error: Connection failed"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/customers/{customer_id}/update":{"patch":{"tags":["Customers"],"summary":"Update customer details","description":"Update customer details by customer ID.\n\n## cURL Example:\n```bash\ncurl -X PUT \"https://api.stockpilot.dev/customers/123/update\"          -H \"X-CLIENT-ID: your_client_id\"          -H \"X-CLIENT-SECRET: your_client_secret\"          -H \"Content-Type: application/json\"          -d '{\n       \"business_name\": \"Updated Corp Name\",\n       \"invoice_email\": \"new-billing@acme.com\",\n       \"payment_status\": \"good\",\n       \"payment_terms\": 14\n     }'\n```\n\n## Request Body\nAll fields are optional - only provide the fields you want to update:\n\n### Available Fields:\n* **business_name**: Business name\n* **invoice_email**: Invoice email address\n* **orders_email**: Orders email address\n* **phone_number**: Phone number\n* **payment_status**: Payment status (must be: good, overdue, blocked)\n* **payment_terms**: Payment terms in days\n* **vat_number**: VAT number\n* **special_notes**: Special notes about the customer\n\n## Response Example:\n```json\n{\n  \"success\": true,\n  \"message\": \"Customer updated successfully\",\n  \"customer\": {\n    \"id\": 123,\n    \"business_name\": \"Updated Corp Name\",\n    \"invoice_email\": \"new-billing@acme.com\",\n    \"orders_email\": \"new-orders@acme.com\",\n    \"phone_number\": \"+31 20 999 8888\",\n    \"payment_status\": \"good\",\n    \"payment_terms\": 14,\n    \"vat_number\": \"NL987654321B01\",\n    \"special_notes\": \"Updated customer notes\"\n  }\n}\n```\n\n## Returns\nA JSON object containing:\n* **success**: Boolean indicating if the request was successful\n* **message**: Success message\n* **customer**: Updated customer object with the modified fields\n\n## Notes\n* Only the fields provided in the request body will be updated\n* Customer ID must exist in the system\n* Payment status validation: only accepts 'good', 'overdue', or 'blocked'\n* All other customer fields (addresses, bank details, etc.) remain unchanged","operationId":"update_customer_customers__customer_id__update_patch","parameters":[{"name":"customer_id","in":"path","required":true,"schema":{"type":"integer","title":"Customer Id"}},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCustomerPayload"}}}},"responses":{"200":{"description":"Customer updated successfully","content":{"application/json":{"schema":{},"example":{"success":true,"message":"Customer updated successfully","customer":{"id":123,"business_name":"Updated Corp Name","invoice_email":"new-billing@acme.com","orders_email":"new-orders@acme.com","phone_number":"+31 20 999 8888","payment_status":"good","payment_terms":14,"vat_number":"NL987654321B01","special_notes":"Updated customer notes"}}}}},"400":{"description":"Invalid request data","content":{"application/json":{"example":{"detail":"Invalid payment status. Must be: good, overdue, or blocked"}}}},"401":{"description":"Missing API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"404":{"description":"Customer not found","content":{"application/json":{"example":{"detail":"Customer with ID 123 not found"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream error: Connection failed"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/brands":{"get":{"tags":["Categories & Brands"],"summary":"List all brands","operationId":"get_brands_brands_get","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"responses":{"200":{"description":"Returns a list of brands","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/brands/create":{"post":{"tags":["Categories & Brands"],"summary":"Create a brand","operationId":"create_brand_brands_create_post","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBrandPayload"}}}},"responses":{"200":{"description":"Returns the created brand ID","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/categories":{"get":{"tags":["Categories & Brands"],"summary":"List all categories","operationId":"get_categories_categories_get","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"responses":{"200":{"description":"Returns a list of product categories","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/categories/create":{"post":{"tags":["Categories & Brands"],"summary":"Create a category","operationId":"create_category_categories_create_post","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCategoryPayload"}}}},"responses":{"200":{"description":"Returns the created category ID","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/products":{"get":{"tags":["Products"],"summary":"Get paginated list of products","description":"Returns a paginated list of products (Parent objects).\n\n## Query Parameters:\n- **page**: Page number for pagination (starts at 1)\n- **page_size**: Number of items per page (max 100)\n\n## cURL Example:\n```bash\ncurl -X GET \"https://api.stockpilot.dev/products?page=1&page_size=50\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n```\n\n## Response Example:\n```json\n{\n  \"count\": 50,\n  \"next\": \"https://api.stockpilot.dev/products?page=2\",\n  \"previous\": null,\n  \"results\": [\n    {\n      \"id\": 123,\n      \"title\": \"Premium Wireless Headphones\",\n      \"description\": \"High-quality wireless headphones\",\n      \"brand\": 1,\n      \"brand_name\": \"TechBrand\",\n      \"category\": 2,\n      \"category_name\": \"Electronics\",\n      \"is_active\": true,\n      \"image_url\": \"https://example.com/product.jpg\"\n    }\n  ]\n}\n```","operationId":"get_product_list_products_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page","default":100,"title":"Page Size"},"description":"Items per page"},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Products list retrieved successfully","content":{"application/json":{"schema":{},"example":{"count":50,"next":"https://api.stockpilot.dev/products?page=2","results":[{"id":123,"title":"Premium Wireless Headphones","description":"High-quality wireless headphones with noise cancellation","brand":1,"brand_name":"TechBrand","category":2,"category_name":"Electronics","is_active":true,"image_url":"https://example.com/product.jpg","created_at":"2023-01-15T10:30:00Z","updated_at":"2023-01-20T14:45:00Z"}]}}}},"401":{"description":"Missing API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream error: Connection failed"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/products/get":{"get":{"tags":["Products"],"summary":"Get product by ID","description":"Retrieve a single product (Parent) by its internal ID.\n\n## Query Parameters:\n- **id**: Internal product ID (required)\n\n## cURL Example:\n```bash\ncurl -X GET \"https://api.stockpilot.dev/products/get?id=123\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n```\n\n## Response Example:\n```json\n{\n  \"id\": 123,\n  \"title\": \"Premium Wireless Headphones\",\n  \"description\": \"High-quality wireless headphones\",\n  \"brand\": 1,\n  \"brand_name\": \"TechBrand\",\n  \"category\": 2,\n  \"category_name\": \"Electronics\",\n  \"is_active\": true,\n  \"image_url\": \"https://example.com/product.jpg\"\n}\n```","operationId":"get_product_products_get_get","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"integer","description":"Internal product ID","title":"Id"},"description":"Internal product ID"},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Product found","content":{"application/json":{"schema":{},"example":{"id":123,"title":"Premium Wireless Headphones","description":"High-quality wireless headphones with noise cancellation","brand":1,"brand_name":"TechBrand","category":2,"category_name":"Electronics","is_active":true,"image_url":"https://example.com/product.jpg","created_at":"2023-01-15T10:30:00Z","updated_at":"2023-01-20T14:45:00Z"}}}},"400":{"description":"Missing product ID","content":{"application/json":{"example":{"detail":"Missing product ID"}}}},"404":{"description":"Product not found","content":{"application/json":{"example":{"detail":"Product not found."}}}},"401":{"description":"Missing API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream error: Connection failed"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/products/{product_id}/set-image":{"post":{"tags":["Products"],"summary":"Set image for a product","description":"Set the image of a product (parent) via direct upload or remote URL.\n\nYou can:\n- Upload an image file via `multipart/form-data`\n- Provide an `image_url` as JSON\n\nOnly one method should be used per request.","operationId":"set_product_image_products__product_id__set_image_post","parameters":[{"name":"product_id","in":"path","required":true,"schema":{"type":"integer","description":"Product (Parent) ID","title":"Product Id"},"description":"Product (Parent) ID"},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_set_product_image_products__product_id__set_image_post"}}}},"responses":{"200":{"description":"Uploads or fetches the product image","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/products/create":{"post":{"tags":["Products"],"summary":"Create a new product","description":"Create a new product in your organization.\n\n    **Default Behavior for Optional Fields:**\n    - **Brand**: If `brand` is not provided, null, or 0, the system will automatically create or use a default brand named \"Brandless\" for your organization\n    - **Category**: If `category` is not provided, null, or 0, the system will automatically create or use a default category named \"No category\" for your organization\n    - **Description**: Can be left empty if not needed\n    - **is_active**: Defaults to `true` if not specified\n\n    **Examples:**\n    - Minimal payload: `{\"title\": \"My Product\"}` - will use default brand and category\n    - Full payload: `{\"title\": \"My Product\", \"brand\": 123, \"category\": 456, \"description\": \"Great product\", \"is_active\": true}`","operationId":"create_product_products_create_post","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID for authentication","title":"X-Client-Id"},"description":"Your API client ID for authentication"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret for authentication","title":"X-Client-Secret"},"description":"Your API client secret for authentication"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProductPayload"}}}},"responses":{"200":{"description":"Returns the newly created product with ID and title","content":{"application/json":{"schema":{}}}},"201":{"description":"Product created successfully","content":{"application/json":{"example":{"product_id":789,"title":"Premium Wireless Headphones"}}}},"400":{"description":"Validation error","content":{"application/json":{"example":{"title":["This field is required."]}}}},"401":{"description":"Authentication failed - Invalid client ID or secret"},"500":{"description":"Internal server error or upstream service error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/inventory":{"get":{"tags":["Inventory"],"summary":"List inventory items","description":"Retrieve a paginated list of inventory items with location and threshold features.\n\n## Parameters\n* **page**: Page number for pagination (starts at 1)\n* **page_size**: Number of items per page (max 100)\n* **created_at**: Filter by creation date (optional, YYYY-MM-DD format)\n\n## Paging to the end\nPage until `current_page == total_pages`. Requesting a page beyond `total_pages`\nreturns `404`, so read `total_pages` from the first response rather than probing.\n\nNote that `next` and `previous` are **booleans**, not URLs - the DRF convention\nimplies URLs, but this API does not return them. Increment `page` yourself.\n\n## cURL Example:\n```bash\ncurl -X GET \"https://api.stockpilot.dev/inventory?page=1&page_size=50&created_at=2023-01-15\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n```\n\n## Response Example:\n```json\n{\n  \"count\": 2150,\n  \"next\": true,\n  \"previous\": false,\n  \"current_page\": 1,\n  \"total_pages\": 22,\n  \"results\": [\n    {\n      \"id\": 12345,\n      \"product_id\": 456,\n      \"item_name\": \"Premium Wireless Headphones\",\n      \"sku\": \"WH-PREM-001\",\n      \"barcode\": \"5901234123457\",\n      \"barcode_type\": \"EAN\",\n      \"image\": \"https://example.com/image.jpg\",\n      \"bin_location\": [\"A1-001-01\", \"B2-003-05\"],\n      \"threshold\": \"5u\",\n      \"condition\": \"new\",\n      \"hs_code\": \"8518300095\",\n      \"country_origin\": \"CN\",\n      \"weight\": \"0.300\",\n      \"item_length\": \"20.00\",\n      \"item_width\": \"15.00\",\n      \"item_height\": \"8.00\",\n      \"purchase_price\": \"45.00\",\n      \"wholesale_price\": \"75.00\",\n      \"base_price\": \"99.99\",\n      \"retail_price\": \"99.99\",\n      \"vat_class\": \"standard_rate\",\n      \"quantity\": 150,\n      \"moq\": 1,\n      \"stock_threshold\": 5,\n      \"backorder_amount\": 0,\n      \"reserved_quantity\": 3,\n      \"incoming_quantity\": 50,\n      \"is_active\": true,\n      \"created_at\": \"2023-01-15T10:30:00Z\",\n      \"updated_at\": \"2023-01-20T14:45:00Z\"\n    }\n  ]\n}\n```\n\n## Field Groups\nEach inventory item includes fields organized in logical groups:\n- **Product Identification**: id, product_id, item_name, sku, barcode, barcode_type, image\n- **Location & Threshold**: bin_location, threshold, stock_threshold\n- **Product Details**: condition, hs_code, country_origin, weight, dimensions, vat_class\n- **Pricing**: purchase_price, wholesale_price, base_price, retail_price\n- **Stock Management**: quantity, moq, reserved_quantity, incoming_quantity, backorder_amount\n- **Status & Timestamps**: is_active, created_at, updated_at","operationId":"get_inventory_list_inventory_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number for pagination","default":1,"title":"Page"},"description":"Page number for pagination"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Number of items per page","default":100,"title":"Page Size"},"description":"Number of items per page"},{"name":"created_at","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Filter by creation date (YYYY-MM-DD)","title":"Created At"},"description":"Filter by creation date (YYYY-MM-DD)"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Inventory list retrieved successfully","content":{"application/json":{"schema":{},"example":{"results":[{"id":12345,"product_id":456,"item_name":"Premium Wireless Headphones","sku":"WH-PREM-001","barcode":"5901234123457","barcode_type":"EAN","image":"https://example.com/image.jpg","bin_location":["A1-001-01","B2-003-05"],"threshold":"5u","condition":"NEW","quantity":150,"base_price":99.99,"is_active":true}],"count":2150,"next":true,"previous":false,"current_page":1,"total_pages":22}}}},"400":{"description":"Invalid pagination parameter","content":{"application/json":{"example":{"error":"Failed to fetch inventory"}}}},"401":{"description":"Missing API credentials"},"404":{"description":"Requested page is beyond the last page - compare current_page with total_pages before requesting the next one","content":{"application/json":{"example":{"error":"Failed to fetch inventory"}}}},"500":{"description":"Internal server error"},"502":{"description":"Upstream service could not be reached","content":{"application/json":{"example":{"error":"Upstream service error"}}}},"504":{"description":"Upstream service did not respond within 10 seconds","content":{"application/json":{"example":{"error":"Inventory service temporarily unavailable","detail":"Request timed out, please retry"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/inventory/get":{"get":{"tags":["Inventory"],"summary":"Get single inventory item by barcode, SKU, or ID","description":"Fetch a single inventory item by ID, SKU, or barcode with location and threshold features.\n\n## Query Parameters (choose exactly one):\n- **id**: Internal Stockpilot product ID  \n- **sku**: SKU of the product  \n- **barcode**: EAN/barcode of the product\n\nAt least one of the above must be provided.\n\n## cURL Examples:\n```bash\n# Get by SKU\ncurl -X GET \"https://api.stockpilot.dev/inventory/get?sku=WH-PREM-001\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n\n# Get by ID\ncurl -X GET \"https://api.stockpilot.dev/inventory/get?id=12345\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n\n# Get by Barcode\ncurl -X GET \"https://api.stockpilot.dev/inventory/get?barcode=5901234123457\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n```\n\n## Response Example:\n```json\n{\n  \"id\": 12345,\n  \"product_id\": 456,\n  \"item_name\": \"Premium Wireless Headphones\",\n  \"sku\": \"WH-PREM-001\",\n  \"barcode\": \"5901234123457\",\n  \"barcode_type\": \"EAN\",\n  \"image\": \"https://example.com/image.jpg\",\n  \"bin_location\": [\"A1-001-01\", \"B2-003-05\"],\n  \"threshold\": \"5u\",\n  \"condition\": \"new\",\n  \"hs_code\": \"8518300095\",\n  \"country_origin\": \"CN\",\n  \"weight\": \"0.300\",\n  \"item_length\": \"20.00\",\n  \"item_width\": \"15.00\",\n  \"item_height\": \"8.00\",\n  \"purchase_price\": \"45.00\",\n  \"wholesale_price\": \"75.00\",\n  \"base_price\": \"99.99\",\n  \"retail_price\": \"99.99\",\n  \"vat_class\": \"standard_rate\",\n  \"quantity\": 150,\n  \"moq\": 1,\n  \"stock_threshold\": 5,\n  \"backorder_amount\": 0,\n  \"reserved_quantity\": 3,\n  \"incoming_quantity\": 50,\n  \"is_active\": true,\n  \"created_at\": \"2023-01-15T10:30:00Z\",\n  \"updated_at\": \"2023-01-20T14:45:00Z\"\n}\n```\n\n## Response Structure\nReturns a comprehensive inventory item with fields organized in logical groups:\n- **Product Identification**: Basic product info and identifiers\n- **Location & Threshold**: bin location and threshold management\n- **Product Details**: Physical characteristics and classification\n- **Pricing**: All pricing tiers (purchase, wholesale, base, retail)\n- **Stock Management**: Current stock levels and management settings\n- **Status & Timestamps**: Active status and audit timestamps\n\n## Error Responses:\n- **400**: Missing or invalid identifier\n- **404**: Inventory item not found","operationId":"get_inventory_item_inventory_get_get","parameters":[{"name":"id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Internal product ID","title":"Id"},"description":"Internal product ID"},{"name":"sku","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Stock Keeping Unit","title":"Sku"},"description":"Stock Keeping Unit"},{"name":"barcode","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Barcode / EAN","title":"Barcode"},"description":"Barcode / EAN"},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Item retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InventoryItemResponse"}}}},"400":{"description":"Invalid request - missing identifier","content":{"application/json":{"example":{"detail":"At least one of id, sku or barcode must be provided."}}}},"404":{"description":"Item not found","content":{"application/json":{"example":{"detail":"Inventory item not found"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/inventory/{item_id}/set-image":{"post":{"tags":["Inventory"],"summary":"Set image for an inventory item","description":"Set an image for a given inventory item by either:\n\n- Uploading an image file (`image_file`)\n- Providing a remote image URL (`image_url`)\n\nBoth methods are supported, but only one should be used per request.","operationId":"set_inventory_image_inventory__item_id__set_image_post","parameters":[{"name":"item_id","in":"path","required":true,"schema":{"type":"integer","description":"Inventory item ID","title":"Item Id"},"description":"Inventory item ID"},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_set_inventory_image_inventory__item_id__set_image_post"}}}},"responses":{"200":{"description":"Uploads or fetches an image for the given inventory item","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/inventory/update":{"post":{"tags":["Inventory"],"summary":"Update inventory item","description":"Update an inventory item's details with location and threshold management.\n\n## Required Identifier\nAt least one identifier (id, sku, or barcode) must be provided.\n\n## Standard Update Fields\n* **id**: Internal product ID (optional)\n* **sku**: Stock Keeping Unit (optional) \n* **barcode**: EAN/barcode (optional)\n* **quantity**: New stock quantity (optional)\n* **base_price**: New base price (optional)\n* **weight**: Product weight (optional)\n* **condition**: Product condition (optional)\n* **is_active**: Product status (optional)\n\n## Location & Threshold Management\n* **assign_bin_location**: Assign new bin location using format like \"A1-001-01\" (path-rack-shelf or path-rack-shelf-bin)\n* **remove_bin_location**: Remove existing bin location using format like \"A1-001-01\"\n* **threshold**: Set new threshold using format like \"5u\" for 5 units or \"33w\" for 33 weeks\n\n## Location Format Examples\n* \"A1-001-01\" - Path A1, Rack 001, Shelf 01\n* \"B2-003-05-02\" - Path B2, Rack 003, Shelf 05, Bin 02\n\n## Threshold Format Examples  \n* \"5u\" - Alert when stock drops below 5 units\n* \"33w\" - Alert when stock is estimated to run out in 33 weeks\n* \"10u\" - Alert when stock drops below 10 units\n\n## Implementation Notes\n* Bin locations use LocationEntry model for multiple locations per product\n* Threshold format validates against unit-based (\"Nu\") and weeks-based (\"Nw\") patterns\n* Location assignments are additive - use remove_bin_location to remove specific locations\n* Invalid formats return appropriate error responses with guidance","operationId":"update_inventory_inventory_update_post","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateInventoryPayload"}}}},"responses":{"200":{"description":"Inventory item updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateInventoryResponse"},"example":{"product_id":101,"sku":"TSHIRT-BLUE-M","updated_fields":["quantity","base_price","assign_bin_location","threshold"]}}}},"400":{"description":"Invalid request payload","content":{"application/json":{"examples":{"missing_identifier":{"summary":"Missing identifier","value":{"detail":"Must provide at least one identifier: id, sku, or barcode"}},"invalid_location_format":{"summary":"Invalid bin location format","value":{"detail":"Invalid bin location format. Use format like 'A1-001-01' (path-rack-shelf or path-rack-shelf-bin)"}},"invalid_threshold_format":{"summary":"Invalid threshold format","value":{"detail":"Invalid threshold format. Use '5u' for 5 units or '33w' for 33 weeks"}}}}}},"401":{"description":"Missing API credentials"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/inventory/create":{"post":{"tags":["Inventory"],"summary":"Create a new inventory item","description":"Create a new inventory item in the system.\n\n## Request Body Fields:\n- **product_id**: Database ID of parent product (required)\n- **item_name**: Product display name (required)\n- **sku**: Stock Keeping Unit (required, must be unique)\n- **barcode**: Product barcode/EAN (optional)\n- **barcode_type**: Type of barcode (e.g., \"EAN\", \"UPC\")\n- **quantity**: Initial stock quantity (default: 0)\n- **stock_threshold**: Minimum stock alert level\n- **moq**: Minimum order quantity (default: 1)\n- **base_price**: Selling price (required)\n- **purchase_price**: Cost price (optional)\n- **wholesale_price**: B2B price (optional)\n- **weight**: Product weight in kg (optional)\n- **length/width/height**: Dimensions in cm (optional)\n- **condition**: Product condition (default: \"new\")\n- **vat_class**: VAT classification (default: \"standard_rate\")\n- **is_active**: Active status (default: true)\n\n## cURL Example:\n```bash\ncurl -X POST \"https://api.stockpilot.dev/inventory/create\" -H \"Content-Type: application/json\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\" -d '{\"product_id\": 456, \"item_name\": \"Premium Wireless Headphones\", \"sku\": \"WH-PREM-001\", \"barcode\": \"5901234123457\", \"barcode_type\": \"EAN\", \"quantity\": 100, \"stock_threshold\": 10, \"moq\": 1, \"base_price\": \"99.99\", \"purchase_price\": \"45.00\", \"wholesale_price\": \"75.00\", \"weight\": \"0.3\", \"length\": \"20.00\", \"width\": \"15.00\", \"height\": \"8.00\", \"condition\": \"new\", \"vat_class\": \"standard_rate\", \"is_active\": true}'\n```\n\n## Response Example:\n```json\n{\n  \"item_id\": 12346,\n  \"sku\": \"WH-PREM-001\",\n  \"message\": \"Inventory item created successfully\"\n}\n```\n\n## Error Responses:\n- **400**: Invalid request payload, duplicate SKU, missing required fields\n- **401**: Missing or invalid authentication credentials  \n- **500**: Internal server error","operationId":"create_inventory_inventory_create_post","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInventoryPayload"}}}},"responses":{"200":{"description":"Returns the newly created inventory item ID and details","content":{"application/json":{"schema":{}}}},"201":{"description":"Inventory item created successfully","content":{"application/json":{"example":{"id":12346,"product_id":457,"sku":"NEW-PROD-001","message":"Inventory item created successfully"}}}},"400":{"description":"Invalid request payload","content":{"application/json":{"example":{"detail":"Invalid product data provided"}}}},"401":{"description":"Missing API credentials"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bundles/":{"get":{"tags":["Bundles"],"summary":"Get Bundles","description":"Get paginated list of bundles.\n\nReturns all bundles with their items included.","operationId":"get_bundles_bundles__get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Number of bundles per page","default":100,"title":"Page Size"},"description":"Number of bundles per page"},{"name":"X-CLIENT-ID","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"X-CLIENT-SECRET","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Bundles Bundles  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bundles/{bundle_id}":{"get":{"tags":["Bundles"],"summary":"Get Bundle Detail","description":"Get detailed information about a specific bundle.\n\nReturns bundle details including all items in the bundle.","operationId":"get_bundle_detail_bundles__bundle_id__get","parameters":[{"name":"bundle_id","in":"path","required":true,"schema":{"type":"integer","title":"Bundle Id"}},{"name":"X-CLIENT-ID","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"X-CLIENT-SECRET","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BundleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Bundles"],"summary":"Update Bundle","description":"Update an existing bundle.\n\nPartially updates bundle information with the provided fields.","operationId":"update_bundle_bundles__bundle_id__patch","parameters":[{"name":"bundle_id","in":"path","required":true,"schema":{"type":"integer","title":"Bundle Id"}},{"name":"X-CLIENT-ID","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"X-CLIENT-SECRET","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBundlePayload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Bundles"],"summary":"Delete Bundle","description":"Delete a bundle.\n\nPermanently deletes a bundle and all its associated items and relationships.","operationId":"delete_bundle_bundles__bundle_id__delete","parameters":[{"name":"bundle_id","in":"path","required":true,"schema":{"type":"integer","title":"Bundle Id"}},{"name":"X-CLIENT-ID","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"X-CLIENT-SECRET","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bundles/create":{"post":{"tags":["Bundles"],"summary":"Create Bundle","description":"Create a new bundle.\n\nCreates a new bundle with the provided information. Items can be added separately.","operationId":"create_bundle_bundles_create_post","parameters":[{"name":"X-CLIENT-ID","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"X-CLIENT-SECRET","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBundlePayload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BundleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bundles/{bundle_id}/items/add":{"post":{"tags":["Bundles"],"summary":"Add Bundle Items","description":"Add multiple products to a bundle.\n\nAdds or updates the quantities of multiple products in the specified bundle.","operationId":"add_bundle_items_bundles__bundle_id__items_add_post","parameters":[{"name":"bundle_id","in":"path","required":true,"schema":{"type":"integer","title":"Bundle Id"}},{"name":"X-CLIENT-ID","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"X-CLIENT-SECRET","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddBundleItemsPayload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bundles/{bundle_id}/items/{product_id}/update":{"put":{"tags":["Bundles"],"summary":"Update Bundle Item","description":"Update the quantity of a product in a bundle.\n\nUpdates the quantity of an existing product in the bundle.","operationId":"update_bundle_item_bundles__bundle_id__items__product_id__update_put","parameters":[{"name":"bundle_id","in":"path","required":true,"schema":{"type":"integer","title":"Bundle Id"}},{"name":"product_id","in":"path","required":true,"schema":{"type":"integer","title":"Product Id"}},{"name":"X-CLIENT-ID","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"X-CLIENT-SECRET","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBundleItemPayload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BundleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bundles/{bundle_id}/items/{product_id}/delete":{"delete":{"tags":["Bundles"],"summary":"Remove Bundle Item","description":"Remove a product from a bundle.\n\nRemoves the specified product from the bundle completely.","operationId":"remove_bundle_item_bundles__bundle_id__items__product_id__delete_delete","parameters":[{"name":"bundle_id","in":"path","required":true,"schema":{"type":"integer","title":"Bundle Id"}},{"name":"product_id","in":"path","required":true,"schema":{"type":"integer","title":"Product Id"}},{"name":"X-CLIENT-ID","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"X-CLIENT-SECRET","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/warehouses/get":{"get":{"tags":["Warehouses"],"summary":"List warehouses","description":"Retrieve a paginated list of warehouses.\n\n## Parameters\n* **page**: Page number for pagination (starts at 1)\n* **page_size**: Number of warehouses per page (max 100)\n\n## cURL Example:\n```bash\ncurl -X GET \"https://api.stockpilot.dev/warehouses/get?page=1&page_size=10\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n```\n\n## Response Example:\n```json\n{\n  \"count\": 3,\n  \"next\": null,\n  \"previous\": null,\n  \"results\": [\n    {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\",\n      \"unique_id\": \"main-wh-uuid-123\",\n      \"location_code\": \"NL-AMS\",\n      \"country\": \"NL\",\n      \"default\": true,\n      \"inventory_source\": \"default\",\n      \"sales_channel_handle\": \"shopify\",\n      \"sales_channel_id\": 123\n    }\n  ]\n}\n```\n\n## Returns\nA JSON object containing:\n* List of warehouses with location and configuration details\n* Pagination information (count, next, previous)\n* Total count of warehouses","operationId":"get_warehouses_list_warehouses_get_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number for pagination","default":1,"title":"Page"},"description":"Page number for pagination"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Number of items per page","default":100,"title":"Page Size"},"description":"Number of items per page"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Warehouses list retrieved successfully","content":{"application/json":{"schema":{},"example":{"count":3,"results":[{"id":1,"name":"Main Warehouse","unique_id":"main-wh-uuid-123","location_code":"NL-AMS","country":"NL","default":true,"inventory_source":"default","sales_channel_handle":"shopify","sales_channel_id":123,"address":"123 Warehouse Street","city":"Amsterdam","zipcode":"1000AA","created_at":"2023-01-15T10:30:00Z"},{"id":2,"name":"Secondary Warehouse","unique_id":"sec-wh-uuid-456","location_code":"NL-RTM","country":"NL","default":false,"inventory_source":"external","sales_channel_handle":"woocommerce","sales_channel_id":456,"address":"456 Storage Ave","city":"Rotterdam","zipcode":"3000BB","created_at":"2023-02-01T14:20:00Z"}]}}}},"401":{"description":"Missing API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream error: Connection failed"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/warehouses/{unique_id}/items":{"get":{"tags":["Warehouses"],"summary":"List warehouse items","description":"Retrieve a paginated list of warehouse items.\n\n## Parameters\n* **page**: Page number for pagination (starts at 1)\n* **page_size**: Number of items per page (max 100)\n* **unique_id**: Unique ID for this warehouse\n* **sku**: Optional filter by SKU (product identifier)\n* **barcode**: Optional filter by barcode (product identifier)\n\n## Returns\nA JSON object containing:\n* List of warehouse items\n* Pagination information\n* Total count\n\n## Note\nYou can filter by either SKU or barcode, but not both. Both parameters are optional.","operationId":"get_warehouses_items_warehouses__unique_id__items_get","parameters":[{"name":"unique_id","in":"path","required":true,"schema":{"type":"string","description":"Unique ID for this warehouse","title":"Unique Id"},"description":"Unique ID for this warehouse"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number for pagination","default":1,"title":"Page"},"description":"Page number for pagination"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Number of items per page","default":100,"title":"Page Size"},"description":"Number of items per page"},{"name":"sku","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by SKU (product identifier)","title":"Sku"},"description":"Filter by SKU (product identifier)"},{"name":"barcode","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by barcode (product identifier)","title":"Barcode"},"description":"Filter by barcode (product identifier)"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Warehouses items retrieved successfully","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing API credentials"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/get-single":{"get":{"tags":["Orders"],"summary":"Get single order","description":"Retrieve a single order by either order_pk or order_number.\n\n## Parameters\n* **order_pk**: Order primary key (integer) - provide either this OR order_number\n* **order_number**: Order number (string) - provide either this OR order_pk\n\n## cURL Examples:\n```bash\n# Get by order_pk\ncurl -X GET \"https://api.stockpilot.dev/orders/get-single?order_pk=12345\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n\n# Get by order_number  \ncurl -X GET \"https://api.stockpilot.dev/orders/get-single?order_number=ORD-12345\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n```\n\n## Response Example:\n```json\n{\n  \"id\": 123,o\n  \"order_id\": \"ORD-2023-001\",\n  \"customer_name\": \"John Doe\",\n  \"customer_email\": \"john@example.com\",\n  \"is_processing\": true,\n  \"is_completed\": false,\n  \"is_cancelled\": false,\n  \"created_at\": \"2023-01-15T10:30:00Z\",\n  \"order_details\": {\n    \"ship_street\": \"123 Main St\",\n    \"ship_city\": \"Amsterdam\",\n    \"ship_country\": \"NL\",\n    \"order_items\": [\n      {\n        \"product_name\": \"Premium Headphones\",\n        \"quantity\": 2,\n        \"retail_price\": \"99.99\"\n      }\n    ]\n  }\n}\n```\n\n## Returns\nA JSON object containing the complete order details including customer information, items, and status.","operationId":"get_single_order_orders_get_single_get","parameters":[{"name":"order_pk","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Order primary key","title":"Order Pk"},"description":"Order primary key"},{"name":"order_number","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Order number","title":"Order Number"},"description":"Order number"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Order retrieved successfully","content":{"application/json":{"schema":{},"example":{"id":123,"order_id":"ORD-2023-001","customer_name":"John Doe","customer_email":"john@example.com","is_processing":true,"is_completed":false,"is_cancelled":false,"created_at":"2023-01-15T10:30:00Z","order_details":{"ship_street":"123 Main St","ship_city":"Amsterdam","ship_country":"NL","ship_zipcode":"1000AA","order_items":[{"product_name":"Premium Headphones","sku":"WH-PREM-001","quantity":2,"retail_price":"99.99","total_price":"199.98"}],"order_total":"199.98","shipping_cost":"9.99","total_with_shipping":"209.97"}}}}},"400":{"description":"Must provide either order_pk or order_number","content":{"application/json":{"example":{"detail":"Must provide either order_pk or order_number"}}}},"401":{"description":"Missing API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"404":{"description":"Order not found","content":{"application/json":{"example":{"detail":"Order not found"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream error: Connection failed"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders":{"get":{"tags":["Orders"],"summary":"List orders","description":"Retrieve a paginated list of orders with optional status filtering.\n\n## Parameters\n* **page**: Page number for pagination (starts at 1)\n* **page_size**: Number of orders per page (max 100)\n* **status**: Comma-separated list of order statuses to filter by (e.g., \"open,pending,on-hold,cancelled,completed\")\n* **is_forwarded**: Include forwarded orders (default true)\n* **commission**: Include commission data per line item (default false)\n\n## Returns\nA JSON object containing:\n* List of orders\n* Pagination information\n* Total count\n\n## Commissions\n`has_commissions` (order level) and `commissions` (line item level) are always present.\nWithout `commission=true` they are always `false` / `null`.\n\n* **fee**: string, per-unit commission in EUR. Line total is `fee` x `quantity`.\n* **percentage**: integer, so a 14.5% rate shows as 14. Treat as approximate, not exact.\n* **commissions: null**: no commission resolved for that line. Render blank, not 0.00.\n* **has_commissions**: true if any line resolved. Use it to decide whether to show a commission column.\n\nOnly Bol returns data for now, all other channels return `null`. Field names differ per\nchannel (Bol sends `fee`/`percentage`), so key off the order's `handle` and don't assume\n`fee` exists on every commission object. The value is an estimate: it is the commission on\nthe product's listing price at last sync, not on what the order actually sold for.","operationId":"get_orders_list_orders_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number for pagination","default":1,"title":"Page"},"description":"Page number for pagination"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Number of items per page","default":100,"title":"Page Size"},"description":"Number of items per page"},{"name":"status","in":"query","required":false,"schema":{"type":"string","description":"Filter orders by status (comma-separated for multiple statuses)","default":"open","title":"Status"},"description":"Filter orders by status (comma-separated for multiple statuses)"},{"name":"is_forwarded","in":"query","required":false,"schema":{"type":"string","description":"Include forwarded orders (default True)","default":"true","title":"Is Forwarded"},"description":"Include forwarded orders (default True)"},{"name":"commission","in":"query","required":false,"schema":{"type":"boolean","description":"Commission per line item (default False)","default":false,"title":"Commission"},"description":"Commission per line item (default False)"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Orders list retrieved successfully","content":{"application/json":{"schema":{},"example":{"count":100,"next":"https://api.stockpilot.dev/orders?page=2","results":[{"id":123,"order_id":"ORD-2023-001","customer_name":"John Doe","customer_email":"john@example.com","is_processing":true,"is_completed":false,"is_cancelled":false,"is_forwarded":false,"created_at":"2023-01-15T10:30:00Z","channel":"Shopify","order_total":"209.97","order_details":{"ship_street":"123 Main St","ship_city":"Amsterdam","ship_country":"NL","order_items":[{"product_name":"Premium Headphones","quantity":2,"retail_price":"99.99"}]}}]}}}},"401":{"description":"Missing API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream error: Connection failed"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/fulfil":{"post":{"tags":["Orders"],"summary":"Fulfil an order","description":"Register fulfillment for an order with shipping and tracking details.\n\n## Request Body\n* **order_pk**: Order primary key as integer (provide either this OR order_number)\n* **order_number**: Order number as string (provide either this OR order_pk)\n* **fulfilled_at**: ISO datetime string of fulfillment (optional)\n* **service**: Shipping service provider name (optional)\n* **carrier_code**: Carrier code (optional)\n* **carrier_name**: Human-readable carrier name (optional)\n* **shipping_method**: Shipping method (optional)\n* **shipment_type**: Shipment type (optional)\n* **tracking_code**: Tracking number (optional)\n* **tracking_url**: Tracking URL (optional, defaults to \"https://www.no-tracking-url.com/\")\n* **items**: List of fulfilled items with SKU and quantity (optional)\n\n## Example Requests\n\nUsing order_pk:\n```json\n{\n    \"order_pk\": 12345,\n    \"fulfilled_at\": \"2024-03-20T14:30:00Z\",\n    \"carrier_name\": \"PostNL\",\n    \"tracking_code\": \"3SDEUT987654321\",\n    \"items\": [\n        {\"sku\": \"PROD-001\", \"quantity\": 1}\n    ]\n}\n```\n\nUsing order_number:\n```json\n{\n    \"order_number\": \"ORD-12345\",\n    \"fulfilled_at\": \"2024-03-20T14:30:00Z\",\n    \"carrier_name\": \"PostNL\",\n    \"tracking_code\": \"3SDEUT987654321\",\n    \"items\": [\n        {\"sku\": \"PROD-001\", \"quantity\": 1}\n    ]\n}\n```","operationId":"fulfil_order_orders_fulfil_post","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FulfilOrderPayload"}}}},"responses":{"200":{"description":"Order fulfilled successfully","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid request payload"},"401":{"description":"Missing API credentials"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/fulfillment":{"get":{"tags":["Orders"],"summary":"Get fulfillment details","description":"Retrieve fulfillment info for a specific order.\n\n### Query Parameters\n- `order_pk`: Internal Stockpilot order PK (Primary Key) - Can be found in the url of order details page.\n\n### Response example\n```json\n{\n    \"order_id\": 123,\n    \"carrier\": \"PostNL\",\n    \"tracking_number\": \"3SYZ123456789\",\n    \"service\": \"postnl-42\"\n}\n```","operationId":"get_fulfillment_details_orders_fulfillment_get","parameters":[{"name":"order_pk","in":"query","required":true,"schema":{"type":"integer","description":"Internal order primary key","title":"Order Pk"},"description":"Internal order primary key"},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Returns fulfillment metadata for the given order","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/{order_id}/update-status":{"patch":{"tags":["Orders"],"summary":"Update order status","operationId":"update_order_status_orders__order_id__update_status_patch","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"integer","title":"Order Id"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderStatusUpdatePayload"}}}},"responses":{"200":{"description":"Order status updated successfully","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/{order_id}/update-customer-details":{"patch":{"tags":["Orders"],"summary":"Update order customer details","description":"Update billing, shipping and contact information for a specific order.\n\n## Parameters\n* **order_id**: ID of the order to update\n* **payload**: JSON payload with customer details to update","operationId":"update_customer_details_orders__order_id__update_customer_details_patch","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"integer","title":"Order Id"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Payload"}}}},"responses":{"200":{"description":"Customer details updated successfully","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing API credentials"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/create":{"post":{"tags":["Orders"],"summary":"Create a new order","operationId":"create_order_orders_create_post","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderPayload"}}}},"responses":{"200":{"description":"Returns the created order ID and primary key","content":{"application/json":{"schema":{"type":"object","title":"Response Create Order Orders Create Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/{order_id}/update-forwarding":{"patch":{"tags":["Orders"],"summary":"Update order forwarding status","description":"Update order forwarding status and metadata.\n\nThis endpoint marks an order as forwarded and prevents Stockpilot from fetching duplicate orders\nfrom the marketplace. The register_order_id will be prefixed with 'FWD-' in the system.\n\n## Parameters\n* **order_id**: ID of the order to update\n* **register_order_id**: Order number from marketplace (prevents duplicate fetching)\n* **source**: Source identifier (typically 'api')","operationId":"update_forwarding_orders__order_id__update_forwarding_patch","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"integer","title":"Order Id"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateForwardingPayload"}}}},"responses":{"200":{"description":"Order forwarding status updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateForwardingResponse"}}}},"400":{"description":"Bad request - missing required fields"},"401":{"description":"Missing API credentials"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/ordered-items/{item_id}/update":{"patch":{"tags":["Orders"],"summary":"Update ordered item details","description":"Update details of a specific ordered item (quantity, refund etc.).\n\n## Parameters\n* **item_id**: ID of the ordered item to update\n* **payload**: JSON payload with item details to update","operationId":"update_ordered_item_orders_ordered_items__item_id__update_patch","parameters":[{"name":"item_id","in":"path","required":true,"schema":{"type":"integer","title":"Item Id"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Payload"}}}},"responses":{"200":{"description":"Ordered item updated successfully","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing API credentials"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/cancellation-requests":{"get":{"tags":["Orders"],"summary":"Get orders with cancellation requests","operationId":"get_cancellation_requests_orders_cancellation_requests_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/cancellation-request":{"get":{"tags":["Orders"],"summary":"Check cancellation request","operationId":"check_cancellation_request_orders_cancellation_request_get","parameters":[{"name":"order_pk","in":"query","required":true,"schema":{"type":"integer","title":"Order Pk"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancellationRequestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/cancel-order":{"put":{"tags":["Orders"],"summary":"Cancel order","operationId":"cancel_order_orders_cancel_order_put","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Id"}},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","title":"X-Client-Secret"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelOrderPayload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelOrderResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/{order_id}/items/{item_id}":{"delete":{"tags":["Orders"],"summary":"Delete order item","description":"Delete a specific item from an order with optional inventory book-back.\n\nThis endpoint removes an order item and optionally returns inventory to stock.\nIt also recalculates order totals, updates backorder status, and creates a timeline entry.\n\n## Parameters\n* **order_id**: Order primary key (integer)\n* **item_id**: Order item ID to delete\n* **book_back_quantity**: Quantity to return to inventory (defaults to item quantity)\n* **book_back**: Whether to return inventory to stock (default True)\n\n## Request Body Example:\n```json\n{\n    \"book_back_quantity\": 5,\n    \"book_back\": true\n}\n```\n\n## cURL Example:\n```bash\ncurl -X DELETE \"https://api.stockpilot.dev/orders/12345/items/67890\"       -H \"X-CLIENT-ID: your_client_id\"       -H \"X-CLIENT-SECRET: your_client_secret\"       -H \"Content-Type: application/json\"       -d '{\"book_back_quantity\": 2, \"book_back\": true}'\n```\n\n## Response Example:\n```json\n{\n    \"message\": \"Order item deleted successfully\",\n    \"item_id\": 67890,\n    \"book_back_quantity\": 2\n}\n```","operationId":"delete_order_item_orders__order_id__items__item_id__delete","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"integer","title":"Order Id"}},{"name":"item_id","in":"path","required":true,"schema":{"type":"integer","title":"Item Id"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteOrderItemPayload"}}}},"responses":{"200":{"description":"Order item deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteOrderItemResponse"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Missing API credentials"},"404":{"description":"Order or item not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/{order_id}":{"delete":{"tags":["Orders"],"summary":"Delete entire order","description":"Delete an entire order and all its items with optional inventory book-back.\n\nThis endpoint removes the complete order including all items, order details, \nand order call records. Optionally returns all item quantities to inventory.\n\n## Parameters\n* **order_id**: Order primary key (integer) \n* **book_back**: Whether to return inventory to stock (default True)\n\n## Request Body Example:\n```json\n{\n    \"book_back\": true\n}\n```\n\n## cURL Example:\n```bash\ncurl -X DELETE \"https://api.stockpilot.dev/orders/12345\"       -H \"X-CLIENT-ID: your_client_id\"       -H \"X-CLIENT-SECRET: your_client_secret\"       -H \"Content-Type: application/json\"       -d '{\"book_back\": true}'\n```\n\n## Response Example:\n```json\n{\n    \"message\": \"Order deleted successfully\",\n    \"order_id\": \"ORD-12345\",\n    \"order_pk\": 12345,\n    \"book_back_quantity\": 15\n}\n```\n\n## ⚠️ Warning\nThis operation is **irreversible**. The order and all associated data will be permanently deleted.","operationId":"delete_order_orders__order_id__delete","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"integer","title":"Order Id"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteOrderPayload"}}}},"responses":{"200":{"description":"Order deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteOrderResponse"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Missing API credentials"},"404":{"description":"Order not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/{order_pk}/items/add":{"post":{"tags":["Orders"],"summary":"Add item to order","description":"Add a new item to an existing order.\n\nThis endpoint adds a product to an order, updates inventory levels (reduces available quantity,\nincreases reserved), recalculates order totals and taxes, creates timeline entry for audit trail,\nand updates picklist automatically.\n\n## Parameters\n* **order_pk**: Order primary key (integer)\n* **product_id**: ID of the product to add\n* **quantity**: Number of items to add (must be > 0)\n* **retail_price**: Price per item\n* **vat_rate**: VAT rate percentage (defaults to 21)\n\n## Request Body Example:\n```json\n{\n    \"product_id\": 123,\n    \"quantity\": 2,\n    \"retail_price\": 29.99,\n    \"vat_rate\": 21\n}\n```\n\n## cURL Example:\n```bash\ncurl -X POST \"https://api.stockpilot.dev/orders/12345/items/add\"       -H \"X-CLIENT-ID: your_client_id\"       -H \"X-CLIENT-SECRET: your_client_secret\"       -H \"Content-Type: application/json\"       -d '{\"product_id\": 123, \"quantity\": 2, \"retail_price\": 29.99, \"vat_rate\": 21}'\n```\n\n## Response Example:\n```json\n{\n    \"message\": \"Order item added successfully\",\n    \"item_id\": 456,\n    \"product_name\": \"Product Name - Variant\",\n    \"quantity\": 2,\n    \"retail_price\": 29.99,\n    \"total_price\": 59.98\n}\n```","operationId":"add_order_item_orders__order_pk__items_add_post","parameters":[{"name":"order_pk","in":"path","required":true,"schema":{"type":"integer","title":"Order Pk"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddOrderItemPayload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddOrderItemResponse"}}}},"201":{"description":"Order item added successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddOrderItemResponse"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Missing API credentials"},"404":{"description":"Order or product not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/{order_pk}/items/swap":{"post":{"tags":["Orders"],"summary":"Swap order item product","description":"Replace a product in an existing order item with a different product.\n\nThis endpoint swaps the product in an order item while maintaining original quantity and pricing.\nIt handles inventory adjustments for both old and new products, creates timeline entry for \naudit trail, and updates product name display automatically.\n\n## Parameters\n* **order_pk**: Order primary key (integer)\n* **item_id**: ID of the order item to modify\n* **new_product_id**: ID of the new product to swap to\n\n## Request Body Example:\n```json\n{\n    \"item_id\": 456,\n    \"new_product_id\": 789\n}\n```\n\n## cURL Example:\n```bash\ncurl -X POST \"https://api.stockpilot.dev/orders/12345/items/swap\"       -H \"X-CLIENT-ID: your_client_id\"       -H \"X-CLIENT-SECRET: your_client_secret\"       -H \"Content-Type: application/json\"       -d '{\"item_id\": 456, \"new_product_id\": 789}'\n```\n\n## Response Example:\n```json\n{\n    \"message\": \"Order item product swapped successfully\",\n    \"item_id\": 456,\n    \"old_product_name\": \"Old Product Name\",\n    \"new_product_name\": \"New Product Name - Variant\"\n}\n```","operationId":"swap_order_item_orders__order_pk__items_swap_post","parameters":[{"name":"order_pk","in":"path","required":true,"schema":{"type":"integer","title":"Order Pk"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwapOrderItemPayload"}}}},"responses":{"200":{"description":"Order item product swapped successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwapOrderItemResponse"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Missing API credentials"},"404":{"description":"Order, item, or new product not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/{order_id}/move-to-backorder":{"post":{"tags":["Orders"],"summary":"Move order items to backorder","description":"Move specific order items to backorder status.\n\nThis endpoint moves order items to backorder when they cannot be fulfilled due to\ninsufficient stock. The system will update backorder amounts for affected products\nand create appropriate timeline entries for tracking.\n\n## Parameters\n* **order_id**: Order primary key (integer)\n* **backorder_items**: List of items to move to backorder with quantities\n\n## Request Body Example:\n```json\n{\n    \"backorder_items\": [\n        {\"item_id\": 123, \"quantity\": 5},\n        {\"item_id\": 456, \"quantity\": 2}\n    ]\n}\n```\n\n## cURL Example:\n```bash\ncurl -X POST \"https://api.stockpilot.dev/orders/12345/move-to-backorder\"       -H \"X-CLIENT-ID: your_client_id\"       -H \"X-CLIENT-SECRET: your_client_secret\"       -H \"Content-Type: application/json\"       -d '{\"backorder_items\": [{\"item_id\": 123, \"quantity\": 5}]}'\n```\n\n## Response Example:\n```json\n{\n    \"message\": \"Successfully moved 2 item(s) to backorder\",\n    \"items_processed\": 2,\n    \"backorder_status\": \"partial\",\n    \"backorder_results\": [\n        {\"item_id\": 123, \"quantity_moved\": 5, \"new_backorder_qty\": 5},\n        {\"item_id\": 456, \"quantity_moved\": 2, \"new_backorder_qty\": 2}\n    ]\n}\n```","operationId":"move_to_backorder_orders__order_id__move_to_backorder_post","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"integer","title":"Order Id"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveToBackorderPayload"}}}},"responses":{"200":{"description":"Items moved to backorder successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveToBackorderResponse"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Missing API credentials"},"404":{"description":"Order not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/{order_id}/move-from-backorder":{"post":{"tags":["Orders"],"summary":"Move order items from backorder","description":"Move specific order items from backorder status back to normal fulfillment.\n\nThis endpoint removes items from backorder when stock becomes available.\nIt validates that sufficient stock exists before moving items and updates\ninventory levels and backorder amounts accordingly.\n\n## Parameters\n* **order_id**: Order primary key (integer)\n* **get_out_items**: List of items to move from backorder with quantities\n\n## Request Body Example:\n```json\n{\n    \"get_out_items\": [\n        {\"item_id\": 123, \"quantity\": 3},\n        {\"item_id\": 456, \"quantity\": 1}\n    ]\n}\n```\n\n## cURL Example:\n```bash\ncurl -X POST \"https://api.stockpilot.dev/orders/12345/move-from-backorder\"       -H \"X-CLIENT-ID: your_client_id\"       -H \"X-CLIENT-SECRET: your_client_secret\"       -H \"Content-Type: application/json\"       -d '{\"get_out_items\": [{\"item_id\": 123, \"quantity\": 3}]}'\n```\n\n## Response Example:\n```json\n{\n    \"message\": \"Successfully moved 0 item(s) out of backorder\",\n    \"total_moved\": 0,\n    \"backorder_status\": \"full\",\n    \"results\": [\n        {\"item_id\": 123, \"success\": False, \"error\": \"No stock available\"}\n    ]\n}\n```\n\n## Notes\n- Items can only be moved from backorder if sufficient stock is available\n- The system will validate stock levels before processing the request\n- Backorder amounts and inventory levels are updated automatically","operationId":"move_from_backorder_orders__order_id__move_from_backorder_post","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"integer","title":"Order Id"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveFromBackorderPayload"}}}},"responses":{"200":{"description":"Items moved from backorder successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveFromBackorderResponse"}}}},"400":{"description":"Bad request - invalid parameters or insufficient stock"},"401":{"description":"Missing API credentials"},"404":{"description":"Order not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/returns":{"get":{"tags":["Returns"],"summary":"List returns","description":"Retrieve a paginated list of returns for your organization, newest first.\n\nReturns that are not linked to an order are excluded.\n\n## Parameters\n* **page**: Page number for pagination (starts at 1)\n* **page_size**: Number of returns per page (max 100)\n* **status**: Comma-separated list of return statuses to filter by:\n    * `requested` - nothing handled yet, needs action\n    * `partly_accepted` - some lines handled, some not\n    * `accepted` - fully handled\n* **handle**: Sales channel handle to filter by (e.g. \"shopify\", \"bol\")\n* **channel_id**: Sales channel ID to filter by\n\n**Note**: `handle` and `channel_id` must be supplied together - sending only one\nis rejected with a 400 rather than silently returning unfiltered results.\n\n## Returns\nA JSON object containing:\n* List of returns, each with its order and line items\n* Pagination information (`current_page`, `total_pages`)\n* Total count\n\nEach return carries an uppercase `status` of `REQUESTED`, `PARTLY_ACCEPTED` or\n`RETURN_ACCEPTED` (note that the filter values above are lowercase and that\n`accepted` maps to `RETURN_ACCEPTED`). Per-line `items[].is_handled` shows which\nlines are done.\n\n`next` and `previous` are booleans, not URLs - increment `page` to page through\nresults. `product_id` and `sku` are null when a line could not be matched to a\nproduct; fall back to `title`.","operationId":"get_returns_list_returns_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number for pagination","default":1,"title":"Page"},"description":"Page number for pagination"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Number of items per page","default":100,"title":"Page Size"},"description":"Number of items per page"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter returns by status (comma-separated): requested, partly_accepted, accepted","title":"Status"},"description":"Filter returns by status (comma-separated): requested, partly_accepted, accepted"},{"name":"handle","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/ChannelHandle"},{"type":"null"}],"description":"Sales channel handle. Must be used together with channel_id","title":"Handle"},"description":"Sales channel handle. Must be used together with channel_id"},{"name":"channel_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Sales channel ID. Must be used together with handle","title":"Channel Id"},"description":"Sales channel ID. Must be used together with handle"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Returns list retrieved successfully","content":{"application/json":{"schema":{},"example":{"count":42,"next":true,"previous":false,"current_page":1,"total_pages":5,"results":[{"id":1234,"return_id":"R2026-1003","status":"PARTLY_ACCEPTED","is_handled":false,"custom_return":true,"created_at":"2026-08-30T10:12:00Z","handle":"bol","channel_id":12,"order":{"id":987,"order_number":"SP-1029","channel_order_number":"3012345678","customer_name":"Jane Doe"},"items_count":2,"total_quantity":3,"items":[{"id":55,"title":"Blue Mug","rma_id":"R2026-1003-55","quantity":1,"reason":"Damaged","is_handled":true,"tracking_provider":"PostNL","tracking_code":"3SABC123","product_id":4410,"sku":"MUG-BLUE"}]}]}}}},"400":{"description":"Invalid filter or page request","content":{"application/json":{"example":{"error":"Invalid page request","details":"Requested page 9999 but only 5 pages available","total_pages":5,"total_count":42}}}},"401":{"description":"Missing or invalid API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"403":{"description":"Organization tier does not include API access","content":{"application/json":{"example":{"detail":"Upgrade to Growth tier or higher for API access"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"error":"Internal server error"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/purchase-orders/recommendations":{"post":{"tags":["Purchase Orders"],"summary":"Start purchase order recommendations generation","description":"Start generating purchase order recommendations (async task).\n\nExpected payload:\n```json\n{\n    \"supplier_id\": 123,\n    \"lead_time\": 7,          \n    \"durability\": 14,        \n    \"scope_days\": 30,        \n    \"include_flagged\": false, \n    \"include_inbound\": true   \n}\n```\n\nReturns task_id for monitoring progress via `/recommendations/status/{task_id}`.","operationId":"start_purchase_order_recommendations_purchase_orders_recommendations_post","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","title":"Payload"}}}},"responses":{"200":{"description":"Starts async task to generate purchase order recommendations and returns task_id","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/purchase-orders/recommendations/status/{task_id}":{"get":{"tags":["Purchase Orders"],"summary":"Get recommendations task status","description":"Get the status and result of a recommendations task.\n\nReturns:\n- `PROCESSING`: Task is still running\n- `COMPLETED`: Task finished successfully with results\n- `FAILED`: Task failed with error details","operationId":"get_recommendations_status_purchase_orders_recommendations_status__task_id__get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Returns the status and result of a recommendations generation task","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/purchase-orders":{"get":{"tags":["Purchase Orders"],"summary":"Get purchase orders list","description":"Get list of purchase orders with filtering options.\n\nRows come back under `results`, wrapped in the standard paginated envelope: `count`,\n`current_page`, `total_pages`, and `next` / `previous` (both `false` when there is no\nfurther page rather than a URL). Filter with `status` and `supplier_id`.\n\nEvery row also has `delivered_date`, which stays `null` until someone marks the order\ndelivered - it is omitted from the example below because null-valued fields are stripped\nwhen this schema is rendered.\n\n## Delivery progress\nEvery row carries `total_delivered`, `total_remaining` and `fully_delivered`, which is\nenough to render a progress bar or a \"12 / 30 delivered\" badge without a per-row detail\nfetch.\n\nNote that delivering goods does **not** advance `status` - a fully delivered purchase\norder stays `ORDERED` until someone explicitly marks it delivered. Use `fully_delivered`\n/ `total_remaining` to tell whether goods have arrived, not `status`.","operationId":"get_purchase_orders_purchase_orders_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status","title":"Status"},"description":"Filter by status"},{"name":"supplier_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filter by supplier ID","title":"Supplier Id"},"description":"Filter by supplier ID"},{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"page_size","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":100,"minimum":1},{"type":"null"}],"description":"Items per page","default":100,"title":"Page Size"},"description":"Items per page"},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Purchase orders retrieved successfully","content":{"application/json":{"schema":{},"example":{"count":10,"next":false,"previous":false,"current_page":1,"total_pages":1,"results":[{"id":48,"cart_id":"000009","supplier":{"id":6,"name":"Example Supplier Ltd","contact_name":"John Doe"},"status":"ORDERED","order_total":0.0,"shipping_total":0.0,"processed_by":"Sander Hegeman","created_at":"2026-08-13T18:41:31.401525+00:00","updated_at":"2026-08-13T18:41:31.409214+00:00","expected_date":"2026-08-26T22:00:00+00:00","items_count":1,"total_units":2,"total_delivered":2,"total_remaining":0,"fully_delivered":true},{"id":42,"cart_id":"000002","supplier":{"id":6,"name":"Example Supplier Ltd","contact_name":"John Doe"},"status":"ORDERED","order_total":11.32,"shipping_total":0.0,"processed_by":"Sander Hegeman","created_at":"2026-04-12T16:10:06.794594+00:00","updated_at":"2026-04-12T16:10:06.804489+00:00","expected_date":"2026-04-18T22:00:00+00:00","items_count":0,"total_units":0,"total_delivered":1,"total_remaining":1,"fully_delivered":false}]}}}},"401":{"description":"Missing or invalid API credentials"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Purchase Orders"],"summary":"Create purchase order","description":"Create a new purchase order with flexible product identification.\n\n## Product Identification\nEach item in the order can be identified using ANY ONE of the following methods:\n* **product_id**: Database ID (legacy method, continues to work)\n* **barcode**: Product barcode/EAN for lookup\n* **sku**: Product SKU for lookup\n\n## Validation Rules\n* **Exactly one identifier required**: Must provide product_id OR barcode OR sku (not multiple)\n* **Product must exist**: Product must be active and associated with the specified supplier\n* **Supplier association**: Product must be linked to the supplier for the purchase order\n* **MOQ constraints**: Minimum order quantities are automatically applied\n\n## Delivery warehouse\n`delivery_warehouse_id` is optional. When omitted the purchase order is delivered to the\norganization's default warehouse, which matters for multi-warehouse organizations - use\n`GET /warehouses` to look up the id you want.\n\n`expected_delivery` is optional too and can safely be left out.\n\n## Request Examples\n\n### Mixed Identifier Types\n```json\n{\n  \"supplier_id\": 123,\n  \"items\": [\n    {\n      \"product_id\": 456,\n      \"quantity\": 10,\n      \"purchase_price\": 25.50,\n      \"supplier_reference\": \"SUP-REF-123\"\n    },\n    {\n      \"barcode\": \"123456789\",\n      \"quantity\": 5,\n      \"purchase_price\": 12.25\n    },\n    {\n      \"sku\": \"WIDGET-001\",\n      \"quantity\": 20\n    }\n  ],\n  \"delivery_warehouse_id\": 3\n}\n```\n\n### Barcode Scanner Integration\n```json\n{\n  \"supplier_id\": 123,\n  \"items\": [\n    {\"barcode\": \"987654321\", \"quantity\": 15},\n    {\"barcode\": \"567890123\", \"quantity\": 8}\n  ]\n}\n```\n\n## Error Handling\nDetailed error messages include item index for easy debugging:\n* `\"Item 0: Must provide one of: product_id, barcode, or sku\"`\n* `\"Item 1: Provide only one identifier, not multiple\"`\n* `\"Item 2: Product not found (sku: INVALID-SKU)\"`\n\n## Benefits\n* **Flexibility**: Use the most convenient product identifier for your workflow\n* **Integration-friendly**: Perfect for barcode scanners and external systems\n* **Backwards compatible**: Existing product_id usage continues to work\n* **Clear validation**: Detailed error messages help identify issues quickly\n\nCreates purchase order with inbound tracking, validates product availability,\napplies MOQ constraints, and returns confirmation details.","operationId":"create_purchase_order_purchase_orders_post","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePurchaseOrderPayload"}}}},"responses":{"200":{"description":"Creates a new purchase order with flexible product identification","content":{"application/json":{"schema":{}}}},"201":{"description":"Purchase order created successfully","content":{"application/json":{"example":{"order_id":12345,"order_number":"PO-2026-001","supplier_id":123,"total_amount":315.5,"items_count":3,"status":"PENDING","created_at":"2026-02-19T14:30:00Z"}}}},"400":{"description":"Invalid request payload with detailed error information","content":{"application/json":{"examples":{"missing_identifier":{"summary":"Missing product identifier","value":{"detail":"Item 0: Must provide one of: product_id, barcode, or sku"}},"multiple_identifiers":{"summary":"Multiple identifiers provided","value":{"detail":"Item 1: Provide only one identifier (product_id, barcode, or sku), not multiple"}},"product_not_found":{"summary":"Product not found","value":{"detail":"Item 2: Product not found or not associated with supplier (sku: INVALID-SKU)"}},"supplier_mismatch":{"summary":"Product not associated with supplier","value":{"detail":"Item 0: Product not associated with supplier (barcode: 123456789)"}}}}}},"401":{"description":"Missing or invalid API credentials"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/purchase-orders/{order_id}":{"get":{"tags":["Purchase Orders"],"summary":"Get purchase order details","description":"Get detailed information for a specific purchase order.\n\nIncludes order items, supplier information, and inbound tracking details.\n\n## Delivered quantities\nEach entry in `items` carries the delivery state of that line:\n\n| Field | Meaning |\n| --- | --- |\n| `delivered_quantity` | Cumulative quantity actually delivered into stock |\n| `remaining_quantity` | Still outstanding (ordered - delivered, floored at 0) |\n| `dispatched_quantity` | Claimed by an in-flight parcel but not yet applied to stock |\n| `invoiced_quantity` | Quantity on the supplier invoice |\n\n`inbound_tracking` is an object, not a list. It rolls the whole purchase order up into\n`total_ordered` / `total_delivered` / `total_remaining` / `total_parcels`, then carries two\narrays: `items` (per product: `ordered`, `incoming_units`, `delivered`, `invoiced`,\n`remaining`) and `parcels` (every delivery registered against the order, each with\n`parcel_id`, `index_number`, `reference` and `status`). `totals` repeats\n`total_delivered`, `total_remaining` and `fully_delivered` alongside the money fields.\n\n`inbound_tracking.parcels` is where you get the `parcel_id` for\n`POST /purchase-orders/{order_id}/parcels/{parcel_id}/delete`.\n\nThis endpoint is the completion signal after creating a parcel: poll it rather than\n`parcel.status`, which stays `PROCESSING`. See\n`POST /purchase-orders/{order_id}/parcels/create`.\n\nA non-zero `dispatched_quantity` means a parcel is mid-flight. If it never clears, that\nparcel is stuck - see `POST /purchase-orders/{order_id}/parcels/{parcel_id}/delete`.\n\n## `status` is not a delivery signal\nDelivering goods does not advance `status` - a fully delivered purchase order stays\n`ORDERED` until someone explicitly marks it delivered in the Stockpilot UI, because\nmarking it delivered has accounting side effects. To tell whether goods have arrived,\nread `totals.fully_delivered` and `totals.total_remaining`, never `status`.\n\n`delivered_date` follows `status`, not the goods: it stays `null` on a fully delivered\norder until someone marks it delivered. It is absent from the example below only because\nnull-valued fields are stripped when this schema is rendered.","operationId":"get_purchase_order_detail_purchase_orders__order_id__get","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"integer","title":"Order Id"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Purchase order retrieved successfully","content":{"application/json":{"schema":{},"example":{"id":48,"cart_id":"000009","supplier":{"id":6,"name":"Example Supplier Ltd","contact_name":"John Doe","order_email":"orders@example.com"},"status":"ORDERED","order_total":0.0,"shipping_total":0.0,"processed_by":"Sander Hegeman","order_note":"","created_at":"2026-08-13T18:41:31.401525+00:00","updated_at":"2026-08-13T18:41:31.409214+00:00","expected_date":"2026-08-26T22:00:00+00:00","items":[{"product_id":68573,"sku":"ABC-1","product_name":"Widget - Blue","supplier_reference":"SUP-REF-123","ordered_quantity":2,"purchase_price":0.0,"line_total":0.0,"current_stock":4,"backorder_amount":0,"available_stock":4,"recent_sales_30_days":0,"delivered_quantity":2,"remaining_quantity":0,"dispatched_quantity":0,"invoiced_quantity":0}],"inbound_tracking":{"total_ordered":2,"total_delivered":2,"total_remaining":0,"total_parcels":1,"expected_delivery":"2026-08-26T22:00:00+00:00","items":[{"product_id":68573,"ordered":2,"incoming_units":0,"delivered":2,"invoiced":0,"remaining":0}],"parcels":[{"parcel_id":"042252","index_number":1,"reference":"D-2026-000009-1","status":"PROCESSING"}]},"totals":{"items_count":1,"total_units":2,"subtotal":0.0,"total_with_shipping":0.0,"total_delivered":2,"total_remaining":0,"fully_delivered":true}}}}},"401":{"description":"Missing or invalid API credentials"},"404":{"description":"Purchase order not found for this organization"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/purchase-orders/suppliers/list":{"get":{"tags":["Purchase Orders"],"summary":"Get suppliers list","description":"Get list of suppliers for purchase order creation.\n\nReturns all suppliers with their contact information and product counts.","operationId":"get_suppliers_list_purchase_orders_suppliers_list_get","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Returns list of suppliers for purchase order creation","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/purchase-orders/{order_id}/parcels/create":{"post":{"tags":["Purchase Orders"],"summary":"Create a parcel against a purchase order","description":"Register a delivery against a purchase order, in full or in part.\n\nA **parcel** is one delivery against the purchase order. Send the quantities that\narrived; a partial delivery is simply a parcel for less than the outstanding quantity,\nand a purchase order can have as many parcels as it takes to arrive in full.\n\n## Partial parcel\nSend the lines that arrived. Identify each line by **exactly one** of `product_id`,\n`sku` or `barcode` - sending two is a `400`.\n\n```json\n{\n  \"items\": [\n    {\"product_id\": 456, \"quantity\": 4},\n    {\"sku\": \"ABC-1\", \"quantity\": 2}\n  ],\n  \"reference\": \"PACKSLIP-88213\"\n}\n```\n\n* `quantity` must be a positive integer and cannot exceed what is still outstanding on that line.\n* `reference` is optional and is stored on the parcel - one is generated when omitted.\n\n## Full parcel\nOmit `items` entirely and everything still outstanding is taken:\n\n```json\n{\"reference\": \"PACKSLIP-88213\"}\n```\n\nAn empty body `{}` does the same.\n\n## Asynchronous by default (202)\nProcessing is asynchronous. A `202` means the parcel was queued and **the stock has not\nmoved yet**: `applied` is `false`, `delivered_quantity` is the pre-parcel number, and the\n`projected_delivered_quantity` / `projected_remaining_quantity` fields are optimistic UI\nvalues rather than facts.\n\nDo **not** poll `parcel.status` - it stays `PROCESSING` and is not a completion signal.\nRe-fetch `GET /purchase-orders/{order_id}` instead to see confirmed quantities.\n\n## Synchronous mode (200)\nAdd `\"async\": false` and the request blocks until the stock has actually moved, returning\n`200` with `applied: true` and the real `delivered_quantity` / `remaining_quantity` per\nline (instead of the `projected_*` fields).\n\nSynchronous mode runs backorder allocation, warehouse writes and an external accounting\nsync inline, so it can be slow. Prefer the default async mode for large purchase orders.\n\n## Suggested flow\n1. `GET /purchase-orders/{order_id}` and pre-fill the parcel inputs with `remaining_quantity`.\n2. `POST /purchase-orders/{order_id}/parcels/create` with the adjusted quantities.\n3. On `202`, show the `projected_*` numbers optimistically in a \"processing\" state.\n4. Re-fetch the purchase order detail a few seconds later for the confirmed `delivered_quantity`.\n5. `fully_delivered` is `true` once nothing is outstanding.\n\n## Errors\n| Code | Meaning |\n| --- | --- |\n| 400 | Bad or missing identifier, non-positive quantity, product not on this purchase order, duplicate product, over-delivery, or nothing left to receive |\n| 404 | Purchase order does not exist for this organization |\n| 409 | A parcel is already being processed on this purchase order |\n| 500 | Failed to queue the parcel |\n\nError bodies are `{\"error\": \"...\"}`, and item-level messages are prefixed with the item\nindex, e.g. `\"Item 1: cannot receive 11, only 6 remaining for ABC-1\"`.\n\nThe `409` is worth handling explicitly: only one parcel can be in flight per purchase\norder at a time, including one a warehouse user started in the Stockpilot UI. Surface it\nas \"a delivery is currently being processed for this purchase order, try again shortly\"\nrather than a generic failure, and do not retry in a tight loop.\n\nThe body tells you what is blocking you - `blocking_parcel_id` and `dispatched_lines`\n(the quantities that parcel has claimed against each line). Almost always the right\nresponse is to wait a few seconds and re-fetch `GET /purchase-orders/{order_id}`. If the\nsame quantities are still dispatched well after the fact, that parcel never completed and\n`POST /purchase-orders/{order_id}/parcels/{parcel_id}/delete` clears it. The `hint` field\nspells out that same call, with the upstream `/api` prefix.\n\n## Delivering does not change the purchase order status\nA fully delivered purchase order stays `ORDERED` until someone explicitly marks it\ndelivered or completed, because marking it delivered has accounting side effects. Drive\nyour UI off `fully_delivered` / `total_remaining` rather than `status`.","operationId":"create_purchase_order_parcel_purchase_orders__order_id__parcels_create_post","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"integer","title":"Order Id"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePurchaseOrderParcelPayload"}}}},"responses":{"200":{"description":"Synchronous parcel (`async: false`) - stock has been moved, quantities are confirmed","content":{"application/json":{"schema":{},"example":{"purchase_order_id":48,"cart_id":"1042","applied":true,"parcel":{"parcel_id":"748219","index_number":3,"reference":"PACKSLIP-88213","status":"PROCESSING"},"items":[{"product_id":456,"sku":"ABC-1","quantity":4,"ordered_quantity":10,"delivered_quantity":4,"remaining_quantity":6}],"totals":{"total_ordered":10,"total_delivered":4,"total_remaining":6,"fully_delivered":false},"failed_items":[],"status":"COMPLETED"}}}},"202":{"description":"Parcel queued (default). Stock has **not** moved yet - `applied` is `false` and the `projected_*` fields are optimistic","content":{"application/json":{"example":{"purchase_order_id":48,"cart_id":"1042","applied":false,"parcel":{"parcel_id":"748219","index_number":3,"reference":"PACKSLIP-88213","status":"PROCESSING"},"task_id":"3f2a9c10-8b4e-4d2f-9a77-1c0b5e2d4a31","items":[{"product_id":456,"sku":"ABC-1","quantity":4,"ordered_quantity":10,"delivered_quantity":0,"projected_delivered_quantity":4,"projected_remaining_quantity":6}],"totals":{"total_ordered":10,"total_delivered":0,"projected_total_delivered":4,"projected_total_remaining":6,"projected_fully_delivered":false},"status":"PROCESSING","message":"Parcel queued. Poll GET /purchase-orders/48 for applied quantities."}}}},"400":{"description":"Validation error - bad or missing identifier, non-positive quantity, product not on this purchase order, duplicate product, over-delivery, or nothing left to receive","content":{"application/json":{"examples":{"over_delivery":{"summary":"More than outstanding","value":{"error":"Item 1: cannot receive 11, only 6 remaining for ABC-1"}},"missing_identifier":{"summary":"No product identifier","value":{"error":"Item 0: Must provide one of: product_id, barcode, or sku"}},"nothing_outstanding":{"summary":"Nothing left to receive","value":{"error":"Nothing left to receive on this purchase order"}}}}}},"404":{"description":"Purchase order does not exist for this organization","content":{"application/json":{"example":{"error":"Purchase order not found"}}}},"409":{"description":"A parcel is already being processed on this purchase order. The body names the blocking parcel and the quantities it claimed","content":{"application/json":{"example":{"error":"A parcel is already being processed for this purchase order","blocking_parcel_id":"748219","dispatched_lines":[{"product_id":456,"sku":"ABC-1","dispatched_quantity":4}],"hint":"Wait for the in-flight parcel to finish, or remove it with POST /api/purchase-orders/48/parcels/748219/delete"}}}},"500":{"description":"Failed to queue the parcel","content":{"application/json":{"example":{"error":"Failed to queue parcel for processing: broker unavailable"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/purchase-orders/{order_id}/parcels/{parcel_id}/delete":{"post":{"tags":["Purchase Orders"],"summary":"Remove an unprocessed parcel","description":"Remove a parcel that was never applied to stock, releasing the quantities it claimed.\n\nCreating a parcel happens in two steps: the quantities are *dispatched* against each\npurchase order line, then a worker applies them to stock. If that second step never runs\nthe quantities stay dispatched, and because only one parcel may be in flight per purchase\norder at a time, every later `POST /purchase-orders/{order_id}/parcels/create` returns `409`.\nRemoving the parcel releases those quantities so deliveries can continue.\n\nDispatched quantities are visible as `dispatched_quantity` on each line of\n`GET /purchase-orders/{order_id}`, and in the `dispatched_lines` array of the `409` body,\nwhich also names the `blocking_parcel_id` to pass here. Failing that, every parcel on the\norder is listed with its `parcel_id` and `status` under `inbound_tracking.parcels`.\n\n## This does not reverse stock\nOnly quantities that were never applied are released. Anything a completed parcel already\ndelivered stays delivered - `delivered_quantity` and `total_delivered` are untouched. A\nparcel whose `status` is `COMPLETED` is refused with a `409`.\n\nRemoval is permanent: the parcel is deleted rather than marked failed, so it disappears\nfrom `total_parcels` and from the purchase order's parcel history. The delivery it\nrepresented has to be re-created with `POST /purchase-orders/{order_id}/parcels/create`.\n\n## Releases every dispatched line on the purchase order\nThe release is scoped to the purchase order, not to this parcel alone: every line with a\nnon-zero `dispatched_quantity` is reset, whichever parcel claimed it. In practice only one\nparcel is ever in flight at a time, so this is the same set - but do not use this endpoint\nto prune one of several pending parcels.\n\n## Do not reach for this on every 409\nA `409` from `POST /purchase-orders/{order_id}/parcels/create` normally means a parcel really is\nin flight, quite possibly one a warehouse user started in the Stockpilot UI seconds ago -\nremoving it throws away their work. Treat this as a recovery tool for a parcel that is\ngenuinely stuck:\n\n1. On `409`, note `blocking_parcel_id`, wait a few seconds and re-fetch\n   `GET /purchase-orders/{order_id}`.\n2. If `delivered_quantity` moved, the parcel completed - nothing to remove.\n3. If the same `dispatched_quantity` values are still there well after the fact, the\n   parcel never completed. `POST /purchase-orders/{order_id}/parcels/{parcel_id}/delete`.\n4. Re-create the delivery with `POST /purchase-orders/{order_id}/parcels/create`.\n\n## Errors\n| Code | Meaning |\n| --- | --- |\n| 400 | The purchase order has no inbound tracking record |\n| 404 | Purchase order or parcel does not exist for this organization |\n| 409 | The parcel is `COMPLETED` - its quantities are in stock and cannot be released |\n\nError bodies are `{\"error\": \"...\"}`.","operationId":"delete_purchase_order_parcel_purchase_orders__order_id__parcels__parcel_id__delete_post","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"integer","title":"Order Id"}},{"name":"parcel_id","in":"path","required":true,"schema":{"type":"string","title":"Parcel Id"}},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Parcel removed and its dispatched quantities released","content":{"application/json":{"schema":{},"example":{"purchase_order_id":48,"cart_id":"1042","removed_parcel_id":"748219","released":[{"product_id":456,"sku":"ABC-1","released_quantity":4}],"total_released":4,"message":"Parcel removed. You can create a new parcel for this purchase order now."}}}},"400":{"description":"The purchase order has no inbound tracking record","content":{"application/json":{"example":{"error":"Purchase order has no inbound tracking record"}}}},"404":{"description":"Purchase order or parcel does not exist for this organization","content":{"application/json":{"examples":{"order_not_found":{"summary":"Unknown purchase order","value":{"error":"Purchase order not found"}},"parcel_not_found":{"summary":"Unknown parcel on this purchase order","value":{"error":"Parcel not found"}}}}}},"409":{"description":"The parcel has already been applied to stock and cannot be removed","content":{"application/json":{"example":{"error":"This parcel has already been completed and cannot be removed"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/invoices/send":{"post":{"tags":["Invoices"],"summary":"Send invoice for an order","description":"Send an invoice for a specific order.\n\n## Parameters\n* **order_pk**: Order primary key as integer (form data)\n* **invoice**: Invoice PDF file (optional). If provided, Stockpilot will use this invoice, otherwise it creates a new one from Stockpilot data\n\n## Behavior\n- If `invoice` file is provided: Uses the uploaded PDF as the invoice\n- If `invoice` file is not provided: Stockpilot automatically generates a new invoice from order data\n\n## Returns\nA JSON object containing:\n* **success**: Boolean indicating operation success\n* **message**: Descriptive message about the operation result","operationId":"send_invoice_invoices_send_post","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_send_invoice_invoices_send_post"}}}},"responses":{"200":{"description":"Invoice sent successfully","content":{"application/json":{"schema":{},"example":{"success":true,"message":"Invoice sent successfully"}}}},"400":{"description":"Failed to send invoice","content":{"application/json":{"example":{"success":false,"message":"Failed to send invoice"}}}},"401":{"description":"Missing API credentials"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/invoices/get/{order_pk}":{"get":{"tags":["Invoices"],"summary":"Get invoice for an order","description":"Retrieve an invoice PDF for a specific order. Returns a PDF file stream.","operationId":"get_invoice_invoices_get__order_pk__get","parameters":[{"name":"order_pk","in":"path","required":true,"schema":{"type":"integer","title":"Order Pk"}},{"name":"X-CLIENT-ID","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"X-CLIENT-SECRET","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Invoice retrieved successfully - returns PDF file stream","content":{"application/json":{"schema":{}},"application/pdf":{"example":"Binary PDF content"}}},"404":{"description":"Invoice not found for the specified order","content":{"application/json":{"example":{"success":false,"message":"Invoice not found"}}}},"401":{"description":"Missing or invalid API credentials","content":{"application/json":{"example":{"detail":"Missing required headers: X-CLIENT-ID, X-CLIENT-SECRET"}}}},"500":{"description":"Internal server error or upstream service error","content":{"application/json":{"example":{"detail":"Upstream error: Connection failed"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/shipping/integrations":{"get":{"tags":["Shipping"],"summary":"List available shipping integrations","description":"Returns all configured shipping integrations (e.g. PostNL, DHL, Sendcloud, Bol.com) for the authenticated organization.","operationId":"get_shipping_integrations_shipping_integrations_get","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Returns shipping carriers configured for the organization","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/shipping/label-templates":{"get":{"tags":["Shipping"],"summary":"Get available shipping label templates","description":"Returns available shipping label templates for the authenticated organization.\n\nThis includes both:\n- **Database-defined templates** (created via the backoffice)\n- **Virtual templates**, which are auto-generated for convenience:\n\n### Virtual Templates\n\n#### Bol.com VVB labels (`carrier_identifier` = `VVB_MAILBOX` / `VVB_PARCEL`)\nIf you have one or more BolAPIConnect channels connected, you get these per channel:\n- `id = vvb_mailbox_<channel_id>` → `Bol.com Mailbox Label`\n- `id = vvb_parcel_<channel_id>` → `Bol.com Parcel Label`\n\nUse the Bol channel ID from `/shipping/integrations` to select the right template.\n\n#### Letter labels\nThese are always available:\n- `id = letter_unstamped` → `Unstamped Letter`\n- `id = letter_stamped` → `Stamped Letter`\n\n### Query examples\n- `GET /shipping/label-templates` → all templates\n- `GET /shipping/label-templates?carrier=vvb_mailbox&id=42` → Bol.com mailbox template for Bol channel 42\n- `GET /shipping/label-templates?carrier=letter&id=unstamped` → Unstamped letter template\n\n### Response Format\n```json\n[\n    {\n        \"id\": \"vvb_parcel_42\",\n        \"name\": \"Bol.com Parcel Label\",\n        \"carrier_identifier\": \"VVB_PARCEL\"\n    },\n    {\n        \"id\": \"letter_unstamped\",\n        \"name\": \"Unstamped Letter\",\n        \"carrier_identifier\": \"LETTER_UNSTAMPED\"\n    },\n    ...\n]","operationId":"get_shipping_label_templates_shipping_label_templates_get","parameters":[{"name":"carrier","in":"query","required":false,"schema":{"type":"string","description":"Optional carrier type, e.g. 'vvb_mailbox', 'vvb_parcel', or 'letter'","title":"Carrier"},"description":"Optional carrier type, e.g. 'vvb_mailbox', 'vvb_parcel', or 'letter'"},{"name":"id","in":"query","required":false,"schema":{"type":"string","description":"Optional template ID (e.g. channel ID for vvb, or 'unstamped'/'stamped' for letter)","title":"Id"},"description":"Optional template ID (e.g. channel ID for vvb, or 'unstamped'/'stamped' for letter)"},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Returns label templates including virtual ones like Bol.com (VVB) and Letter types","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/shipping/request-label":{"post":{"tags":["Shipping"],"summary":"Request a shipping label","description":"Initiates the creation of a shipping label for a given order.\n\nDepending on the `template_id` and `carrier_id`, the backend will determine how to generate the label:\n- For `vvb_` labels (Bol.com): auto-maps to Mailbox/Parcel logic\n- For `letter` or `_letter` templates: creates an internal letter label\n- For other template-based carriers: triggers a standard label flow\n\n### Request Body\n- `template_id`: The template identifier (e.g. `vvb_mailbox_4`, `letter_unstamped`, `123`)\n- `carrier_id`: The carrier source (e.g. `bol-4`, `letter`, `sendcloud-1`)\n- `order_pk`: Primary key of the order\n\n### Returns\nA JSON object containing:\n- `status`: `queued`\n- `entity_id`: Use in `/retrieve-label` endpoint\n- `order_pk`: Order identifier\n- `service`: Resolved carrier/service type (e.g. `bol`, `letter`, `dhl`)","operationId":"request_label_shipping_request_label_post","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestLabelPayload"}}}},"responses":{"200":{"description":"Queues label generation and returns task ID and metadata","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/shipping/retrieve-label":{"post":{"tags":["Shipping"],"summary":"Retrieve a generated shipping label","description":"Retrieves a previously requested shipping label using the `entity_id` returned from `/request-label`.\n\nThis endpoint checks task status and returns a streaming PDF file when complete.\n\n### Request Body\n- `entity_id`: The Entity ID that you retrieve from the `/request-label` endpoint\n- `service`: The carrier block (e.g. `bol`, `letter`, `dhl`)\n- `order_pk`: The internal order PK\n\n### Returns\nA `200` response with a streamed PDF if ready.\nAdds tracking metadata to headers:\n- `X-Tracking-Number`\n- `X-Carrier`\n- `X-Label-Status`\n\nA `202` response is returned if the label is still processing.","operationId":"retrieve_label_shipping_retrieve_label_post","parameters":[{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetrieveLabelPayload"}}}},"responses":{"200":{"description":"Returns a PDF with tracking and carrier metadata","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/shipping/label-suggestion":{"get":{"tags":["Shipping"],"summary":"Get label suggestion based on shipping rules","description":"Returns a suggested shipping label template based on your configured shipping rules.\n\nThis may resolve to:\n- A real label template (e.g. created in the back office)\n- A virtual template (e.g. Bol.com VVB label or letter label)\n\n### Example request\n`/shipping/label-suggestion?order_pk=123`\n\n### Example response\n```json\n{\n    \"suggested\": {\n        \"label_template\": \"DHL NL\",\n        \"vvb_label_type\": null,\n        \"letter_type\": null\n    }\n}\n```\n\nThe resolution logic is fully rule-based, matching conditions like SKU, weight, country, etc.","operationId":"get_shipping_label_suggestion_shipping_label_suggestion_get","parameters":[{"name":"order_pk","in":"query","required":true,"schema":{"type":"string","description":"Internal order ID to evaluate shipping rules against","title":"Order Pk"},"description":"Internal order ID to evaluate shipping rules against"},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Returns the most appropriate label template for the given order based on configured rules","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/items/sales":{"get":{"tags":["Analytics"],"summary":"Get item sales analytics","description":"Get sales analytics for a specific inventory item.\n\n## Product Identification (choose exactly one):\n- **id**: Product ID (primary key) - query parameter: ?id=123\n- **sku**: Product SKU - query parameter: ?sku=PRODUCT-SKU  \n- **barcode**: Product barcode/EAN - query parameter: ?barcode=123456789\n\n## Query Parameters:\n- **range**: Number of days to look back (default: 14)\n- **include_channels**: Include per-channel breakdown (default: false)\n- **metrics**: Comma-separated list of metrics to include (default: all)\n  - Options: total_orders, total_items, revenue, forecast, pricing, daily_breakdown\n\n## cURL Example:\n```bash\ncurl -X GET \"https://api.stockpilot.dev/analytics/items/sales?sku=PROD-123&range=30&include_channels=true\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n```\n\n## Response Example:\n```json\n{\n  \"item\": {\n    \"id\": 456,\n    \"sku\": \"PROD-123\",\n    \"product_name\": \"Sample Product\",\n    \"current_stock\": 50,\n    \"backorder_amount\": 10\n  },\n  \"period\": {\n    \"days\": 30,\n    \"start_date\": \"2023-01-01T00:00:00Z\",\n    \"end_date\": \"2023-01-30T23:59:59Z\"\n  },\n  \"total_orders\": 25,\n  \"total_items_sold\": 75,\n  \"revenue\": {\n    \"total_revenue\": 1875.50,\n    \"average_order_value\": 75.02\n  },\n  \"pricing\": {\n    \"average_selling_price\": 25.00,\n    \"highest_selling_price\": 30.00,\n    \"lowest_selling_price\": 20.00\n  },\n  \"forecast\": {\n    \"daily_forecast\": 2.5,\n    \"weekly_forecast\": 17.5,\n    \"monthly_forecast\": 75.0,\n    \"method\": \"filtered_average\"\n  },\n  \"daily_breakdown\": [\n    {\n      \"date\": \"2023-01-01\",\n      \"quantity_sold\": 3,\n      \"orders\": 2,\n      \"revenue\": 75.00\n    }\n  ],\n  \"channel_breakdown\": [\n    {\n      \"channel\": \"Shopify\",\n      \"total_items_sold\": 45,\n      \"total_orders\": 15,\n      \"total_revenue\": 1125.00\n    }\n  ]\n}\n```\n\n## Error Responses:\n- **400**: Missing or multiple identifiers, invalid parameters\n- **404**: Item not found","operationId":"get_item_sales_analytics_analytics_items_sales_get","parameters":[{"name":"id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Product ID (primary key)","title":"Id"},"description":"Product ID (primary key)"},{"name":"sku","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Product SKU","title":"Sku"},"description":"Product SKU"},{"name":"barcode","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Product barcode/EAN","title":"Barcode"},"description":"Product barcode/EAN"},{"name":"range","in":"query","required":false,"schema":{"type":"integer","description":"Number of days to look back","default":14,"title":"Range"},"description":"Number of days to look back"},{"name":"include_channels","in":"query","required":false,"schema":{"type":"boolean","description":"Include per-channel breakdown","default":false,"title":"Include Channels"},"description":"Include per-channel breakdown"},{"name":"metrics","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated list of metrics (total_orders, total_items, revenue, forecast, pricing, daily_breakdown)","default":"all","title":"Metrics"},"description":"Comma-separated list of metrics (total_orders, total_items, revenue, forecast, pricing, daily_breakdown)"},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Sales analytics retrieved successfully","content":{"application/json":{"schema":{},"example":{"item":{"id":456,"sku":"PROD-123","product_name":"Sample Product","current_stock":50,"backorder_amount":10},"period":{"days":30,"start_date":"2023-01-01T00:00:00Z","end_date":"2023-01-30T23:59:59Z"},"total_orders":25,"total_items_sold":75,"revenue":{"total_revenue":1875.5,"average_order_value":75.02},"pricing":{"average_selling_price":25.0,"highest_selling_price":30.0,"lowest_selling_price":20.0},"forecast":{"daily_forecast":2.5,"weekly_forecast":17.5,"monthly_forecast":75.0,"method":"filtered_average"},"daily_breakdown":[{"date":"2023-01-01","quantity_sold":3,"orders":2,"revenue":75.0}],"channel_breakdown":[{"channel":"Shopify","total_items_sold":45,"total_orders":15,"total_revenue":1125.0}]}}}},"400":{"description":"Missing or multiple identifiers, invalid parameters","content":{"application/json":{"example":{"detail":"Must provide one of: id, sku, or barcode"}}}},"404":{"description":"Item not found","content":{"application/json":{"example":{"detail":"Item with SKU 'PROD-123' not found"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/product-order-history":{"get":{"tags":["Analytics"],"summary":"Get product order history","description":"Get order history for a specific product based on ID, EAN, or SKU.\n\n## Product Identification (choose exactly one):\n- **id**: Product ID (primary key) - query parameter: ?id=123\n- **sku**: Product SKU - query parameter: ?sku=PRODUCT-SKU  \n- **barcode**: Product barcode/EAN - query parameter: ?barcode=123456789\n\n## Query Parameters:\n- **start_date**: Start date filter (YYYY-MM-DD format, optional)\n- **end_date**: End date filter (YYYY-MM-DD format, optional)\n\n## cURL Example:\n```bash\ncurl -X GET \"https://api.stockpilot.dev/analytics/product-order-history?sku=HEADPHONES-PRO&start_date=2026-01-01&end_date=2026-01-31\" \\\n     -H \"X-CLIENT-ID: your_client_id\" \\\n     -H \"X-CLIENT-SECRET: your_client_secret\"\n```\n\n## Response Data:\n- **Product Information**: ID, SKU, name, and barcode\n- **Order Details**: Order IDs, channels, dates, quantities\n- **Customer Information**: Names, emails, shipping addresses\n- **Filtering Applied**: Date range used for the query\n\n## Use Cases:\n- **Customer Support**: Find all orders containing a specific product\n- **Product Analysis**: Track which customers buy certain products\n- **Channel Performance**: See which sales channels sell specific items\n- **Date Range Analysis**: Filter orders within specific time periods\n\n## Error Responses:\n- **400**: Missing or multiple identifiers, invalid date format\n- **404**: Product not found","operationId":"get_product_order_history_analytics_product_order_history_get","parameters":[{"name":"id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Product ID (primary key)","title":"Id"},"description":"Product ID (primary key)"},{"name":"sku","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Product SKU","title":"Sku"},"description":"Product SKU"},{"name":"barcode","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Product barcode/EAN","title":"Barcode"},"description":"Product barcode/EAN"},{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Start date filter (YYYY-MM-DD format)","title":"Start Date"},"description":"Start date filter (YYYY-MM-DD format)"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"End date filter (YYYY-MM-DD format)","title":"End Date"},"description":"End date filter (YYYY-MM-DD format)"},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Product order history retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductOrderHistoryResponse"},"example":{"product":{"id":456,"sku":"HEADPHONES-PRO","product_name":"Premium Wireless Headphones","barcode":"5901234123457"},"filters":{"start_date":"2026-01-01","end_date":"2026-01-31"},"total_orders":15,"orders":[{"id":12345,"order_id":"ORD-2026-001","channel":"Shopify Store","channel_order_id":"SP-789","created_at":"2026-01-15T10:30:00Z","quantity":2,"customer_name":"John Doe","customer_email":"john.doe@example.com","ship_street":"123 Main St","ship_city":"Amsterdam","ship_country":"NL"},{"id":12346,"order_id":"ORD-2026-002","channel":"Bol.com","channel_order_id":"BOL-456","created_at":"2026-01-20T14:45:00Z","quantity":1,"customer_name":"Jane Smith","customer_email":"jane.smith@example.com","ship_street":"456 Oak Ave","ship_city":"Rotterdam","ship_country":"NL"}]}}}},"400":{"description":"Missing or multiple identifiers, invalid date format","content":{"application/json":{"examples":{"missing_identifier":{"summary":"Missing product identifier","value":{"detail":"Must provide one of: id, sku, or barcode"}},"multiple_identifiers":{"summary":"Multiple identifiers provided","value":{"detail":"Provide only one identifier (id, sku, or barcode), not multiple"}},"invalid_date":{"summary":"Invalid date format","value":{"detail":"Invalid start_date format. Use YYYY-MM-DD"}}}}}},"404":{"description":"Product not found","content":{"application/json":{"example":{"detail":"Product with SKU 'HEADPHONES-PRO' not found"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/sales-summary":{"get":{"tags":["Analytics"],"summary":"Get sales summary","description":"Get sales summary across all items for the organization.\n\n## Query Parameters:\n- **range**: Number of days to look back (default: 14)\n- **top_items**: Number of top selling items to include (default: 10)\n\n## cURL Example:\n```bash\ncurl -X GET \"https://api.stockpilot.dev/analytics/sales-summary?range=30&top_items=5\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n```\n\n## Response Example:\n```json\n{\n  \"period\": {\n    \"days\": 30,\n    \"start_date\": \"2023-01-01T00:00:00Z\",\n    \"end_date\": \"2023-01-30T23:59:59Z\"\n  },\n  \"summary\": {\n    \"total_items_sold\": 350,\n    \"total_orders\": 85,\n    \"total_revenue\": 8750.50,\n    \"unique_products_sold\": 25,\n    \"average_order_value\": 102.95\n  },\n  \"top_selling_items\": [\n    {\n      \"item_id\": 456,\n      \"sku\": \"PROD-123\",\n      \"product_name\": \"Sample Product\",\n      \"barcode\": \"1234567890\",\n      \"total_sold\": 75,\n      \"total_orders\": 25,\n      \"total_revenue\": 1875.50\n    }\n  ]\n}\n```\n\n## Returns:\n- Overall sales statistics\n- Top selling items by quantity\n- Revenue metrics and trends\n- Order analytics summary","operationId":"get_sales_summary_analytics_sales_summary_get","parameters":[{"name":"range","in":"query","required":false,"schema":{"type":"integer","description":"Number of days to look back","default":14,"title":"Range"},"description":"Number of days to look back"},{"name":"top_items","in":"query","required":false,"schema":{"type":"integer","description":"Number of top selling items to include","default":10,"title":"Top Items"},"description":"Number of top selling items to include"},{"name":"x-client-id","in":"header","required":true,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":true,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Sales summary retrieved successfully","content":{"application/json":{"schema":{},"example":{"period":{"days":30,"start_date":"2023-01-01T00:00:00Z","end_date":"2023-01-30T23:59:59Z"},"summary":{"total_items_sold":350,"total_orders":85,"total_revenue":8750.5,"unique_products_sold":25,"average_order_value":102.95},"top_selling_items":[{"item_id":456,"sku":"PROD-123","product_name":"Sample Product","barcode":"1234567890","total_sold":75,"total_orders":25,"total_revenue":1875.5}]}}}},"401":{"description":"Missing API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream error: Connection failed"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sales-channels":{"get":{"tags":["Sales Channels"],"summary":"List all channels","description":"Retrieve a paginated list of all available sales channels and integrations.\n\n## Parameters\n* **page**: Page number for pagination (starts at 1)\n* **page_size**: Number of channels per page (max 100)\n\n## cURL Example:\n```bash\ncurl -X GET \"https://api.stockpilot.dev/sales-channels?page=1&page_size=10\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\"\n```\n\n## Response Example:\n```json\n{\n  \"channels\": [\n    {\n      \"id\": 1,\n      \"name\": \"My Shopify Store\",\n      \"handle\": \"shopify\",\n      \"is_active\": true,\n      \"logo\": \"https://sp-s3-bucket.s3.amazonaws.com/static/assets/images/channel-icons/shopify-icon.png\",\n      \"channel_is_forwarded\": false,\n      \"is_inventory_source\": true,\n      \"is_synchronized\": true,\n      \"created_at\": \"2023-01-15T10:30:00Z\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"WooCommerce Store\", \n      \"handle\": \"woocommerce\",\n      \"is_active\": true,\n      \"is_inventory_source\": false,\n      \"is_synchronized\": true\n    }\n  ]\n}\n```\n\n## Supported Handles:\n- **shopify**, **woocommerce**, **amazon**, **bol**, **etsy**, **kaufland**, **mirakl**, **b2b-portal**\n\n## Returns\nA JSON object containing:\n* List of connected sales channels with configuration details\n* Channel status and synchronization information  \n* Logo URLs and integration metadata","operationId":"get_channels_sales_channels_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number for pagination","default":1,"title":"Page"},"description":"Page number for pagination"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Number of items per page","default":100,"title":"Page Size"},"description":"Number of items per page"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"List of channels retrieved successfully","content":{"application/json":{"schema":{},"example":{"channels":[{"id":1,"name":"My Shopify Store","handle":"shopify","is_active":true,"logo":"https://sp-s3-bucket.s3.amazonaws.com/static/assets/images/channel-icons/shopify-icon.png","channel_is_forwarded":false,"is_inventory_source":true,"is_synchronized":true,"created_at":"2023-01-15T10:30:00Z","updated_at":"2023-01-15T10:30:00Z"},{"id":2,"name":"WooCommerce Store","handle":"woocommerce","is_active":true,"logo":"https://sp-s3-bucket.s3.amazonaws.com/static/assets/images/channel-icons/woo-icon.png","channel_is_forwarded":false,"is_inventory_source":false,"is_synchronized":true,"created_at":"2023-02-01T14:20:00Z","updated_at":"2023-02-01T14:20:00Z"}]}}}},"401":{"description":"Missing API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"403":{"description":"Invalid credentials","content":{"application/json":{"example":{"error":"Invalid credentials"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sales-channels/sync-listings":{"post":{"tags":["Sales Channels"],"summary":"Sync listings for a channel","description":"Queue a listing sync for one connected sales channel.\n\nListings are pulled from the channel into Stockpilot in the background. The call\nreturns as soon as the job is queued - it does not wait for the sync to finish.\n\n## Request Body\n* **channel**: Channel handle, exactly as returned in the `handle` field of\n  `GET /sales-channels`\n* **channel_id**: ID of the connected channel, from the `id` field of the same response\n* **full_sync**: Optional, defaults to `false`. Re-imports every listing rather than\n  only what changed since the last sync. Slower and heavier on the channel's rate\n  limits - use it when reconciling after a suspected gap.\n\n## Request Body Example\n```json\n{\n  \"channel\": \"shopify\",\n  \"channel_id\": 1,\n  \"full_sync\": false\n}\n```\n\n## cURL Example:\n```bash\ncurl -X POST \"https://api.stockpilot.dev/sales-channels/sync-listings\" -H \"X-CLIENT-ID: your_client_id\" -H \"X-CLIENT-SECRET: your_client_secret\" -H \"Content-Type: application/json\" -d '{\"channel\": \"shopify\", \"channel_id\": 1}'\n```\n\n## Returns\n`202` with `status: \"queued\"` and the background `task_id` when a sync was started.\n\nOnly one sync runs per channel at a time. If one is already pending or in flight the\ncall is a no-op and returns `200` with `status` set to `already_queued` or\n`already_syncing`, together with the `last_synced` timestamp of the previous run -\nso it is safe to retry without stacking up duplicate jobs.\n\n## Supported Handles\n`amazon`, `ankorstore`, `bigcommerce`, `bol`, `ccvshop`, `cdiscount`, `ebay`, `etsy`,\n`faire`, `floriday`, `fnacdarty`, `kaufland`, `lightspeed`, `magento`, `mijnwebwinkel`,\n`mirakl`, `opencart`, `orderchamp`, `otto`, `prestashop`, `shopify`, `shopware`,\n`squarespace`, `target`, `temu`, `tiktok`, `valkaspos`, `walmart`, `woocommerce`\n\nChannels outside this list return `400` - either they have no listing sync, or they\nare not a listing-bearing channel at all. Channels belonging to another organization\nreturn `404`, the same as an ID that does not exist.","operationId":"sync_channel_listings_sales_channels_sync_listings_post","parameters":[{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncChannelListingsPayload"}}}},"responses":{"202":{"description":"Sync queued successfully","content":{"application/json":{"schema":{},"example":{"status":"queued","task_id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","channel":"shopify","channel_id":1,"full_sync":false}}}},"200":{"description":"A sync is already queued or already running - nothing was started","content":{"application/json":{"examples":{"already_queued":{"summary":"A sync is already waiting in the queue","value":{"status":"already_queued","channel":"shopify","channel_id":1,"last_synced":"2026-09-01T09:14:22Z"}},"already_syncing":{"summary":"A sync is running right now","value":{"status":"already_syncing","channel":"shopify","channel_id":1,"last_synced":"2026-09-01T09:14:22Z"}}}}}},"400":{"description":"Missing fields, or a channel that does not support listing sync","content":{"application/json":{"examples":{"missing_fields":{"summary":"channel or channel_id omitted","value":{"error":"channel and channel_id are required"}},"unsupported_channel":{"summary":"Channel has no listing sync","value":{"error":"Unsupported channel 'cdon'"}}}}}},"401":{"description":"Missing API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"403":{"description":"Invalid credentials","content":{"application/json":{"example":{"error":"Invalid credentials"}}}},"404":{"description":"Channel not found, or not owned by your organization","content":{"application/json":{"example":{"error":"Channel not found"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks":{"get":{"tags":["Webhooks"],"summary":"List webhooks","description":"Retrieve a paginated list of webhook subscriptions for your organization.\n\nBoth active and deactivated webhooks are returned - check `is_active`. A webhook that\nhas been soft deleted, or auto-deactivated after repeated delivery failures, stays in\nthis list with `is_active: false` and keeps its delivery history.\n\n## Parameters\n* **page**: Page number for pagination (starts at 1)\n* **page_size**: Number of webhooks per page (max 100)\n* **category**: Filter by event category, the namespace part of the event name\n* **event**: Filter by exact event type, e.g. `inventory.stock_changed`\n\n## Returns\nA JSON object containing the list of webhooks and pagination information.","operationId":"get_webhooks_list_webhooks_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number for pagination","default":1,"title":"Page"},"description":"Page number for pagination"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Number of items per page","default":100,"title":"Page Size"},"description":"Number of items per page"},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by event category (the event namespace, e.g. `orders` or `inventory`)","title":"Category"},"description":"Filter by event category (the event namespace, e.g. `orders` or `inventory`)"},{"name":"event","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"null"}],"description":"Filter by exact event type","title":"Event"},"description":"Filter by exact event type"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Webhooks retrieved successfully","content":{"application/json":{"schema":{},"example":{"count":2,"current_page":1,"total_pages":1,"results":[{"id":42,"name":"Stock changes to ERP","event":"inventory.stock_changed","target_url":"https://erp.example.com/hooks/stockpilot","warehouse_id":"WH1","is_active":true,"created_at":"2026-09-01T09:14:22Z","updated_at":"2026-09-02T14:03:11Z"},{"id":43,"name":"Completed orders to fulfilment","event":"orders.completed","target_url":"https://ops.example.com/hooks/orders","is_active":false,"created_at":"2026-08-14T11:02:00Z","updated_at":"2026-09-01T06:30:41Z"}]}}}},"401":{"description":"Missing or invalid API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream service error"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/events":{"get":{"tags":["Webhooks"],"summary":"List available event types","description":"List the event types you can subscribe to, with a description of what triggers each.\n\nQuery this at runtime rather than hardcoding the list, so new event types become\navailable to your integration without a redeploy.\n\nSee **Webhook Delivery Contract** for the payload each event carries.","operationId":"get_webhook_events_webhooks_events_get","parameters":[{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Event types retrieved successfully","content":{"application/json":{"schema":{},"example":{"results":[{"event":"orders.completed","category":"orders","description":"An order reaches the completed state, by transition or by arriving already completed. Fires once per order."},{"event":"inventory.stock_changed","category":"inventory","description":"A product's stock quantity changes in any warehouse."}]}}}},"401":{"description":"Missing or invalid API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream service error"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/create":{"post":{"tags":["Webhooks"],"summary":"Create a webhook","description":"Create a webhook subscription.\n\n## Request Body\n* **name**: Human readable name for the webhook\n* **event**: Event type to subscribe to - `orders.completed` or `inventory.stock_changed`\n* **target_url**: HTTPS endpoint that receives deliveries. Must be publicly resolvable;\n  private, loopback, link-local and cloud-metadata addresses are rejected here and\n  again at delivery time. Redirects are not followed.\n* **secret**: Optional signing secret, used verbatim when supplied (min 16 characters).\n  Omit it and Stockpilot generates one.\n* **warehouse_id**: Optional. Restrict `inventory.stock_changed` deliveries to changes\n  in a single warehouse. Omit to receive events for all warehouses. The payload still\n  carries every warehouse in `quantities`; this filters what triggers a delivery.\n\n## Request Body Example\n```json\n{\n  \"name\": \"Stock changes to ERP\",\n  \"event\": \"inventory.stock_changed\",\n  \"target_url\": \"https://erp.example.com/hooks/stockpilot\",\n  \"secret\": \"a-secret-of-at-least-16-chars\",\n  \"warehouse_id\": \"WH1\"\n}\n```\n\n## Returns\nThe created webhook. The `id` is what arrives in the `X-Stockpilot-Webhook` header on\nevery delivery - store it if you route by webhook. The secret is never echoed back.\n\nSee **Webhook Delivery Contract** for signature verification and the retry schedule.","operationId":"create_webhook_webhooks_create_post","parameters":[{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookPayload"}}}},"responses":{"201":{"description":"Webhook created successfully","content":{"application/json":{"schema":{},"example":{"id":42,"name":"Stock changes to ERP","event":"inventory.stock_changed","target_url":"https://erp.example.com/hooks/stockpilot","warehouse_id":"WH1","is_active":true,"created_at":"2026-09-01T09:14:22Z","updated_at":"2026-09-02T14:03:11Z"}}}},"400":{"description":"Invalid payload - unreachable, non-HTTPS or disallowed target URL","content":{"application/json":{"example":{"detail":"target_url must be an HTTPS address that resolves publicly"}}}},"401":{"description":"Missing or invalid API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream service error"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/{webhook_id}":{"get":{"tags":["Webhooks"],"summary":"Get a webhook","description":"Retrieve a single webhook subscription.\n\nWebhooks are scoped to your organization. An ID belonging to another organization\nreturns `404`, the same as an ID that does not exist.\n\nThe signing secret is never returned.","operationId":"get_webhook_webhooks__webhook_id__get","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer","description":"Webhook ID","title":"Webhook Id"},"description":"Webhook ID"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Webhook retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponse"},"example":{"id":42,"name":"Stock changes to ERP","event":"inventory.stock_changed","target_url":"https://erp.example.com/hooks/stockpilot","warehouse_id":"WH1","is_active":true,"created_at":"2026-09-01T09:14:22Z","updated_at":"2026-09-02T14:03:11Z"}}}},"401":{"description":"Missing or invalid API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"404":{"description":"Webhook not found, or not owned by your organization","content":{"application/json":{"example":{"detail":"Webhook 42 not found"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream service error"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/{webhook_id}/update":{"post":{"tags":["Webhooks"],"summary":"Update a webhook","description":"Update a webhook subscription.\n\nOnly the fields you send are changed. Omitting `secret` leaves the current secret\nuntouched; sending one replaces it verbatim (min 16 characters).\n\nChanging `target_url` re-runs the address checks - private, loopback, link-local and\ncloud-metadata addresses are rejected.\n\n## Request Body Example\n```json\n{\n  \"name\": \"Stock changes to ERP (v2)\",\n  \"target_url\": \"https://erp.example.com/hooks/stockpilot/v2\"\n}\n```","operationId":"update_webhook_webhooks__webhook_id__update_post","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer","description":"Webhook ID","title":"Webhook Id"},"description":"Webhook ID"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookPayload"}}}},"responses":{"200":{"description":"Webhook updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponse"},"example":{"id":42,"name":"Stock changes to ERP","event":"inventory.stock_changed","target_url":"https://erp.example.com/hooks/stockpilot","warehouse_id":"WH1","is_active":true,"created_at":"2026-09-01T09:14:22Z","updated_at":"2026-09-02T14:03:11Z"}}}},"400":{"description":"Invalid payload - unreachable, non-HTTPS or disallowed target URL","content":{"application/json":{"example":{"detail":"target_url must be an HTTPS address that resolves publicly"}}}},"401":{"description":"Missing or invalid API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"404":{"description":"Webhook not found, or not owned by your organization","content":{"application/json":{"example":{"detail":"Webhook 42 not found"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream service error"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/{webhook_id}/delete":{"post":{"tags":["Webhooks"],"summary":"Delete a webhook","description":"Delete a webhook subscription.\n\nThis is a **soft delete**: the webhook stops delivering and its `is_active` becomes\n`false`, but the record and its delivery history are kept so past attempts remain\navailable for debugging. It continues to appear in `GET /webhooks`.\n\nReverse it with `POST /webhooks/{id}/reactivate`.\n\nReturns `204` with no response body.","operationId":"delete_webhook_webhooks__webhook_id__delete_post","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer","description":"Webhook ID","title":"Webhook Id"},"description":"Webhook ID"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"204":{"description":"Webhook deactivated successfully. No response body"},"401":{"description":"Missing or invalid API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"404":{"description":"Webhook not found, or not owned by your organization","content":{"application/json":{"example":{"detail":"Webhook 42 not found"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream service error"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/{webhook_id}/reactivate":{"post":{"tags":["Webhooks"],"summary":"Reactivate a webhook","description":"Reactivate a webhook that was deleted or auto-deactivated.\n\nStockpilot auto-deactivates a webhook after 5 fully-failed deliveries within 24 hours.\nFix your endpoint first, confirm with `POST /webhooks/{id}/test`, then reactivate -\notherwise it will simply deactivate again.\n\nReactivating does not replay missed deliveries.","operationId":"reactivate_webhook_webhooks__webhook_id__reactivate_post","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer","description":"Webhook ID","title":"Webhook Id"},"description":"Webhook ID"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Webhook reactivated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponse"},"example":{"id":42,"name":"Stock changes to ERP","event":"inventory.stock_changed","target_url":"https://erp.example.com/hooks/stockpilot","warehouse_id":"WH1","is_active":true,"created_at":"2026-09-01T09:14:22Z","updated_at":"2026-09-02T14:03:11Z"}}}},"401":{"description":"Missing or invalid API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"404":{"description":"Webhook not found, or not owned by your organization","content":{"application/json":{"example":{"detail":"Webhook 42 not found"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream service error"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/{webhook_id}/test":{"post":{"tags":["Webhooks"],"summary":"Send a test delivery","description":"Send a sample envelope to the webhook's `target_url` and return what your endpoint\nreplied with.\n\nThe sample carries the same headers and signature as a real delivery, so this is the\nfastest way to check your signature verification. The response is passed through as\nreceived - status code, body and all - rather than normalised, so you see exactly what\nyour endpoint returned.\n\nA test delivery is recorded in `GET /webhooks/{id}/deliveries` alongside real attempts,\nand does not count towards the auto-deactivation threshold.","operationId":"test_webhook_webhooks__webhook_id__test_post","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer","description":"Webhook ID","title":"Webhook Id"},"description":"Webhook ID"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Test delivery attempted. The body is what your endpoint returned","content":{"application/json":{"schema":{},"example":{"delivery_id":"9c1f0f8e5b7a4c2d8e3f1a2b3c4d5e6f","status":"succeeded","response_code":200,"response_body":"{\"ok\": true}"}}}},"401":{"description":"Missing or invalid API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"404":{"description":"Webhook not found, or not owned by your organization","content":{"application/json":{"example":{"detail":"Webhook 42 not found"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream service error"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/{webhook_id}/deliveries":{"get":{"tags":["Webhooks"],"summary":"List recent deliveries","description":"List recent delivery attempts for a webhook, newest first - for debugging a failing\nintegration.\n\nEach entry carries the `status`, the `response_code` your endpoint returned, and an\n`error` when the request never completed (timeout, DNS failure, rejected address).\n\nRetried attempts share a `delivery_id` and differ by `attempt`, so a single event that\nwas retried appears as several rows. Delivery history is kept after a webhook is\ndeleted or auto-deactivated.\n\n## Parameters\n* **page**: Page number for pagination (starts at 1)\n* **page_size**: Number of attempts per page (max 100)","operationId":"get_webhook_deliveries_webhooks__webhook_id__deliveries_get","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer","description":"Webhook ID","title":"Webhook Id"},"description":"Webhook ID"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number for pagination","default":1,"title":"Page"},"description":"Page number for pagination"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Number of items per page","default":100,"title":"Page Size"},"description":"Number of items per page"},{"name":"x-client-id","in":"header","required":false,"schema":{"type":"string","description":"Your API client ID","title":"X-Client-Id"},"description":"Your API client ID"},{"name":"x-client-secret","in":"header","required":false,"schema":{"type":"string","description":"Your API client secret","title":"X-Client-Secret"},"description":"Your API client secret"}],"responses":{"200":{"description":"Deliveries retrieved successfully","content":{"application/json":{"schema":{},"example":{"count":3,"current_page":1,"total_pages":1,"results":[{"delivery_id":"9c1f0f8e5b7a4c2d8e3f1a2b3c4d5e6f","event":"inventory.stock_changed","attempted_at":"2026-09-02T14:03:12Z","status":"failed","response_code":502,"error":"Endpoint returned 502","attempt":3},{"delivery_id":"9c1f0f8e5b7a4c2d8e3f1a2b3c4d5e6f","event":"inventory.stock_changed","attempted_at":"2026-09-02T14:02:41Z","status":"failed","error":"Read timeout after 10s","attempt":2}]}}}},"401":{"description":"Missing or invalid API credentials","content":{"application/json":{"example":{"detail":"Missing API credentials"}}}},"404":{"description":"Webhook not found, or not owned by your organization","content":{"application/json":{"example":{"detail":"Webhook 42 not found"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Upstream service error"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AddBundleItemsPayload":{"properties":{"items":{"items":{"$ref":"#/components/schemas/BundleItemInput"},"type":"array","title":"Items","description":"List of items to add to the bundle"}},"type":"object","required":["items"],"title":"AddBundleItemsPayload","example":{"items":[{"product_id":456,"quantity":2},{"product_id":789,"quantity":1}]}},"AddOrderItemPayload":{"properties":{"product_id":{"type":"integer","title":"Product Id","description":"Product ID to add to order","example":123},"quantity":{"type":"integer","exclusiveMinimum":0.0,"title":"Quantity","description":"Number of items to add (must be > 0)","example":2},"retail_price":{"type":"number","title":"Retail Price","description":"Price per item","example":29.99},"vat_rate":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Vat Rate","description":"VAT rate percentage (defaults to 21)","default":21,"example":21}},"type":"object","required":["product_id","quantity","retail_price"],"title":"AddOrderItemPayload","example":{"product_id":123,"quantity":2,"retail_price":29.99,"vat_rate":21}},"AddOrderItemResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Success message"},"item_id":{"type":"integer","title":"Item Id","description":"Added item ID"},"product_name":{"type":"string","title":"Product Name","description":"Product name with variant info"},"quantity":{"type":"integer","title":"Quantity","description":"Added quantity"},"retail_price":{"type":"number","title":"Retail Price","description":"Price per item"},"total_price":{"type":"number","title":"Total Price","description":"Total price for the quantity"}},"type":"object","required":["message","item_id","product_name","quantity","retail_price","total_price"],"title":"AddOrderItemResponse","example":{"item_id":456,"message":"Order item added successfully","product_name":"Product Name - Variant","quantity":2,"retail_price":29.99,"total_price":59.98}},"Address":{"properties":{"firstname":{"type":"string","title":"Firstname","example":"John"},"lastname":{"type":"string","title":"Lastname","example":"Doe"},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company","example":"ACME Corp"},"zipcode":{"type":"string","title":"Zipcode","example":"1234AB"},"street":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Street","example":"Main Street"},"housenumber":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Housenumber","example":"12A"},"suffix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suffix","example":"bis"},"address_2":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address 2","example":"Unit 5"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City","example":"Amsterdam"},"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region","example":"Noord-Holland"},"country":{"type":"string","title":"Country","example":"NL"}},"type":"object","required":["firstname","lastname","zipcode","country"],"title":"Address"},"BackorderItem":{"properties":{"item_id":{"type":"integer","title":"Item Id","description":"Order item ID"},"quantity":{"type":"integer","exclusiveMinimum":0.0,"title":"Quantity","description":"Quantity to move to/from backorder"}},"type":"object","required":["item_id","quantity"],"title":"BackorderItem"},"Body_send_invoice_invoices_send_post":{"properties":{"order_pk":{"type":"integer","title":"Order Pk","description":"Order primary key"},"invoice":{"anyOf":[{"type":"string","format":"binary"},{"type":"null"}],"title":"Invoice","description":"Invoice PDF file to upload"}},"type":"object","required":["order_pk"],"title":"Body_send_invoice_invoices_send_post"},"Body_set_inventory_image_inventory__item_id__set_image_post":{"properties":{"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url","description":"Image URL to fetch"},"image_file":{"anyOf":[{"type":"string","format":"binary"},{"type":"null"}],"title":"Image File","description":"Image file to upload"}},"type":"object","title":"Body_set_inventory_image_inventory__item_id__set_image_post"},"Body_set_product_image_products__product_id__set_image_post":{"properties":{"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url","description":"Remote image URL"},"image_file":{"anyOf":[{"type":"string","format":"binary"},{"type":"null"}],"title":"Image File","description":"Image file to upload"}},"type":"object","title":"Body_set_product_image_products__product_id__set_image_post"},"BundleItem":{"properties":{"product_id":{"type":"integer","title":"Product Id","description":"Product ID in the bundle"},"product_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Product Name","description":"Product name"},"product_sku":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Product Sku","description":"Product SKU"},"product_barcode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Product Barcode","description":"Product barcode"},"quantity_in_bundle":{"type":"integer","title":"Quantity In Bundle","description":"Quantity of this product in the bundle"}},"type":"object","required":["product_id","quantity_in_bundle"],"title":"BundleItem"},"BundleItemInput":{"properties":{"product_id":{"type":"integer","title":"Product Id","description":"Product ID to add to the bundle"},"quantity":{"type":"integer","exclusiveMinimum":0.0,"title":"Quantity","description":"Quantity of the product in the bundle"}},"type":"object","required":["product_id","quantity"],"title":"BundleItemInput"},"BundleResponse":{"properties":{"id":{"type":"integer","title":"Id","description":"Bundle ID"},"name":{"type":"string","title":"Name","description":"Bundle name"},"sku":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sku","description":"Bundle SKU"},"barcode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Barcode","description":"Bundle barcode"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Bundle description"},"quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Quantity","description":"Available bundle quantity"},"purchase_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Purchase Price","description":"Bundle purchase price"},"ws_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ws Price","description":"Bundle wholesale price"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Whether the bundle is active"},"is_b2b":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is B2B","description":"Whether the bundle is B2B"},"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url","description":"Bundle image URL"},"items":{"items":{"$ref":"#/components/schemas/BundleItem"},"type":"array","title":"Items","description":"Items included in the bundle"}},"type":"object","required":["id","name"],"title":"BundleResponse","example":{"barcode":"1234567890123","description":"Complete gaming setup with headphones and mouse","id":123,"image_url":"https://example.com/bundle-image.jpg","is_active":true,"is_b2b":false,"items":[{"product_barcode":"1111111111111","product_id":456,"product_name":"Gaming Headphones","product_sku":"HEADPHONES-GAMING","quantity_in_bundle":1},{"product_barcode":"2222222222222","product_id":789,"product_name":"Gaming Mouse","product_sku":"MOUSE-GAMING","quantity_in_bundle":1}],"name":"Gaming Setup Bundle","purchase_price":75.0,"quantity":50,"sku":"BUNDLE-GAMING-001","ws_price":120.0}},"CancelOrderPayload":{"properties":{"order_pk":{"type":"integer","title":"Order Pk","description":"Order primary key"},"reason_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason Code","description":"Cancellation reason code","default":"REQUESTED_BY_CUSTOMER"}},"type":"object","required":["order_pk"],"title":"CancelOrderPayload"},"CancelOrderResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Success message"},"order_id":{"type":"string","title":"Order Id","description":"Cancelled order ID"},"is_success":{"type":"boolean","title":"Is Success","description":"Whether cancellation was successful"},"no_items":{"type":"boolean","title":"No Items","description":"Whether order had no items"}},"type":"object","required":["message","order_id","is_success","no_items"],"title":"CancelOrderResponse"},"CancellationRequestItem":{"properties":{"order_item_id":{"type":"string","title":"Order Item Id","description":"Order item ID"},"quantity":{"type":"integer","title":"Quantity","description":"Item quantity"},"quantity_cancelled":{"type":"integer","title":"Quantity Cancelled","description":"Quantity cancelled"},"has_cancellation_request":{"type":"boolean","title":"Has Cancellation Request","description":"Whether item has cancellation request"}},"type":"object","required":["order_item_id","quantity","quantity_cancelled","has_cancellation_request"],"title":"CancellationRequestItem"},"CancellationRequestResponse":{"properties":{"order_id":{"type":"string","title":"Order Id","description":"Order ID"},"order_pk":{"type":"integer","title":"Order Pk","description":"Order primary key"},"has_cancellation_request":{"type":"boolean","title":"Has Cancellation Request","description":"Whether order has cancellation request"},"items":{"items":{"$ref":"#/components/schemas/CancellationRequestItem"},"type":"array","title":"Items","description":"Order items with cancellation status"}},"type":"object","required":["order_id","order_pk","has_cancellation_request","items"],"title":"CancellationRequestResponse"},"ChannelHandle":{"type":"string","enum":["channel","amazon","bol","mirakl","etsy","kaufland","shopify","woocommerce","b2b-portal"],"title":"ChannelHandle"},"CreateBrandPayload":{"properties":{"name":{"type":"string","title":"Name"}},"type":"object","required":["name"],"title":"CreateBrandPayload"},"CreateBundlePayload":{"properties":{"name":{"type":"string","title":"Name","description":"Bundle name"},"sku":{"type":"string","title":"Sku","description":"Bundle SKU"},"barcode":{"type":"string","title":"Barcode","description":"Bundle barcode"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Bundle description"},"purchase_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Purchase Price","description":"Bundle purchase price"},"wholesale_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Wholesale Price","description":"Bundle wholesale price"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Whether the bundle is active","default":true},"is_b2b":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is B2B","description":"Whether the bundle is B2B","default":false}},"type":"object","required":["name","sku","barcode"],"title":"CreateBundlePayload","example":{"barcode":"1234567890123","description":"Complete gaming setup with headphones and mouse","is_active":true,"is_b2b":false,"name":"Gaming Setup Bundle","purchase_price":75.0,"sku":"BUNDLE-GAMING-001","wholesale_price":120.0}},"CreateCategoryPayload":{"properties":{"name":{"type":"string","title":"Name"}},"type":"object","required":["name"],"title":"CreateCategoryPayload"},"CreateInventoryPayload":{"properties":{"product_id":{"type":"integer","title":"Product Id"},"sku":{"type":"string","title":"Sku"},"item_name":{"type":"string","title":"Item Name"},"barcode":{"type":"string","title":"Barcode"},"barcode_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Barcode Type","default":"EAN"},"quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Quantity","default":0},"moq":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Moq","default":1},"stock_threshold":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Stock Threshold","default":1},"purchase_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Purchase Price","default":0.0},"wholesale_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Wholesale Price","default":0.0},"base_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Base Price","default":0.0},"weight":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Weight"},"length":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Length","default":0.0},"width":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Width","default":0.0},"height":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Height","default":0.0},"condition":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Condition","default":"NEW"},"vat_class":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vat Class","default":"standard_rate"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","default":true}},"type":"object","required":["product_id","sku","item_name","barcode"],"title":"CreateInventoryPayload"},"CreateOrderLineItem":{"properties":{"product_id":{"type":"string","title":"Product Id","example":"1"},"price":{"type":"string","title":"Price","example":"9.99"},"quantity":{"type":"integer","title":"Quantity","example":2}},"type":"object","required":["product_id","price","quantity"],"title":"CreateOrderLineItem"},"CreateOrderPayload":{"properties":{"billing":{"$ref":"#/components/schemas/Address"},"shipping":{"$ref":"#/components/schemas/Address"},"customer_email":{"type":"string","title":"Customer Email","example":"jane@example.com"},"customer_phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer Phone","example":"+31612345678"},"vat_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vat Number","example":"NL123456789B01"},"shipping_total":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shipping Total","default":"5.99","example":"5.99"},"shipping_method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shipping Method","example":"PostNL Standard"},"customer_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer Note","example":"Please deliver in the morning."},"line_items":{"items":{"$ref":"#/components/schemas/CreateOrderLineItem"},"type":"array","title":"Line Items"}},"type":"object","required":["billing","shipping","customer_email","line_items"],"title":"CreateOrderPayload"},"CreateProductPayload":{"properties":{"title":{"type":"string","title":"Title","description":"Product title","example":"Premium Wireless Headphones"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Product description","example":"High-quality wireless headphones with noise cancellation"},"brand":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Brand","description":"Brand ID. If not provided or empty, a default 'Brandless' brand will be created for your organization","example":123},"category":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Category","description":"Category ID. If not provided or empty, a default 'No category' category will be created for your organization","example":456},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Whether the product is active","default":true,"example":true}},"type":"object","required":["title"],"title":"CreateProductPayload"},"CreatePurchaseOrderParcelPayload":{"properties":{"items":{"anyOf":[{"items":{"$ref":"#/components/schemas/PurchaseOrderParcelItem"},"type":"array"},{"type":"null"}],"title":"Items","description":"Lines that arrived in this parcel. Omit entirely to take everything still outstanding on the purchase order"},"reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reference","description":"Reference stored on the parcel, e.g. a packing slip number. Auto-generated when omitted"},"async":{"type":"boolean","title":"Async","description":"When true (default) the parcel is queued and the endpoint returns 202 with projected quantities. Set to false to block until stock has actually moved and receive 200 with confirmed quantities","default":true}},"type":"object","title":"CreatePurchaseOrderParcelPayload","examples":[{"summary":"Partial parcel (async)","value":{"items":[{"product_id":456,"quantity":4},{"quantity":2,"sku":"ABC-1"}],"reference":"PACKSLIP-88213"}},{"summary":"Full parcel - everything still outstanding","value":{"reference":"PACKSLIP-88213"}},{"summary":"Synchronous partial parcel","value":{"async":false,"items":[{"product_id":456,"quantity":4}],"reference":"PACKSLIP-88213"}}]},"CreatePurchaseOrderPayload":{"properties":{"supplier_id":{"type":"integer","title":"Supplier Id","description":"Supplier ID"},"items":{"items":{"$ref":"#/components/schemas/PurchaseOrderItemPayload"},"type":"array","title":"Items","description":"List of items to order with flexible product identification"},"order_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Order Note","description":"Optional order note"},"shipping_cost":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Shipping Cost","description":"Shipping cost","default":0},"expected_delivery":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expected Delivery","description":"Expected delivery date (ISO format)"},"delivery_warehouse_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Delivery Warehouse Id","description":"Warehouse the goods are delivered to. Defaults to the organization's default warehouse when omitted"},"processed_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Processed By","description":"Who processed the order","default":"API"}},"type":"object","required":["supplier_id","items"],"title":"CreatePurchaseOrderPayload","examples":[{"summary":"Mixed identifier types","value":{"delivery_warehouse_id":3,"expected_delivery":"2026-02-28T10:00:00Z","items":[{"product_id":456,"purchase_price":25.5,"quantity":10,"supplier_reference":"SUP-REF-123"},{"barcode":"123456789","purchase_price":12.25,"quantity":5},{"quantity":20,"sku":"WIDGET-001"}],"order_note":"Mixed identifier example","processed_by":"API User","shipping_cost":15.5,"supplier_id":123}},{"summary":"Barcode-only order","value":{"items":[{"barcode":"987654321","quantity":15},{"barcode":"567890123","purchase_price":33.75,"quantity":8}],"order_note":"Barcode scanner integration","supplier_id":123}}]},"CreateWebhookPayload":{"properties":{"name":{"type":"string","title":"Name","description":"Human readable name for this webhook"},"event":{"$ref":"#/components/schemas/WebhookEvent","description":"Event type that triggers this webhook"},"target_url":{"type":"string","title":"Target Url","description":"HTTPS endpoint that receives the delivery. Must be publicly resolvable"},"secret":{"anyOf":[{"type":"string","minLength":16},{"type":"null"}],"title":"Secret","description":"Signing secret used for X-Stockpilot-Signature. Used verbatim when supplied (min 16 characters); omit it and Stockpilot generates one"},"warehouse_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Warehouse Id","description":"Restrict inventory.stock_changed deliveries to changes in a single warehouse. Omit to receive events for all warehouses"}},"type":"object","required":["name","event","target_url"],"title":"CreateWebhookPayload","example":{"event":"inventory.stock_changed","name":"Stock changes to ERP","secret":"a-secret-of-at-least-16-chars","target_url":"https://erp.example.com/hooks/stockpilot","warehouse_id":"WH1"}},"DeleteOrderItemPayload":{"properties":{"book_back_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Book Back Quantity","description":"Quantity to book back to inventory (defaults to item quantity)"},"book_back":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Book Back","description":"Whether to book back inventory (default True)","default":true}},"type":"object","title":"DeleteOrderItemPayload","example":{"book_back":true,"book_back_quantity":5}},"DeleteOrderItemResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Success message"},"item_id":{"type":"integer","title":"Item Id","description":"Deleted item ID"},"book_back_quantity":{"type":"integer","title":"Book Back Quantity","description":"Quantity booked back to inventory"}},"type":"object","required":["message","item_id","book_back_quantity"],"title":"DeleteOrderItemResponse","example":{"book_back_quantity":5,"item_id":12345,"message":"Order item deleted successfully"}},"DeleteOrderPayload":{"properties":{"book_back":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Book Back","description":"Whether to book back inventory (default True)","default":true}},"type":"object","title":"DeleteOrderPayload","example":{"book_back":true}},"DeleteOrderResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Success message"},"order_id":{"type":"string","title":"Order Id","description":"Deleted order number"},"order_pk":{"type":"integer","title":"Order Pk","description":"Deleted order primary key"},"book_back_quantity":{"type":"integer","title":"Book Back Quantity","description":"Total quantity booked back to inventory"}},"type":"object","required":["message","order_id","order_pk","book_back_quantity"],"title":"DeleteOrderResponse","example":{"book_back_quantity":15,"message":"Order deleted successfully","order_id":"ORD-12345","order_pk":67890}},"FulfilOrderPayload":{"properties":{"order_pk":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Order Pk","description":"Order primary key (integer)"},"order_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Order Number","description":"Order number (string)"},"fulfilled_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fulfilled At","description":"ISO datetime string"},"service":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Service","description":"Shipping service provider name (e.g., Sendcloud)"},"carrier_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Carrier Code","description":"Carrier code (e.g., POSTNL)"},"carrier_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Carrier Name","description":"Human-readable carrier name (e.g., PostNL)"},"shipping_method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shipping Method","description":"Shipping method (e.g., Standard, Evening)"},"shipment_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shipment Type","description":"Shipment type (e.g., Parcel, Mailbox)"},"tracking_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tracking Code","description":"Tracking number"},"tracking_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tracking Url","description":"Tracking URL","default":"https://www.no-tracking-url.com/"},"items":{"anyOf":[{"items":{"$ref":"#/components/schemas/FulfilmentItem"},"type":"array"},{"type":"null"}],"title":"Items"}},"type":"object","title":"FulfilOrderPayload"},"FulfilmentItem":{"properties":{"sku":{"type":"string","title":"Sku"},"quantity":{"type":"integer","title":"Quantity"}},"type":"object","required":["sku","quantity"],"title":"FulfilmentItem"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"InventoryItemResponse":{"properties":{"id":{"type":"integer","title":"Id","description":"Internal Stockpilot product ID"},"product_id":{"type":"integer","title":"Product Id","description":"Product reference ID"},"item_name":{"type":"string","title":"Item Name","description":"Product name/title"},"sku":{"type":"string","title":"Sku","description":"Stock Keeping Unit"},"barcode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Barcode","description":"Product barcode (EAN)"},"barcode_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Barcode Type","description":"Type of barcode (e.g., 'EAN')"},"image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image","description":"Product image URL"},"bin_location":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Bin Location","description":"Array of bin location strings using format like ['A1-001-01', 'B2-003-05'] or null if no locations assigned"},"threshold":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Threshold","description":"Threshold setting using format like '5u' for 5 units or '33w' for 33 weeks"},"condition":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Condition","description":"Product condition (e.g., 'NEW', 'USED')"},"hs_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hs Code","description":"Harmonized System code for customs"},"country_origin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country Origin","description":"Country of origin (ISO 2-letter code)"},"weight":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Weight","description":"Product weight as string (default in grams)"},"item_length":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Item Length","description":"Length in millimeters"},"item_width":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Item Width","description":"Width in millimeters"},"item_height":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Item Height","description":"Height in millimeters"},"vat_class":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vat Class","description":"VAT class name (e.g., 'standard_rate')"},"purchase_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Purchase Price","description":"Purchase/cost price"},"wholesale_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Wholesale Price","description":"Wholesale price"},"base_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Base Price","description":"Base selling price"},"retail_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Retail Price","description":"Retail price"},"quantity":{"type":"integer","title":"Quantity","description":"Current stock quantity"},"moq":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Moq","description":"Minimum order quantity"},"stock_threshold":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Stock Threshold","description":"Minimum stock amount for alerts (legacy field, see 'threshold' for new format)"},"backorder_amount":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Backorder Amount","description":"Quantity on backorder"},"reserved_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reserved Quantity","description":"Quantity reserved for orders"},"incoming_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Incoming Quantity","description":"Quantity expected to arrive"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Whether the product is active"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At","description":"Creation timestamp (ISO format)"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At","description":"Last update timestamp (ISO format)"}},"type":"object","required":["id","product_id","item_name","sku","quantity"],"title":"InventoryItemResponse","description":"Enhanced inventory item response model with comprehensive field grouping for ReDoc documentation.\n\n## Field Groups\n\n### Product Identification\nBasic product identification fields\n\n### Location & Threshold\nBin location management and threshold settings\n\n### Product Details\nPhysical product characteristics\n\n### Pricing\nAll pricing-related fields\n\n### Stock Management\nStock levels and management settings\n\n### Status & Timestamps\nStatus and audit trail information","example":{"backorder_amount":0,"barcode":"5901234123457","barcode_type":"EAN","base_price":99.99,"bin_location":["A1-001-01","B2-003-05"],"condition":"NEW","country_origin":"CN","created_at":"2026-01-15T10:30:00Z","hs_code":"8518300000","id":12345,"image":"https://example.com/image.jpg","incoming_quantity":100,"is_active":true,"item_height":80.0,"item_length":200.0,"item_name":"Premium Wireless Headphones","item_width":180.0,"moq":1,"product_id":456,"purchase_price":45.0,"quantity":150,"reserved_quantity":25,"retail_price":129.99,"sku":"WH-PREM-001","stock_threshold":10,"threshold":"5u","updated_at":"2026-02-19T14:45:00Z","vat_class":"standard_rate","weight":"250.0","wholesale_price":75.0}},"MoveFromBackorderPayload":{"properties":{"get_out_items":{"items":{"$ref":"#/components/schemas/BackorderItem"},"type":"array","title":"Get Out Items","description":"List of items to move from backorder"}},"type":"object","required":["get_out_items"],"title":"MoveFromBackorderPayload","example":{"get_out_items":[{"item_id":123,"quantity":3},{"item_id":456,"quantity":1}]}},"MoveFromBackorderResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Operation result message"},"total_moved":{"type":"integer","title":"Total Moved","description":"Total number of items moved"},"backorder_status":{"type":"string","title":"Backorder Status","description":"Current backorder status"},"results":{"items":{},"type":"array","title":"Results","description":"List of detailed operation results"}},"type":"object","required":["message","total_moved","backorder_status","results"],"title":"MoveFromBackorderResponse","example":{"backorder_status":"none","message":"Successfully moved 2 item(s) out of backorder","results":[{"item_id":123,"quantity_moved":2,"remaining_backorder":0,"success":true}],"total_moved":2}},"MoveToBackorderPayload":{"properties":{"backorder_items":{"items":{"$ref":"#/components/schemas/BackorderItem"},"type":"array","title":"Backorder Items","description":"List of items to move to backorder"}},"type":"object","required":["backorder_items"],"title":"MoveToBackorderPayload","example":{"backorder_items":[{"item_id":123,"quantity":5},{"item_id":456,"quantity":2}]}},"MoveToBackorderResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Operation result message"},"items_processed":{"type":"integer","title":"Items Processed","description":"Number of items processed"},"backorder_status":{"type":"string","title":"Backorder Status","description":"Current backorder status"},"backorder_results":{"items":{},"type":"array","title":"Backorder Results","description":"List of backorder operation results"}},"type":"object","required":["message","items_processed","backorder_status","backorder_results"],"title":"MoveToBackorderResponse","example":{"backorder_results":[{"item_id":123,"new_backorder_qty":2,"quantity_moved":2}],"backorder_status":"partial","items_processed":2,"message":"Successfully moved 2 item(s) to backorder"}},"OrderPublicStatus":{"type":"string","enum":["open","pending","completed","cancelled"],"title":"OrderPublicStatus"},"OrderStatusUpdatePayload":{"properties":{"status":{"$ref":"#/components/schemas/OrderPublicStatus","description":"New status for the order"}},"type":"object","required":["status"],"title":"OrderStatusUpdatePayload"},"ProductOrderHistoryFilters":{"properties":{"start_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Date","description":"Start date filter used (YYYY-MM-DD)"},"end_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End Date","description":"End date filter used (YYYY-MM-DD)"}},"type":"object","title":"ProductOrderHistoryFilters"},"ProductOrderHistoryOrder":{"properties":{"id":{"type":"integer","title":"Id","description":"Order call ID"},"order_id":{"type":"string","title":"Order Id","description":"Order number/ID"},"channel":{"type":"string","title":"Channel","description":"Sales channel name"},"channel_order_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Channel Order Id","description":"Channel order ID"},"created_at":{"type":"string","title":"Created At","description":"Order creation timestamp (ISO format)"},"quantity":{"type":"integer","title":"Quantity","description":"Quantity of this product in the order"},"customer_name":{"type":"string","title":"Customer Name","description":"Customer name"},"customer_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer Email","description":"Customer email address"},"ship_street":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ship Street","description":"Shipping street address"},"ship_city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ship City","description":"Shipping city"},"ship_country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ship Country","description":"Shipping country"}},"type":"object","required":["id","order_id","channel","created_at","quantity","customer_name"],"title":"ProductOrderHistoryOrder"},"ProductOrderHistoryProduct":{"properties":{"id":{"type":"integer","title":"Id","description":"Product ID"},"sku":{"type":"string","title":"Sku","description":"Product SKU"},"product_name":{"type":"string","title":"Product Name","description":"Product name"},"barcode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Barcode","description":"Product barcode"}},"type":"object","required":["id","sku","product_name"],"title":"ProductOrderHistoryProduct"},"ProductOrderHistoryResponse":{"properties":{"product":{"$ref":"#/components/schemas/ProductOrderHistoryProduct","description":"Product information"},"filters":{"$ref":"#/components/schemas/ProductOrderHistoryFilters","description":"Applied filters"},"total_orders":{"type":"integer","title":"Total Orders","description":"Total number of orders found"},"orders":{"items":{"$ref":"#/components/schemas/ProductOrderHistoryOrder"},"type":"array","title":"Orders","description":"List of orders containing this product"}},"type":"object","required":["product","filters","total_orders","orders"],"title":"ProductOrderHistoryResponse","description":"Response model for product order history analytics.\n\nReturns comprehensive order history for a specific product with customer details,\nchannel information, and flexible date filtering.","example":{"filters":{"end_date":"2026-01-31","start_date":"2026-01-01"},"orders":[{"channel":"Shopify Store","channel_order_id":"SP-789","created_at":"2026-01-15T10:30:00Z","customer_email":"john.doe@example.com","customer_name":"John Doe","id":12345,"order_id":"ORD-2026-001","quantity":2,"ship_city":"Amsterdam","ship_country":"NL","ship_street":"123 Main St"},{"channel":"Bol.com","channel_order_id":"BOL-456","created_at":"2026-01-20T14:45:00Z","customer_email":"jane.smith@example.com","customer_name":"Jane Smith","id":12346,"order_id":"ORD-2026-002","quantity":1,"ship_city":"Rotterdam","ship_country":"NL","ship_street":"456 Oak Ave"}],"product":{"barcode":"5901234123457","id":456,"product_name":"Premium Wireless Headphones","sku":"HEADPHONES-PRO"},"total_orders":15}},"PurchaseOrderItemPayload":{"properties":{"product_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Product Id","description":"Product database ID (legacy method)"},"barcode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Barcode","description":"Product barcode/EAN for lookup"},"sku":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sku","description":"Product SKU for lookup"},"quantity":{"type":"integer","exclusiveMinimum":0.0,"title":"Quantity","description":"Quantity to order"},"purchase_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Purchase Price","description":"Purchase price per unit (uses product default if not provided)"},"supplier_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier Reference","description":"Supplier reference code"}},"type":"object","required":["quantity"],"title":"PurchaseOrderItemPayload","examples":[{"summary":"Using Product ID (legacy)","value":{"product_id":456,"purchase_price":25.5,"quantity":10,"supplier_reference":"SUP-REF-123"}},{"summary":"Using Barcode","value":{"barcode":"123456789","purchase_price":12.25,"quantity":5}},{"summary":"Using SKU","value":{"quantity":20,"sku":"WIDGET-001"}}]},"PurchaseOrderParcelItem":{"properties":{"product_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Product Id","description":"Product database ID"},"sku":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sku","description":"Product SKU for lookup"},"barcode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Barcode","description":"Product barcode/EAN for lookup"},"quantity":{"type":"integer","exclusiveMinimum":0.0,"title":"Quantity","description":"Quantity in this parcel for this line. Cannot exceed the quantity still outstanding on the purchase order line"}},"type":"object","required":["quantity"],"title":"PurchaseOrderParcelItem","example":{"product_id":456,"quantity":4}},"RequestLabelPayload":{"properties":{"template_id":{"anyOf":[{"type":"integer"},{"type":"string"}],"title":"Template Id"},"carrier_id":{"anyOf":[{"type":"integer"},{"type":"string"}],"title":"Carrier Id"},"order_pk":{"type":"integer","title":"Order Pk"}},"type":"object","required":["template_id","carrier_id","order_pk"],"title":"RequestLabelPayload"},"RetrieveLabelPayload":{"properties":{"entity_id":{"type":"string","title":"Entity Id"},"service":{"type":"string","title":"Service"},"order_pk":{"type":"integer","title":"Order Pk"}},"type":"object","required":["entity_id","service","order_pk"],"title":"RetrieveLabelPayload"},"SwapOrderItemPayload":{"properties":{"item_id":{"type":"integer","title":"Item Id","description":"ID of the order item to modify","example":456},"new_product_id":{"type":"integer","title":"New Product Id","description":"ID of the new product to swap to","example":789}},"type":"object","required":["item_id","new_product_id"],"title":"SwapOrderItemPayload","example":{"item_id":456,"new_product_id":789}},"SwapOrderItemResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Success message"},"item_id":{"type":"integer","title":"Item Id","description":"Modified item ID"},"old_product_name":{"type":"string","title":"Old Product Name","description":"Previous product name"},"new_product_name":{"type":"string","title":"New Product Name","description":"New product name"}},"type":"object","required":["message","item_id","old_product_name","new_product_name"],"title":"SwapOrderItemResponse","example":{"item_id":456,"message":"Order item product swapped successfully","new_product_name":"New Product Name - Variant","old_product_name":"Old Product Name"}},"SyncChannelListingsPayload":{"properties":{"channel":{"type":"string","title":"Channel","description":"Channel handle, as returned in the `handle` field of `GET /sales-channels`. Supported: amazon, ankorstore, bigcommerce, bol, ccvshop, cdiscount, ebay, etsy, faire, floriday, fnacdarty, kaufland, lightspeed, magento, mijnwebwinkel, mirakl, opencart, orderchamp, otto, prestashop, shopify, shopware, squarespace, target, temu, tiktok, valkaspos, walmart, woocommerce"},"channel_id":{"type":"integer","title":"Channel Id","description":"ID of the connected channel, as returned in the `id` field of `GET /sales-channels`"},"full_sync":{"type":"boolean","title":"Full Sync","description":"Re-import every listing instead of only what changed since the last sync. Slower and heavier on the channel's rate limits - leave false unless you are reconciling after a suspected gap","default":false}},"type":"object","required":["channel","channel_id"],"title":"SyncChannelListingsPayload","example":{"channel":"shopify","channel_id":1,"full_sync":false}},"UpdateBundleItemPayload":{"properties":{"quantity":{"type":"integer","exclusiveMinimum":0.0,"title":"Quantity","description":"New quantity of the product in the bundle"}},"type":"object","required":["quantity"],"title":"UpdateBundleItemPayload","example":{"quantity":3}},"UpdateBundlePayload":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Bundle name"},"sku":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sku","description":"Bundle SKU"},"barcode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Barcode","description":"Bundle barcode"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Bundle description"},"purchase_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Purchase Price","description":"Bundle purchase price"},"ws_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ws Price","description":"Bundle wholesale price"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Whether the bundle is active"},"is_b2b":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is B2B","description":"Whether the bundle is B2B"}},"type":"object","title":"UpdateBundlePayload","example":{"description":"Updated complete gaming setup","is_active":true,"name":"Updated Gaming Bundle","purchase_price":80.0,"ws_price":130.0}},"UpdateCustomerPayload":{"properties":{"business_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Name","description":"Business name"},"invoice_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invoice Email","description":"Invoice email address"},"orders_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Orders Email","description":"Orders email address"},"phone_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone Number","description":"Phone number"},"payment_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Status","description":"Payment status: good, overdue, blocked"},"payment_terms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Payment Terms","description":"Payment terms in days"},"vat_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vat Number","description":"VAT number"},"special_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Special Notes","description":"Special notes about the customer"}},"type":"object","title":"UpdateCustomerPayload","example":{"business_name":"Updated Corp Name","invoice_email":"new-billing@acme.com","orders_email":"new-orders@acme.com","payment_status":"good","payment_terms":14,"phone_number":"+31 20 999 8888","special_notes":"Updated customer notes","vat_number":"NL987654321B01"}},"UpdateForwardingPayload":{"properties":{"register_order_id":{"type":"string","title":"Register Order Id","description":"Order number from marketplace (will be prefixed with 'FWD-')","example":"MP-12345"},"source":{"type":"string","title":"Source","description":"Source identifier (typically 'api')","example":"api"}},"type":"object","required":["register_order_id","source"],"title":"UpdateForwardingPayload","example":{"register_order_id":"MP-12345","source":"api"}},"UpdateForwardingResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Success message"},"forwarded_order_id":{"type":"string","title":"Forwarded Order Id","description":"Forwarded order ID with FWD- prefix"},"to_forwarded_channel":{"type":"string","title":"To Forwarded Channel","description":"Source channel for forwarding"}},"type":"object","required":["message","forwarded_order_id","to_forwarded_channel"],"title":"UpdateForwardingResponse","example":{"forwarded_order_id":"FWD-MP-12345","message":"Forwarding details updated.","to_forwarded_channel":"api"}},"UpdateInventoryPayload":{"properties":{"id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Id","description":"Internal Stockpilot product ID"},"item_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Item Name","description":"Name of the item"},"sku":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sku","description":"Stock Keeping Unit"},"barcode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Barcode","description":"Product barcode (EAN)"},"quantity":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Quantity","description":"Stock quantity"},"base_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Base Price","description":"Base price"},"retail_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Retail Price","description":"Retail price"},"purchase_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Purchase Price","description":"Purchase price"},"wholesale_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Wholesale Price","description":"Wholesale price"},"sale_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Sale Price","description":"Sale price"},"weight":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Weight","description":"Weight as string, (default in grams)'"},"length":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Length","description":"Length (default in mm)"},"width":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Width","description":"Width (default in mm)"},"height":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Height","description":"Height (default in mm)"},"vat_class":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vat Class","description":"VAT class name (e.g., 'standard_rate')"},"condition":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Condition","description":"Condition (e.g., 'NEW')"},"stock_threshold":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Stock Threshold","description":"Minimum stock threshold for alerts"},"moq":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Moq","description":"Minimum order quantity"},"assign_bin_location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Assign Bin Location","description":"Assign bin location using format like 'A1-001-01' (path-rack-shelf or path-rack-shelf-bin)"},"remove_bin_location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Remove Bin Location","description":"Remove bin location using format like 'A1-001-01' (path-rack-shelf or path-rack-shelf-bin)"},"threshold":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Threshold","description":"Set threshold using format like '5u' for 5 units or '33w' for 33 weeks"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Is the product active"}},"type":"object","title":"UpdateInventoryPayload","example":{"assign_bin_location":"A1-001-01","base_price":19.95,"condition":"NEW","height":2.5,"is_active":true,"item_name":"My Blue Shirt","length":30.0,"moq":1,"purchase_price":10.0,"quantity":25,"sku":"TSHIRT-BLUE-M","stock_threshold":3,"threshold":"33w","vat_class":"standard_rate","weight":"0.3","width":20.0}},"UpdateInventoryResponse":{"properties":{"product_id":{"type":"integer","title":"Product Id"},"sku":{"type":"string","title":"Sku"},"updated_fields":{"items":{"type":"string"},"type":"array","title":"Updated Fields"}},"type":"object","required":["product_id","sku","updated_fields"],"title":"UpdateInventoryResponse","example":{"product_id":101,"sku":"TSHIRT-BLUE-M","updated_fields":["quantity","base_price","weight","item_length","condition"]}},"UpdateWebhookPayload":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Human readable name for this webhook"},"event":{"anyOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"null"}],"description":"Event type that triggers this webhook"},"target_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Url","description":"HTTPS endpoint that receives the delivery"},"secret":{"anyOf":[{"type":"string","minLength":16},{"type":"null"}],"title":"Secret","description":"Replacement signing secret (min 16 characters). Omit to leave the current secret untouched"},"warehouse_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Warehouse Id","description":"Restrict inventory.stock_changed deliveries to changes in a single warehouse"}},"type":"object","title":"UpdateWebhookPayload","example":{"name":"Stock changes to ERP (v2)","target_url":"https://erp.example.com/hooks/stockpilot/v2"}},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WebhookEvent":{"type":"string","enum":["orders.completed","inventory.stock_changed"],"title":"WebhookEvent"},"WebhookResponse":{"properties":{"id":{"type":"integer","title":"Id","description":"Webhook ID. Mirrored into the X-Stockpilot-Webhook delivery header"},"name":{"type":"string","title":"Name","description":"Human readable name for this webhook"},"event":{"type":"string","title":"Event","description":"Event type that triggers this webhook"},"target_url":{"type":"string","title":"Target Url","description":"Endpoint that receives the delivery"},"warehouse_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Warehouse Id","description":"Warehouse this webhook is restricted to, or null for all warehouses"},"is_active":{"type":"boolean","title":"Is Active","description":"Whether the webhook is currently delivering. Set to false by a soft delete or by auto-deactivation"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At","description":"Creation timestamp"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At","description":"Last update timestamp"}},"type":"object","required":["id","name","event","target_url","is_active"],"title":"WebhookResponse","example":{"created_at":"2026-09-01T09:14:22Z","event":"inventory.stock_changed","id":42,"is_active":true,"name":"Stock changes to ERP","target_url":"https://erp.example.com/hooks/stockpilot","updated_at":"2026-09-02T14:03:11Z","warehouse_id":"WH1"}}}},"tags":[{"name":"Authentication","description":"Verify API credentials."},{"name":"Rate Limits & Idempotency","description":"\nLimits are applied **per API key** - per `X-CLIENT-ID` / `X-CLIENT-SECRET` pair - in\nfixed 60 second windows. An organization running several integrations on separate keys\ngets these allowances on each of them, and a key can be raised or cut off on its own\nwithout disturbing the others.\n\n## Limits\n\n| Bucket | Limit | What is in it |\n| --- | --- | --- |\n| `read-single` | **300 / min** | Fetching one resource by `id`, `sku` or `barcode` - one upstream call. |\n| `read-list` | **120 / min** | Paginated collections - one upstream call per page of rows. |\n| `write` | **60 / min** | Any `POST`, `PUT`, `PATCH` or `DELETE` that is not in `heavy`. |\n| `heavy` | **20 / min** | Operations that fan out to a system Stockpilot does not control (carriers, marketplaces, mail) or run a large aggregation. These sit behind third-party limits of their own. |\n\nA request is bucketed by what it costs upstream, not by its HTTP verb alone. `GET`s that\nreturn one resource are cheap and get the highest allowance; anything that reaches a\ncarrier, a marketplace or a mail provider gets the lowest, because those systems impose\nlimits of their own that Stockpilot cannot raise for you.\n\n### Endpoints in `heavy`\n\n- `/analytics/items/sales`\n- `/analytics/product-order-history`\n- `/analytics/sales-summary`\n- `/invoices/send`\n- `/purchase-orders/recommendations`\n- `/sales-channels/sync-listings`\n- `/shipping/label-suggestion`\n- `/shipping/request-label`\n- `/shipping/retrieve-label`\n- `/webhooks/{webhook_id}/test`\n\n## Headers\n\nEvery documented endpoint returns its position in the current window:\n\n| Header | Meaning |\n| --- | --- |\n| `X-RateLimit-Limit` | Requests allowed per window for this bucket |\n| `X-RateLimit-Remaining` | Requests left in the current window |\n| `X-RateLimit-Reset` | Unix timestamp (UTC seconds) when the window resets |\n| `X-RateLimit-Policy` | Which bucket the request was counted against |\n\n```\nX-RateLimit-Limit: 120\nX-RateLimit-Remaining: 117\nX-RateLimit-Reset: 1756819200\nX-RateLimit-Policy: read-list\n```\n\n## Enforcement\n\n**The limits are advisory today.** Stockpilot does not return `429` and does not drop\nrequests over the line - the headers report your usage so you can pace an integration\nbefore enforcement begins. Enforcement will be announced in advance, and per-account\nceilings are agreed during onboarding; the numbers above are the defaults.\n\nBecause the counters are held per API instance, `X-RateLimit-Remaining` is a **lower\nbound** on what you actually have left, never an overstatement.\n\n### Pacing an integration\n\n- Read `X-RateLimit-Remaining` and slow down as it approaches zero, rather than\n  discovering a limit by hitting it.\n- Sleep until `X-RateLimit-Reset` instead of retrying immediately.\n- Prefer one paginated `read-list` call over N `read-single` calls when you need many\n  records - it is cheaper for both sides, whatever the bucket allowances suggest.\n- Back off on `5xx` with jittered exponential delays. A retry storm is the failure mode\n  these limits exist to contain.\n\n## Idempotency\n\nThere is **no `Idempotency-Key` header today.** Do not send one and assume it is\nhonoured. What is safe to retry depends on the operation:\n\n| Operation | Safe to blind-retry? |\n| --- | --- |\n| Any `GET` | Yes. |\n| `POST /inventory/update`, and the `PATCH` updates on orders, customers and bundles | Yes. These set absolute values rather than applying deltas, so replaying one converges on the same state. |\n| `POST /orders/create`, `POST /purchase-orders`, `POST /bundles/create` | **No.** A retry creates a second record. |\n| `POST /orders/{order_pk}/items/add`, `move-to-backorder`, `move-from-backorder` | **No.** These apply a change relative to current state. |\n| `POST /shipping/request-label` | **No.** A retry can buy a second label. |\n\nIf a create times out or fails ambiguously, **poll before retrying** - `GET /orders`\nfiltered to the customer and window, or `GET /purchase-orders` - and only resend if the\nrecord is genuinely absent. Webhook deliveries carry a `delivery_id` for exactly this\nreason; the REST create endpoints have no equivalent handle yet.\n\nIdempotency keys and account-specific limits are part of the onboarding conversation -\nif your integration needs guaranteed-once creates, raise it there and we will size it\nwith you.\n","x-traitTag":true},{"name":"Products","description":"Product catalog and product images."},{"name":"Categories & Brands","description":"Product categories and brands."},{"name":"Inventory","description":"Inventory items, stock levels, locations and thresholds."},{"name":"Bundles","description":"Bundled products and their component items."},{"name":"Warehouses","description":"Warehouses and their stock."},{"name":"Orders","description":"Order lifecycle from creation through fulfilment."},{"name":"Returns","description":"Return orders with status and channel filtering."},{"name":"Purchase Orders","description":"Supplier procurement workflows."},{"name":"Invoices","description":"Invoice retrieval and sending."},{"name":"Shipping","description":"Shipping integrations and label generation."},{"name":"Customers","description":"Customer records."},{"name":"Analytics","description":"Sales reporting and forecasting."},{"name":"Sales Channels","description":"Sales channel integrations."},{"name":"Webhooks","description":"\nManage outbound webhook subscriptions.\n\nAll webhooks are scoped to your organization. A webhook ID belonging to another\norganization returns `404`, the same as one that does not exist.\n\nSee **Webhook Delivery Contract** for the envelope, signature verification, payload shapes\nand retry behaviour.\n"},{"name":"Webhook Delivery Contract","description":"\nStockpilot pushes events to an HTTPS endpoint you control. You register an endpoint\nwith `POST /webhooks/create`, verify the signature on every request, and respond `2xx`\nquickly. Delivery is **at-least-once**, so your handler must be idempotent.\n\nManage webhooks with the operations under **Webhooks**. This page documents what\nStockpilot sends and what your endpoint has to do with it.\n\n## Available events\n\n| Event | Fires when |\n| --- | --- |\n| `orders.completed` | An order reaches the completed state - either by transitioning to it, or by arriving already completed (e.g. a Bol.com import). Fires once per order. |\n| `inventory.stock_changed` | A product's stock quantity changes in any warehouse. |\n\n`GET /webhooks/events` returns the same list at runtime, so an integration can discover\nnew event types without a redeploy.\n\n## The envelope\n\nEvery delivery has the same outer shape. Only `data` varies by event.\n\n```json\n{\n  \"webhook_id\": 42,\n  \"organization_id\": 1874,\n  \"name\": \"Stock changes to ERP\",\n  \"event\": \"inventory.stock_changed\",\n  \"event_triggered_at\": \"2026-09-02T14:03:11+00:00\",\n  \"delivery_id\": \"9c1f0f8e5b7a4c2d8e3f1a2b3c4d5e6f\",\n  \"data\": { }\n}\n```\n\n## Headers\n\n| Header | Value |\n| --- | --- |\n| `Content-Type` | `application/json` |\n| `User-Agent` | `Stockpilot-Webhooks/1.0` |\n| `X-Stockpilot-Event` | `inventory.stock_changed` |\n| `X-Stockpilot-Delivery` | `9c1f0f8e5b7a4c2d8e3f1a2b3c4d5e6f` |\n| `X-Stockpilot-Webhook` | `42` |\n| `X-Stockpilot-Organization` | `1874` |\n| `X-Stockpilot-Signature` | `base64(HMAC-SHA256(raw_request_body, webhook_secret))` |\n\n`webhook_id` and `organization_id` are mirrored into headers as well as the body, so you\ncan route a request before parsing it.\n\n## Verifying the signature\n\nCompute the HMAC over the **raw body bytes, before any JSON parsing**. Re-serialising a\nparsed body changes whitespace and key order and will not match.\n\n```python\nimport base64, hashlib, hmac\n\nexpected = base64.b64encode(\n    hmac.new(secret.encode(), raw_body, hashlib.sha256).digest()\n).decode()\nvalid = hmac.compare_digest(expected, request.headers[\"X-Stockpilot-Signature\"])\n```\n\nUse `hmac.compare_digest` rather than `==` so the comparison is constant-time.\n\n## Payload for orders.completed\n\nThe same object shape that `GET /orders/get-single-order` returns. See that operation for\nthe full field list.\n\n## Payload for inventory.stock_changed\n\nThis payload does **not** mirror a REST endpoint. It is purpose-built so that\nmulti-warehouse stock is unambiguous.\n\n```json\n{\n  \"id\": 34897,\n  \"sku\": \"201156\",\n  \"name\": \"Vichy Homme Structure Force 50 ml\",\n  \"barcode\": \"3337875647212\",\n  \"offered_stock\": 3,\n  \"total_on_hand\": 4,\n  \"incoming\": 12,\n  \"backorder\": 0,\n  \"quantities\": [\n    {\"warehouse\": \"Main\", \"warehouse_id\": \"WH1\", \"is_default\": true,\n     \"sums_onto_offered\": true, \"available\": 3, \"reserved\": 1, \"on_hand\": 4, \"inbound\": 12},\n    {\"warehouse\": \"Amazon FBA\", \"warehouse_id\": \"WH2\", \"is_default\": false,\n     \"sums_onto_offered\": false, \"available\": 1, \"reserved\": 0, \"on_hand\": 1, \"inbound\": 6}\n  ]\n}\n```\n\n### Field meanings\n\n| Field | Meaning |\n| --- | --- |\n| `offered_stock` | What Stockpilot actually offers to sales channels: the sum of warehouses with `sums_onto_offered: true`, minus buffer stock. **This is the number most integrations want.** |\n| `total_on_hand` | On-hand across contributing warehouses, including reserved. |\n| `incoming` | Units on open purchase orders. Product-level, not attributed to a warehouse. |\n| `backorder` | Units currently on backorder. |\n| `quantities[].available` | On-hand in that warehouse **excluding** reserved. |\n| `quantities[].on_hand` | On-hand in that warehouse **including** reserved. |\n| `quantities[].reserved` | Units allocated to open orders. |\n| `quantities[].inbound` | Stock in transit into that warehouse. |\n| `quantities[].sums_onto_offered` | Whether this warehouse contributes to `offered_stock`. |\n| `quantities[].is_default` | Whether this is the default warehouse. |\n\n### Do not sum inbound across warehouses\n\nOn the default warehouse, per-warehouse `inbound` repeats the product-level `incoming`,\nbecause purchase orders land there. Summing `inbound` across warehouses and comparing the\nresult to `incoming` double-counts. In the example above, `incoming` is `12` and `WH1.inbound`\nis the same `12` - not an additional 12.\n\n### No deltas\n\nNo before/after pair is sent. Deliveries are coalesced (see below), so a previous - new\npair would be misleading: the \"previous\" value may be several writes stale. **Treat every\npayload as current state.**\n\n## Warehouse scoping\n\n`warehouse_id` is optional on create and update, and controls which stock events a webhook\nreceives:\n\n- **Omitted** - the webhook receives `inventory.stock_changed` for every warehouse.\n- **Set** - the webhook receives only events for changes in that warehouse.\n\nA scoped webhook still receives the **full product payload**, with every warehouse present\nin `quantities`. Scoping filters which changes trigger a delivery; it does not trim the\nbody. So `offered_stock` remains the org-wide offered figure, not a per-warehouse subtotal.\n\nUse one scoped webhook per warehouse when different systems own different warehouses, and\nroute on `X-Stockpilot-Webhook`.\n\n## Secrets\n\n`secret` is optional on both create and update:\n\n- Supply one and it is used verbatim. Minimum 16 characters.\n- Omit it and Stockpilot generates one for you.\n\n## Multi-tenant integrations\n\nIf you are a platform receiving webhooks on behalf of many Stockpilot organizations:\n\n1. Create one webhook per customer org, all with your own shared secret.\n2. Store the returned `webhook_id` against your customer record.\n3. On delivery, verify with that constant secret and route on `X-Stockpilot-Webhook`.\n\nBecause `webhook_id` and `organization_id` are both mirrored into headers, you can identify\nthe sender before parsing the body.\n\n**Caveat:** a shared secret is visible to every org admin who has it configured. If your\ncustomers need to be mutually distrustful, use a distinct secret per org instead.\n\n## Consumer contract\n\n- **Respond `2xx` within 10 seconds.** Queue the payload and return immediately; do not\n  process inline.\n- **A non-2xx response or a timeout is a failed attempt.** Retries run 15 times over roughly\n  17 hours: 10s, 30s, 1m, 2m, 5m, 10m, 20m, 30m, 1h, 1h, 2h, 2h, 3h, 3h, 3h.\n- **After 5 fully-failed deliveries in 24 hours the webhook is auto-deactivated.** Reactivate\n  it with `POST /webhooks/{id}/reactivate` or from the Settings UI.\n- **Endpoints must be HTTPS and publicly resolvable.** Private, loopback, link-local and\n  cloud-metadata addresses are rejected at create time and again at delivery time.\n- **Redirects are not followed.**\n- **Delivery is at-least-once.** Deduplicate on `delivery_id`.\n- **`inventory.stock_changed` is coalesced** over roughly 5 seconds **per product per\n  warehouse**, so a burst of writes to one product in one warehouse arrives as a single\n  delivery carrying the final quantity. Coalescing does **not** merge across warehouses:\n  if the same product changes in two warehouses inside that window you receive two\n  deliveries, each with its own `delivery_id`, and each carrying the full `quantities`\n  array. Deduplicating on `delivery_id` will not collapse them - and should not, since\n  both are real changes.\n\nUse `GET /webhooks/{id}/deliveries` to see recent attempts with their status, response code\nand error, and `POST /webhooks/{id}/test` to send a sample envelope and see exactly what your\nendpoint returned.\n\n## Networking\n\nDeliveries originate from Stockpilot's **worker infrastructure**, not from the API gateway.\nCustomers who IP-allowlist inbound traffic need the worker egress ranges - allowlisting the\ngateway's addresses will not work.\n","x-traitTag":true}],"x-tagGroups":[{"name":"Core","tags":["Authentication","Rate Limits & Idempotency"]},{"name":"Catalog","tags":["Products","Categories & Brands","Inventory","Bundles","Warehouses"]},{"name":"Operations","tags":["Orders","Returns","Purchase Orders","Invoices","Shipping","Customers"]},{"name":"Insights","tags":["Analytics"]},{"name":"Integrations","tags":["Sales Channels","Webhooks","Webhook Delivery Contract"]}]}