Getting Started
#getting-startedThe CHUB API lets you programmatically manage your warehouse operations. Your API credentials are generated by your CHUB account manager from the admin panel — once issued, you can view your API Token and API Secret at any time by logging into your client portal and going to Settings.
All API calls must be made over HTTPS. Requests over plain HTTP will be rejected. All request and response bodies are encoded as JSON.
X-Request-ID header with a unique UUID in every request. CHUB echoes it back in all responses — making it much easier to correlate logs and debug issues across distributed systems.
Your First Request
curl -X GET \ https://chub.ae/api/v1/product-stock/YOUR-SKU-HERE \ -H "X-CHUB-TOKEN: your_api_token" \ -H "X-CHUB-SECRET: your_api_secret" \ -H "Accept: application/json"
const response = await fetch('https://chub.ae/api/v1/product-stock/YOUR-SKU-HERE', { headers: { 'X-CHUB-TOKEN': 'your_api_token', 'X-CHUB-SECRET': 'your_api_secret', 'Accept': 'application/json', } }); const data = await response.json(); console.log(data);
$ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => 'https://chub.ae/api/v1/product-stock/YOUR-SKU-HERE', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'X-CHUB-TOKEN: your_api_token', 'X-CHUB-SECRET: your_api_secret', 'Accept: application/json', ], ]); $data = json_decode(curl_exec($ch), true); curl_close($ch);
import requests headers = { 'X-CHUB-TOKEN': 'your_api_token', 'X-CHUB-SECRET': 'your_api_secret', 'Accept': 'application/json', } res = requests.get( 'https://chub.ae/api/v1/product-stock/YOUR-SKU-HERE', headers=headers ) print(res.json())
package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://chub.ae/api/v1/product-stock/YOUR-SKU-HERE", nil) req.Header.Set("X-CHUB-TOKEN", "your_api_token") req.Header.Set("X-CHUB-SECRET", "your_api_secret") req.Header.Set("Accept", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body)) }
Authentication
#authenticationThe CHUB API uses a dual-credential scheme. Every authenticated request must include both headers simultaneously. Missing either will result in a 401 Unauthorized response.
# Required on every authenticated request X-CHUB-TOKEN: tok_live_a1b2c3d4e5f6... X-CHUB-SECRET: sec_live_x7y8z9w0v1u2... Content-Type: application/json Accept: application/json # Recommended for tracing X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
Rate Limiting
#rate-limitingAPI requests are rate-limited to protect platform stability. Limits apply per API token.
When rate-limited, the API returns 429 Too Many Requests. Implement exponential backoff with jitter when handling 429 responses.
Orders API
Submit a new fulfillment order. CHUB will validate stock availability for all line items. If any item has insufficient stock, the order is placed with an on_hold_stock status and an issues array is returned detailing the affected SKUs.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| order_number | string | optional | Your own reference number. Auto-generated if omitted. |
| customer_name | string | required | Full name of the recipient |
| customer_phone | string | optional | UAE format (05XXXXXXXX). Nullable. |
| customer_email | string | optional | Recipient email for notifications |
| country | string | optional | Defaults to "United Arab Emirates" |
| city | string | required | Delivery city |
| area | string | required | Delivery area / district |
| street | string | optional | Street name or number |
| building | string | optional | Building name or number |
| apartment_no | string | optional | Apartment / unit number |
| landmark | string | optional | Nearby landmark for the courier |
| full_address | string | optional | Full address override. Auto-built from the above fields if omitted. |
| payment_type | string | required | cod or prepaid |
| delivery_charge | number | optional | Delivery fee in AED. Defaults to 0. |
| notes | string | optional | Special delivery instructions |
| items | array | required | Array of order line items (see below) |
| items[].sku | string | required | Product SKU — case-insensitive |
| items[].quantity | integer | required | Quantity (min 1) |
curl -X POST \ https://chub.ae/api/v1/orders \ -H "X-CHUB-TOKEN: your_api_token" \ -H "X-CHUB-SECRET: your_api_secret" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "order_number": "MY-ORD-001", "customer_name": "Ahmed Al Mansoori", "customer_phone": "0501234567", "customer_email": "[email protected]", "city": "Dubai", "area": "Al Barsha", "street": "Sheikh Zayed Rd", "building": "Villa 12", "payment_type": "cod", "delivery_charge": 15.00, "notes": "Leave at door", "items": [ { "sku": "PROD-001", "quantity": 2 }, { "sku": "PROD-002", "quantity": 1 } ] }'
const res = await fetch('https://chub.ae/api/v1/orders', { method: 'POST', headers: { 'X-CHUB-TOKEN': 'your_api_token', 'X-CHUB-SECRET': 'your_api_secret', 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify({ order_number: 'MY-ORD-001', // optional — auto-generated if omitted customer_name: 'Ahmed Al Mansoori', customer_phone: '0501234567', // optional customer_email: '[email protected]', // optional city: 'Dubai', area: 'Al Barsha', street: 'Sheikh Zayed Rd', // optional building: 'Villa 12', // optional payment_type: 'cod', delivery_charge: 15.00, // optional, defaults to 0 notes: 'Leave at door', // optional items: [ { sku: 'PROD-001', quantity: 2 }, { sku: 'PROD-002', quantity: 1 }, ], }), }); const order = await res.json();
$payload = [ 'order_number' => 'MY-ORD-001', // optional 'customer_name' => 'Ahmed Al Mansoori', 'customer_phone' => '0501234567', // optional 'customer_email' => '[email protected]', // optional 'city' => 'Dubai', 'area' => 'Al Barsha', 'street' => 'Sheikh Zayed Rd', // optional 'building' => 'Villa 12', // optional 'payment_type' => 'cod', 'delivery_charge' => 15.00, // optional 'notes' => 'Leave at door', // optional 'items' => [ ['sku' => 'PROD-001', 'quantity' => 2], ['sku' => 'PROD-002', 'quantity' => 1], ], ]; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => 'https://chub.ae/api/v1/orders', CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_HTTPHEADER => [ 'X-CHUB-TOKEN: your_api_token', 'X-CHUB-SECRET: your_api_secret', 'Content-Type: application/json', 'Accept: application/json', ], ]); $result = json_decode(curl_exec($ch), true); curl_close($ch);
import requests payload = { "order_number": "MY-ORD-001", # optional "customer_name": "Ahmed Al Mansoori", "customer_phone": "0501234567", # optional "customer_email": "[email protected]", # optional "city": "Dubai", "area": "Al Barsha", "street": "Sheikh Zayed Rd", # optional "building": "Villa 12", # optional "payment_type": "cod", "delivery_charge": 15.00, # optional "notes": "Leave at door", # optional "items": [ { "sku": "PROD-001", "quantity": 2 }, { "sku": "PROD-002", "quantity": 1 }, ], } headers = { "X-CHUB-TOKEN": "your_api_token", "X-CHUB-SECRET": "your_api_secret", "Accept": "application/json", } res = requests.post( "https://chub.ae/api/v1/orders", json=payload, headers=headers ) print(res.json())
package main import ( "bytes"; "encoding/json"; "fmt" "io"; "net/http" ) func main() { body, _ := json.Marshal(map[string]any{ "customer_name": "Ahmed Al Mansoori", "customer_phone": "0501234567", "customer_email": "[email protected]", "city": "Dubai", "area": "Al Barsha", "street": "Sheikh Zayed Rd", "building": "Villa 12", "payment_type": "cod", "delivery_charge": 15.00, "items": []map[string]any{ {"sku": "PROD-001", "quantity": 2}, {"sku": "PROD-002", "quantity": 1}, }, }) req, _ := http.NewRequest("POST", "https://chub.ae/api/v1/orders", bytes.NewBuffer(body)) req.Header.Set("X-CHUB-TOKEN", "your_api_token") req.Header.Set("X-CHUB-SECRET", "your_api_secret") req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() out, _ := io.ReadAll(resp.Body) fmt.Println(string(out)) }
Response — 201 Created
{
"success": true,
"message": "Order created successfully.",
"order_id": 1042,
"order_number": "AH-00001",
"status": "pending",
"issues": [], // Empty = all items in stock
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}Response — 201 with Stock Issues
{
"success": true,
"message": "Order created with stock issues.",
"order_id": 1043,
"order_number": "AH-00002",
"status": "on_hold_stock",
"issues": [
{
"type": "INSUFFICIENT_STOCK",
"sku": "PROD-002",
"product_name": "Blue Hoodie XL",
"requested": 5,
"available": 2,
"missing": 3
}
],
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}Update an existing order. Only orders with status pending or on_hold_stock can be modified. Attempting to update a processing or dispatched order returns a 422 error.
Request Body (all fields optional)
| Field | Type | Required | Description |
|---|---|---|---|
| items | array | optional | Replaces the entire item list when provided |
| items[].sku | string | required | Product SKU — case-insensitive |
| items[].quantity | integer | required | Quantity (min 1) |
| delivery_charge | number | optional | Updated delivery fee in AED |
| payment_type | string | optional | cod or prepaid |
| notes | string | optional | Updated delivery instructions |
curl -X PATCH \ https://chub.ae/api/v1/orders/AH-240001 \ -H "X-CHUB-TOKEN: your_api_token" \ -H "X-CHUB-SECRET: your_api_secret" \ -H "Content-Type: application/json" \ -d '{ "delivery_charge": 20.00, "payment_type": "prepaid", "items": [ { "sku": "PROD-001", "quantity": 3 }, { "sku": "PROD-003", "quantity": 1 } ] }'
Response — 200 OK
{
"success": true,
"message": "Order updated successfully.",
"order_number": "AH-00001",
"status": "pending",
"total_items": 4,
"total_amount": "310.00",
"issues": [],
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}Retrieve the current fulfillment status of an order. Use this to poll for status changes or build real-time tracking into your storefront.
curl https://chub.ae/api/v1/order-status/AH-240001 \ -H "X-CHUB-TOKEN: your_api_token" \ -H "X-CHUB-SECRET: your_api_secret"
Response — 200 OK
{
"success": true,
"order_number": "AH-00001",
"status": "on_the_way",
"last_updated": "2024-11-14T09:32:00.000000Z"
}Products API
Creates a new product linked to your account. SKU must be unique within your catalog. Stock quantity is managed by CHUB warehouse staff — it cannot be set via this API and always starts at 0.
| Field | Type | Required | Description |
|---|---|---|---|
| sku | string | required | Unique product identifier. Max 100 characters. |
| name | string | required | Product display name. Max 255 characters. |
| product_type | string | optional | e.g. simple, bundle. Defaults to simple. |
| sale_price | number | optional | Selling price (used for order value calculation). |
| cost_price | number | optional | Cost / landed price. |
| barcode | string | optional | EAN, UPC or any barcode string. |
| external_product_id | string | optional | Your internal ID (Shopify product ID, WooCommerce ID, etc.). |
| weight_grams | integer | optional | Weight in grams. |
| length_cm | number | optional | Length in centimetres. |
| width_cm | number | optional | Width in centimetres. |
| height_cm | number | optional | Height in centimetres. |
| country_of_origin | string | optional | 2-letter ISO country code (e.g. AE, CN, US). |
| hs_code | string | optional | Harmonised System code for customs. Max 20 characters. |
| image_url | string (URL) | optional | Public URL of the product image on your CDN. |
| description | string | optional | Product description. |
curl -X POST \ https://chub.ae/api/v1/products \ -H "X-CHUB-TOKEN: your_api_token" \ -H "X-CHUB-SECRET: your_api_secret" \ -H "Content-Type: application/json" \ -d '{ "sku": "PROD-001", "name": "Premium Wireless Headphones", "product_type": "simple", "sale_price": 299.00, "cost_price": 120.00, "barcode": "6291041500213", "external_product_id": "shopify_8821934", "weight_grams": 350, "length_cm": 20, "width_cm": 18, "height_cm": 9, "country_of_origin": "CN", "image_url": "https://cdn.yourstore.com/products/headphones.jpg", "description": "Over-ear noise cancelling headphones" }'
const res = await fetch('https://chub.ae/api/v1/products', { method: 'POST', headers: { 'X-CHUB-TOKEN': 'your_api_token', 'X-CHUB-SECRET': 'your_api_secret', 'Content-Type': 'application/json', }, body: JSON.stringify({ sku: 'PROD-001', name: 'Premium Wireless Headphones', sale_price: 299.00, external_product_id: 'shopify_8821934', image_url: 'https://cdn.yourstore.com/products/headphones.jpg', }), }); const { data } = await res.json(); console.log(data.sku);
Response — 201 Created
{
"success": true,
"message": "Product created successfully.",
"data": {
"sku": "PROD-001",
"name": "Premium Wireless Headphones",
"product_type": "simple",
"sale_price": "299.00",
"cost_price": "120.00",
"available_qty": 0,
"barcode": "6291041500213",
"external_product_id": "shopify_8821934",
"weight_grams": 350,
"length_cm": "20.00",
"width_cm": "18.00",
"height_cm": "9.00",
"country_of_origin": "CN",
"hs_code": null,
"image_url": "https://cdn.yourstore.com/products/headphones.jpg",
"description": "Over-ear noise cancelling headphones",
"created_at": "2026-03-31T10:00:00.000000Z",
"updated_at": "2026-03-31T10:00:00.000000Z"
},
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}Error Codes
| HTTP | error_code | Meaning |
|---|---|---|
| 422 | VALIDATION_ERROR | One or more fields failed validation |
| 409 | DUPLICATE_SKU | A product with this SKU already exists in your catalog |
| 500 | SERVER_ERROR | Unexpected server error |
Updates an existing product by SKU. All fields are optional — only the fields you send will be updated (partial update). The sku itself and available_qty cannot be changed via this endpoint.
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | optional | Updated product name. |
| product_type | string | optional | e.g. simple, bundle. |
| sale_price | number | optional | Updated selling price. |
| cost_price | number | optional | Updated cost price. |
| barcode | string | optional | Updated barcode. |
| external_product_id | string | optional | Updated external ID. |
| weight_grams | integer | optional | Updated weight in grams. |
| length_cm | number | optional | Updated length. |
| width_cm | number | optional | Updated width. |
| height_cm | number | optional | Updated height. |
| country_of_origin | string | optional | 2-letter ISO country code. |
| hs_code | string | optional | Updated HS code. |
| image_url | string (URL) | optional | Updated product image URL. |
| description | string | optional | Updated description. |
curl -X PUT \ https://chub.ae/api/v1/products/PROD-001 \ -H "X-CHUB-TOKEN: your_api_token" \ -H "X-CHUB-SECRET: your_api_secret" \ -H "Content-Type: application/json" \ -d '{ "sale_price": 249.00, "image_url": "https://cdn.yourstore.com/products/headphones-v2.jpg" }'
const res = await fetch('https://chub.ae/api/v1/products/PROD-001', { method: 'PUT', headers: { 'X-CHUB-TOKEN': 'your_api_token', 'X-CHUB-SECRET': 'your_api_secret', 'Content-Type': 'application/json', }, body: JSON.stringify({ sale_price: 249.00, image_url: 'https://cdn.yourstore.com/products/headphones-v2.jpg', }), }); const { data } = await res.json(); console.log(data.sale_price);
Response — 200 OK
{
"success": true,
"message": "Product updated successfully.",
"data": {
"sku": "PROD-001",
"name": "Premium Wireless Headphones",
"product_type": "simple",
"sale_price": "249.00",
"available_qty": 142,
"image_url": "https://cdn.yourstore.com/products/headphones-v2.jpg",
"updated_at": "2026-03-31T11:30:00.000000Z"
},
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}Error Codes
| HTTP | error_code | Meaning |
|---|---|---|
| 404 | PRODUCT_NOT_FOUND | No product with this SKU exists in your catalog |
| 422 | VALIDATION_ERROR | One or more fields failed validation |
| 422 | NO_CHANGES | Request body contained no updatable fields |
| 500 | SERVER_ERROR | Unexpected server error |
Stock API
Returns the current available quantity for a single SKU. SKU lookups are case-insensitive — the response returns the SKU exactly as stored in the system.
curl https://chub.ae/api/v1/product-stock/PROD-001 \ -H "X-CHUB-TOKEN: your_api_token" \ -H "X-CHUB-SECRET: your_api_secret"
const res = await fetch( 'https://chub.ae/api/v1/product-stock/PROD-001', { headers: { 'X-CHUB-TOKEN': '...', 'X-CHUB-SECRET': '...' } } ); const { data } = await res.json(); console.log(`Available: ${data.quantity}`);
Response — 200 OK
{
"success": true,
"data": {
"sku": "PROD-001",
"quantity": 142,
"updated_at": "2024-11-14T08:00:00.000000Z"
}
}Query stock levels for up to 200 SKUs in a single request. The response includes a missing array with any SKUs that were not found in your catalog.
| Field | Type | Required | Description |
|---|---|---|---|
| skus | array<string> | required | List of SKUs to query. Max 200. |
curl -X POST \ https://chub.ae/api/v1/product-stock \ -H "X-CHUB-TOKEN: your_api_token" \ -H "X-CHUB-SECRET: your_api_secret" \ -H "Content-Type: application/json" \ -d '{ "skus": ["PROD-001", "PROD-002", "PROD-003", "UNKNOWN-SKU"] }'
Response — 200 OK
{
"success": true,
"data": [
{ "sku": "PROD-001", "quantity": 142, "updated_at": "2024-11-14T08:00:00Z" },
{ "sku": "PROD-002", "quantity": 0, "updated_at": "2024-11-13T14:22:00Z" },
{ "sku": "PROD-003", "quantity": 56, "updated_at": "2024-11-14T07:45:00Z" }
],
"missing": ["UNKNOWN-SKU"] // SKUs not found in your catalog
}Response Format
#response-formatAll API responses use a consistent JSON envelope. Always check the success field before parsing data.
| Field | Type | Description |
|---|---|---|
| success | boolean | true on 2xx responses, false on errors |
| message | string | Human-readable summary |
| data | object | array | Response payload. Present on success. |
| errors | object | Validation error details. Present on 422. |
| error_code | string | Machine-readable error identifier |
| issues | array | Stock or fulfillment warnings on order creation |
| request_id | string | Echo of your X-Request-ID header |
{
"success": false,
"message": "Validation failed",
"error_code": "VALIDATION_ERROR",
"errors": {
"customer_phone": ["The customer phone field is required."],
"items.0.sku": ["The items.0.sku field is required."]
},
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}Order Statuses
#order-statusesOrders progress through the following lifecycle states. The status returned by the API reflects real-time warehouse activity.
| Status | Description | Modifiable |
|---|---|---|
| pending | Order received and queued for picking | ✅ Yes |
| on_hold_stock | Placed on hold due to insufficient stock | ✅ Yes |
| processing | Picker has started fulfilling the order | ❌ No |
| picked | All items picked from warehouse shelves | ❌ No |
| packed | Order packed and labeled | ❌ No |
| ready_dispatch | Awaiting courier pickup | ❌ No |
| on_the_way | With courier for last-mile delivery | ❌ No |
| delivered | Successfully delivered to customer | ❌ No |
| cancelled | Order cancelled | ❌ No |
HTTP Status Codes
#http-codeserrors field.Error Codes
#error-codesMachine-readable error codes are returned in the error_code field. Use these for programmatic error handling in your integration.
errors object.
issues array with details.
request_id when contacting support.