Appearance
E-commerce Webhooks
SmartCut can send webhook notifications to your server when e-commerce events occur. Configure your webhook endpoint URL and the events you want to receive in your organisation settings.
Delivery
Webhooks are sent as POST requests with a JSON body. Each request includes the following headers:
| Header | Description |
|---|---|
Content-Type | application/json |
X-SmartCut-Timestamp | Unix timestamp (ms) of the delivery attempt |
X-SmartCut-Delivery | Unique delivery ID (UUID) |
Failed deliveries (5xx or network errors) are retried up to 3 times with delays of 1s, 5s, and 30s. Client errors (4xx) are not retried.
Events
| Event | Description |
|---|---|
order.created | A new order has been placed and payment confirmed |
order.status_changed | The order status has been updated |
inventory.decremented | Stock quantities have been reduced after an order |
Order Webhook
Sent for order.created and order.status_changed events.
Payload
json
{
"event": "order.created",
"orderId": "abc123",
"organisationId": "org456",
"timestamp": "2026-03-06T12:00:00.000Z",
"data": {
"status": "pending",
"previousStatus": "draft",
"paymentStatus": "paid",
"customer": {
"name": "Jane Smith",
"email": "[email protected]",
"phone": "+44 7700 900000"
},
"pricing": {
"total": 149.99,
"currency": "GBP",
"itemsSubtotal": 129.99,
"shippingCost": 20.00
},
"shipping": {
"method": "standard",
"address": {
"line1": "123 High Street",
"city": "London",
"postalCode": "SW1A 1AA",
"country": "GB"
}
},
"itemCount": 3,
"partsCount": 24,
"createdAt": "2026-03-06T11:55:00.000Z",
"items": [
{
"itemId": "item-uuid-1",
"includeOffcuts": true,
"parts": [
{ "partIndex": 0, "label": "Side Panel", "l": 600, "w": 400, "quantity": 2, "material": "MDF", "thickness": 18 },
{ "partIndex": 1, "label": "Shelf", "l": 580, "w": 350, "quantity": 3, "material": "MDF", "thickness": 18 }
]
}
],
"results": [
{
"jobId": 12345,
"saw": {},
"stock": [],
"parts": [],
"cuts": [],
"offcuts": [],
"unusableParts": [],
"metadata": {}
}
]
}
}Fields
Top-level
| Field | Type | Description |
|---|---|---|
event | string | One of order.created, order.status_changed |
orderId | string | Unique order identifier |
organisationId | string | Your organisation ID |
timestamp | string | ISO 8601 timestamp of when the webhook was sent |
data
| Field | Type | Required | Description |
|---|---|---|---|
status | string | Yes | Current order status |
previousStatus | string | No | Previous status (present on order.status_changed) |
paymentStatus | string | No | Payment status |
itemCount | number | Yes | Number of line items in the order |
partsCount | number | Yes | Total number of individual parts |
createdAt | string | Yes | ISO 8601 timestamp of order creation |
items | array | Yes | Per-item part lists — use with the ecommerce API (see below) |
results | array | No | Optimisation results for each job in the order, in V3 API response format |
data.items[]
Each entry corresponds to one basket item in the order and contains the information needed to call the ecommerce API endpoints.
| Field | Type | Description |
|---|---|---|
itemId | string | Basket item ID — use as itemId in mark-cut / mark-complete / adjust-cut requests |
includeOffcuts | boolean | Whether the customer opted in to receive offcuts for this job (false if not opted in) |
parts | array | Input parts for this item |
data.items[].parts[]
| Field | Type | Description |
|---|---|---|
partIndex | number | Index of this part — use as partIndex in ecommerce API requests |
label | string | Part label/name (if provided) |
l | number | Part length (long side, mm) |
w | number | Part width (short side, mm) |
quantity | number | Number of this part required |
material | string | Material name (if specified) |
thickness | number | Material thickness in mm (if specified) |
data.results[]
Each entry is a full V3 API response containing the optimisation result for one job in the order. See the V3 API documentation for the complete response schema.
| Field | Type | Description |
|---|---|---|
jobId | number | Job identifier |
saw | object | Saw configuration used |
stock | array | Stock items |
parts | array | Parts with coordinates and properties |
cuts | array | Cut instructions |
offcuts | array | Remaining offcut pieces |
unusableParts | array | Parts that could not be placed |
metadata | object | Complete analysis and metrics (efficiency, waste, costs, material summary) |
data.customer
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Customer full name |
email | string | Yes | Customer email address |
phone | string | No | Customer phone number |
data.pricing
| Field | Type | Required | Description |
|---|---|---|---|
total | number | Yes | Order total |
currency | string | Yes | ISO 4217 currency code |
itemsSubtotal | number | No | Subtotal before shipping |
shippingCost | number | No | Shipping cost |
data.shipping
Optional. Present when shipping details are available.
| Field | Type | Required | Description |
|---|---|---|---|
method | string | No | Shipping method name |
address.line1 | string | Yes | Street address |
address.city | string | Yes | City |
address.postalCode | string | Yes | Postal/ZIP code |
address.country | string | Yes | ISO 3166-1 alpha-2 country code |
Inventory Webhook
Sent when stock quantities are decremented after an order is processed.
Payload
json
{
"event": "inventory.decremented",
"orderId": "abc123",
"organisationId": "org456",
"timestamp": "2026-03-06T12:01:00.000Z",
"data": {
"changes": [
{
"stockId": "stock789",
"stockName": "18mm White Melamine",
"material": "Melamine",
"thickness": 18,
"dimensions": {
"length": 2440,
"width": 1220
},
"previousQuantity": 50,
"newQuantity": 47,
"decrementedBy": 3,
"deleted": false
}
]
}
}data.changes[]
| Field | Type | Required | Description |
|---|---|---|---|
stockId | string | Yes | Stock item ID |
stockName | string | No | Human-readable stock name |
material | string | No | Material type |
thickness | number | No | Material thickness |
dimensions.length | number | No | Stock length |
dimensions.width | number | No | Stock width |
previousQuantity | number | Yes | Quantity before decrement |
newQuantity | number | Yes | Quantity after decrement |
decrementedBy | number | Yes | Number of units consumed |
deleted | boolean | No | Whether the stock item was fully consumed |
Testing
Use the test endpoint to verify your webhook URL is reachable. A test webhook sends a simple payload:
json
{
"event": "test",
"timestamp": "2026-03-06T12:00:00.000Z",
"message": "This is a test webhook from SmartCut"
}API
These endpoints allow you to track orders and parts through the cutting and completion workflow. All requests require your API key in the Authorization header.
Order status progression: pending → cut → complete → dispatched
Update Order Status
PATCH /ecommerce/orders/:idUpdates the status of an order.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
status | string | Yes | One of pending, cut, complete, dispatched, cancelled |
updateInventory | boolean | No | Decrement inventory stock when marking as cut (default: false) |
forceOverwrite | boolean | No | Skip partial progress warning when marking as cut (default: false) |
resetCuts | boolean | No | Reset all part cut counts when reverting to pending (default: false) |
Example:
json
{ "status": "complete" }Responses:
| Status | Description |
|---|---|
200 | Order updated — returns the updated order document |
400 | Invalid status value |
403 | Order not in your organisation |
404 | Order not found |
409 | Partial cut progress detected — pass forceOverwrite: true to proceed |
When a 409 is returned, the response body contains:
json
{
"error": "Order has parts with partial cut progress.",
"code": "PARTIAL_PROGRESS",
"data": {
"partsWithProgress": [
{ "itemName": "Item abc", "partIndex": 0, "current": 2, "total": 5 }
]
}
}Mark Parts Cut
PATCH /ecommerce/orders/parts/mark-cutIncrements the numberCut count for one or more parts. When all parts in an order reach their full quantity, the order is automatically set to cut.
Request body:
json
{
"updates": [
{ "orderId": "66abc123", "itemId": "item-uuid", "partIndex": 0, "count": 1 }
]
}| Field | Type | Required | Description |
|---|---|---|---|
orderId | string | Yes | Order ID |
itemId | string | Yes | Basket item ID within the order |
partIndex | number | Yes | Index of the part in the basket item's parts array |
count | number | Yes | Number of additional instances to mark as cut (minimum: 1) |
Responses:
| Status | Description |
|---|---|
200 | Success — returns per-order results and any auto-promoted order IDs |
400 | Missing or invalid request body |
403 | Order not in your organisation |
404 | Order or item not found |
json
{
"success": true,
"data": {
"results": [{ "orderId": "66abc123", "success": true }],
"autoMarkedOrders": ["66abc123"]
}
}autoMarkedOrders contains IDs of orders automatically promoted to cut because all parts reached full quantity.
Mark Parts Complete
PATCH /ecommerce/orders/parts/mark-completeIncrements the numberComplete count for one or more parts. When all parts in an order reach their full quantity and the order is in cut status, the order is automatically set to complete.
Accepts the same request body format as Mark Parts Cut.
Responses:
| Status | Description |
|---|---|
200 | Success — returns per-order results and any auto-promoted order IDs |
400 | Missing or invalid request body |
403 | Order not in your organisation |
404 | Order or item not found |
Adjust Parts Cut
PATCH /ecommerce/orders/parts/adjust-cutDecrements cut counts for individual parts, or resets all cut counts for a basket item to zero. When a decrement causes any part to fall below its full quantity on an order with status cut, the order is automatically reverted to pending.
Request body — decrement:
json
{
"updates": [
{ "orderId": "66abc123", "itemId": "item-uuid", "partIndex": 0, "count": -1 }
]
}Request body — reset all cuts for an item:
json
{
"resetAll": { "orderId": "66abc123", "itemId": "item-uuid" }
}Responses:
| Status | Description |
|---|---|
200 | Success — returns per-order results and any orders reverted to pending |
400 | Missing or invalid request body |
403 | Order not in your organisation |
404 | Order or item not found |
json
{
"success": true,
"data": {
"results": [{ "orderId": "66abc123", "success": true }],
"revertedOrders": ["66abc123"]
}
}revertedOrders contains IDs of orders automatically reverted to pending because one or more parts fell below full quantity.