Skip to content

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:

HeaderDescription
Content-Typeapplication/json
X-SmartCut-TimestampUnix timestamp (ms) of the delivery attempt
X-SmartCut-DeliveryUnique 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

EventDescription
order.createdA new order has been placed and payment confirmed
order.status_changedThe order status has been updated
inventory.decrementedStock 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

FieldTypeDescription
eventstringOne of order.created, order.status_changed
orderIdstringUnique order identifier
organisationIdstringYour organisation ID
timestampstringISO 8601 timestamp of when the webhook was sent

data

FieldTypeRequiredDescription
statusstringYesCurrent order status
previousStatusstringNoPrevious status (present on order.status_changed)
paymentStatusstringNoPayment status
itemCountnumberYesNumber of line items in the order
partsCountnumberYesTotal number of individual parts
createdAtstringYesISO 8601 timestamp of order creation
itemsarrayYesPer-item part lists — use with the ecommerce API (see below)
resultsarrayNoOptimisation 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.

FieldTypeDescription
itemIdstringBasket item ID — use as itemId in mark-cut / mark-complete / adjust-cut requests
includeOffcutsbooleanWhether the customer opted in to receive offcuts for this job (false if not opted in)
partsarrayInput parts for this item

data.items[].parts[]

FieldTypeDescription
partIndexnumberIndex of this part — use as partIndex in ecommerce API requests
labelstringPart label/name (if provided)
lnumberPart length (long side, mm)
wnumberPart width (short side, mm)
quantitynumberNumber of this part required
materialstringMaterial name (if specified)
thicknessnumberMaterial 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.

FieldTypeDescription
jobIdnumberJob identifier
sawobjectSaw configuration used
stockarrayStock items
partsarrayParts with coordinates and properties
cutsarrayCut instructions
offcutsarrayRemaining offcut pieces
unusablePartsarrayParts that could not be placed
metadataobjectComplete analysis and metrics (efficiency, waste, costs, material summary)

data.customer

FieldTypeRequiredDescription
namestringYesCustomer full name
emailstringYesCustomer email address
phonestringNoCustomer phone number

data.pricing

FieldTypeRequiredDescription
totalnumberYesOrder total
currencystringYesISO 4217 currency code
itemsSubtotalnumberNoSubtotal before shipping
shippingCostnumberNoShipping cost

data.shipping

Optional. Present when shipping details are available.

FieldTypeRequiredDescription
methodstringNoShipping method name
address.line1stringYesStreet address
address.citystringYesCity
address.postalCodestringYesPostal/ZIP code
address.countrystringYesISO 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[]

FieldTypeRequiredDescription
stockIdstringYesStock item ID
stockNamestringNoHuman-readable stock name
materialstringNoMaterial type
thicknessnumberNoMaterial thickness
dimensions.lengthnumberNoStock length
dimensions.widthnumberNoStock width
previousQuantitynumberYesQuantity before decrement
newQuantitynumberYesQuantity after decrement
decrementedBynumberYesNumber of units consumed
deletedbooleanNoWhether 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/:id

Updates the status of an order.

Request body:

FieldTypeRequiredDescription
statusstringYesOne of pending, cut, complete, dispatched, cancelled
updateInventorybooleanNoDecrement inventory stock when marking as cut (default: false)
forceOverwritebooleanNoSkip partial progress warning when marking as cut (default: false)
resetCutsbooleanNoReset all part cut counts when reverting to pending (default: false)

Example:

json
{ "status": "complete" }

Responses:

StatusDescription
200Order updated — returns the updated order document
400Invalid status value
403Order not in your organisation
404Order not found
409Partial 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-cut

Increments 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 }
  ]
}
FieldTypeRequiredDescription
orderIdstringYesOrder ID
itemIdstringYesBasket item ID within the order
partIndexnumberYesIndex of the part in the basket item's parts array
countnumberYesNumber of additional instances to mark as cut (minimum: 1)

Responses:

StatusDescription
200Success — returns per-order results and any auto-promoted order IDs
400Missing or invalid request body
403Order not in your organisation
404Order 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-complete

Increments 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:

StatusDescription
200Success — returns per-order results and any auto-promoted order IDs
400Missing or invalid request body
403Order not in your organisation
404Order or item not found

Adjust Parts Cut

PATCH /ecommerce/orders/parts/adjust-cut

Decrements 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:

StatusDescription
200Success — returns per-order results and any orders reverted to pending
400Missing or invalid request body
403Order not in your organisation
404Order 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.