CloudDespatch API

Send orders, products and stock from any till, webstore or system into CloudDespatch, and get tracking back when parcels leave. JSON over HTTPS, one key per system.

Base URL
https://cd.shapeventures.net/api/v1

Authentication

Create a key in Developers, API keys inside your CloudDespatch account. Send it with every request in the Authorization header. Keys start with cd_live_ and carry the company they belong to.

Header
Authorization: Bearer cd_live_yourcompany_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Each key has permissions. Give it only what its system needs: a till key needs inventory:write, products:read and orders:read, nothing else.

PermissionAllows
orders:readList and read orders
orders:writeCreate orders and send bulk imports
products:readRead products and stock levels
products:writeCreate and update products
inventory:writeAdjust stock and record till sales
shipments:readRead tracking and despatch codes
webhooks:manageManage webhook endpoints

Errors

Errors use normal HTTP status codes and always have the same shape, with a message you can show to staff.

422 Unprocessable Entity
{
  "error": {
    "code": "invalid",
    "message": "Every item needs a sku and a qty of at least 1"
  }
}
StatuscodeMeaning
401unauthorizedMissing, wrong or revoked key
403forbidden The key doesn't have the permission this endpoint needs
404not_foundNo order or product with that id or SKU
422invalid The request is missing something or would break a rule, such as stock going below zero

Rate limits

600 requests a minute per key. Every response carries x-ratelimit-limit and x-ratelimit-remaining. For large catalogues, send up to 500 rows at a time to POST /imports.

Sources and duplicates

Tell us where an order came from with source, and send your own order number as external_ref. If the same source and reference arrive twice, for example after a retry, you get the original order back with "duplicate": true and nothing new is created.

sourceUse it for
posTill / POS system
woocommerceWooCommerce (WordPress)
magentoMagento / Adobe Commerce
prestashopPrestaShop
opencartOpenCart
customCustom software
csvFile import

Data use rules

Orders from marketplaces carry buyers' personal details. Marketplaces set strict rules on them, and so do we. Everyone who holds a CloudDespatch API key agrees to these rules when the key is created.

  • Delete buyer details after 30 days Personal details of marketplace buyers (names, addresses, phone numbers, emails) must be deleted or anonymised within 30 days of the order shipping.
  • Keep logs for 12 months Keep request and access logs for 12 months, then delete them.
  • Use buyer data only to fulfil the order Never use marketplace buyer data for marketing, profiling or building data sets, and never pass it to anyone who would.
  • Report breaches within 24 hours Tell us at security@clouddespatch.com within 24 hours of finding any breach or suspected leak of data from CloudDespatch.
  • Lock accounts down Use multi-factor sign-in on every account that can create keys, and give each key only the permissions its system needs.
CloudDespatch applies the same rules to its own systems: buyer details are anonymised 30 days after shipment, logs are kept for 12 months, and every account can use multi-factor sign-in.

Try it

This console calls the live API with the demo company's key. Pick an example or write your own request. Orders you create here appear in the demo company's account.

Response appears here.

{ }

Account

GET /api/v1/me Any key

Check a key works and see which company and permissions it has.

Request
curl -X GET https://cd.shapeventures.net/api/v1/me \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "data": {
    "company": "Acme Outdoor",
    "key": "Shop till",
    "scopes": [
      "inventory:write",
      "products:read",
      "orders:read"
    ],
    "tenant": "acme"
  }
}

Orders

GET /api/v1/orders Needs orders:read

List orders, newest first.

FieldInDescription
status query new, awaiting_pick, picking, packed, despatched, on_hold or cancelled
source query Only orders from this source, e.g. pos or woocommerce
limit query 1 to 200, default 50
Request
curl -X GET https://cd.shapeventures.net/api/v1/orders \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "count": 1,
  "data": [
    {
      "address": {
        "city": "Leeds",
        "country": "United Kingdom",
        "line1": "1 Main Street",
        "line2": "",
        "postcode": "LS1 1AA"
      },
      "customer": {
        "email": "jane@example.co.uk",
        "name": "Jane Doe",
        "phone": ""
      },
      "despatch_code": null,
      "despatched_at": null,
      "external_ref": "WC-1001",
      "id": "CD-48231",
      "items": [
        {
          "name": "Trail Flask 750ml",
          "price": 18.0,
          "qty": 2,
          "sku": "TFM-100"
        }
      ],
      "placed_at": "2026-10-26T11:42:07Z",
      "service": null,
      "shipping_paid": 3.99,
      "source": "woocommerce",
      "status": "new",
      "total": 39.99,
      "tracking": null
    }
  ]
}
GET /api/v1/orders/:id Needs orders:read

Fetch one order by its CloudDespatch id.

FieldInDescription
idrequired path CloudDespatch order id, e.g. CD-48231
Request
curl -X GET https://cd.shapeventures.net/api/v1/orders/CD-48231 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "data": {
    "address": {
      "city": "Leeds",
      "country": "United Kingdom",
      "line1": "1 Main Street",
      "line2": "",
      "postcode": "LS1 1AA"
    },
    "customer": {
      "email": "jane@example.co.uk",
      "name": "Jane Doe",
      "phone": ""
    },
    "despatch_code": null,
    "despatched_at": null,
    "external_ref": "WC-1001",
    "id": "CD-48231",
    "items": [
      {
        "name": "Trail Flask 750ml",
        "price": 18.0,
        "qty": 2,
        "sku": "TFM-100"
      }
    ],
    "placed_at": "2026-10-26T11:42:07Z",
    "service": null,
    "shipping_paid": 3.99,
    "source": "woocommerce",
    "status": "new",
    "total": 39.99,
    "tracking": null
  }
}
POST /api/v1/orders Needs orders:write

Create an order from your shop or system. Returns 201, or 200 with "duplicate": true if the same source and external_ref was sent before.

FieldInDescription
source body Where it came from: pos, woocommerce, magento, prestashop, opencart, custom (default)
external_ref body Your own order number. Used to stop duplicates.
customer.namerequired body Buyer's name
customer.email / customer.phone body Contact details
address.line1, line2, city, postcode, country body Delivery address. country defaults to United Kingdom
items[]required body At least one item: sku (required), qty (at least 1), price and name (default to the product's)
shipping_paid body What the buyer paid for delivery
status body new (default), awaiting_pick, despatched or on_hold
service body Courier service, e.g. Royal Mail Tracked 48
priority, tags, notes body true/false, list of tags, free text
Request
curl -X POST https://cd.shapeventures.net/api/v1/orders \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"address":{"city":"Leeds","line1":"1 Main Street","postcode":"LS1 1AA"},"customer":{"email":"jane@example.co.uk","name":"Jane Doe"},"external_ref":"WC-1001","items":[{"qty":2,"sku":"TFM-100"}],"shipping_paid":3.99,"source":"woocommerce"}'
Response
{
  "data": {
    "address": {
      "city": "Leeds",
      "country": "United Kingdom",
      "line1": "1 Main Street",
      "line2": "",
      "postcode": "LS1 1AA"
    },
    "customer": {
      "email": "jane@example.co.uk",
      "name": "Jane Doe",
      "phone": ""
    },
    "despatch_code": null,
    "despatched_at": null,
    "external_ref": "WC-1001",
    "id": "CD-48231",
    "items": [
      {
        "name": "Trail Flask 750ml",
        "price": 18.0,
        "qty": 2,
        "sku": "TFM-100"
      }
    ],
    "placed_at": "2026-10-26T11:42:07Z",
    "service": null,
    "shipping_paid": 3.99,
    "source": "woocommerce",
    "status": "new",
    "total": 39.99,
    "tracking": null
  }
}

Tills

POST /api/v1/sales Needs inventory:write

Record an in-store sale. Takes the items off stock at the location and stores a despatched order with source pos. Returns 422 if stock is short.

FieldInDescription
items[]required body sku and qty for each line; price defaults to the product's
location body Stock location name; defaults to your main warehouse
external_ref body Receipt number, to stop duplicates
customer body Defaults to Walk-in customer
Request
curl -X POST https://cd.shapeventures.net/api/v1/sales \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_ref":"TILL-0001","items":[{"qty":1,"sku":"TFM-100"}],"location":"Leeds warehouse"}'
Response
{
  "data": {
    "address": {
      "city": "Leeds",
      "country": "United Kingdom",
      "line1": "1 Main Street",
      "line2": "",
      "postcode": "LS1 1AA"
    },
    "customer": {
      "email": "jane@example.co.uk",
      "name": "Jane Doe",
      "phone": ""
    },
    "despatch_code": null,
    "despatched_at": null,
    "external_ref": "WC-1001",
    "id": "CD-48231",
    "items": [
      {
        "name": "Trail Flask 750ml",
        "price": 18.0,
        "qty": 2,
        "sku": "TFM-100"
      }
    ],
    "placed_at": "2026-10-26T11:42:07Z",
    "service": null,
    "shipping_paid": 3.99,
    "source": "woocommerce",
    "status": "new",
    "total": 39.99,
    "tracking": null
  }
}

Products and stock

GET /api/v1/products Needs products:read

List products with stock per location.

FieldInDescription
q query Search by name or SKU
Request
curl -X GET https://cd.shapeventures.net/api/v1/products \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "count": 1,
  "data": [
    {
      "barcode": "5001234567890",
      "category": "Drinkware",
      "cost": 7.12,
      "name": "Trail Flask 750ml",
      "price": 18.0,
      "reorder_point": 20,
      "sku": "TFM-100",
      "status": "active",
      "stock": {
        "Amazon FBA": 30,
        "Leeds warehouse": 120
      },
      "weight_g": 340
    }
  ]
}
GET /api/v1/products/:sku Needs products:read

Fetch one product by SKU.

FieldInDescription
skurequired path Product SKU
Request
curl -X GET https://cd.shapeventures.net/api/v1/products/TFM-100 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "data": {
    "barcode": "5001234567890",
    "category": "Drinkware",
    "cost": 7.12,
    "name": "Trail Flask 750ml",
    "price": 18.0,
    "reorder_point": 20,
    "sku": "TFM-100",
    "status": "active",
    "stock": {
      "Amazon FBA": 30,
      "Leeds warehouse": 120
    },
    "weight_g": 340
  }
}
PUT /api/v1/products/:sku Needs products:write

Create a product, or update the fields you send. 201 when created, 200 when updated.

FieldInDescription
skurequired path Product SKU
name body Required when creating
category, price, cost, barcode body Product details
weight_g, reorder_point body Whole numbers
stock body Opening stock at your main warehouse, used only when creating
Request
curl -X PUT https://cd.shapeventures.net/api/v1/products/TFM-100 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"category":"Gifts","name":"Gift card £20","price":20,"stock":50}'
Response
{
  "data": {
    "barcode": "5001234567890",
    "category": "Drinkware",
    "cost": 7.12,
    "name": "Trail Flask 750ml",
    "price": 18.0,
    "reorder_point": 20,
    "sku": "TFM-100",
    "status": "active",
    "stock": {
      "Amazon FBA": 30,
      "Leeds warehouse": 120
    },
    "weight_g": 340
  }
}
POST /api/v1/inventory/adjust Needs inventory:write

Change stock by an amount, or set it to an exact count. Stock can't go below zero (422).

FieldInDescription
skurequired body Product SKU
delta body Amount to add (negative to remove)
set body Exact level after a stock count; overrides delta
location body Defaults to your main warehouse
reason body Shown in stock history
Request
curl -X POST https://cd.shapeventures.net/api/v1/inventory/adjust \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"delta":-2,"reason":"Damaged in store","sku":"TFM-100"}'
Response
{
  "data": {
    "barcode": "5001234567890",
    "category": "Drinkware",
    "cost": 7.12,
    "name": "Trail Flask 750ml",
    "price": 18.0,
    "reorder_point": 20,
    "sku": "TFM-100",
    "status": "active",
    "stock": {
      "Amazon FBA": 30,
      "Leeds warehouse": 120
    },
    "weight_g": 340
  }
}

Bulk

POST /api/v1/imports Needs orders:write

Send up to 500 products and 500 orders in one request. Each row is handled on its own; problems are reported by row.

FieldInDescription
products[] body Same fields as PUT /products/:sku, plus sku
orders[] body Same fields as POST /orders
Request
curl -X POST https://cd.shapeventures.net/api/v1/imports \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"orders":[{"customer":{"name":"Ali Khan"},"external_ref":"B-1","items":[{"qty":1,"sku":"MUG-01"}]}],"products":[{"name":"Enamel mug","price":12,"sku":"MUG-01"}]}'
Response
{
  "data": {
    "orders": {
      "created": 1,
      "duplicates": 0,
      "errors": []
    },
    "products": {
      "created": 1,
      "duplicates": 0,
      "errors": []
    }
  }
}

Shipping

GET /api/v1/shipments/:order_id Needs shipments:read

Tracking and despatch details for an order, to show your customer.

FieldInDescription
order_idrequired path CloudDespatch order id
Request
curl -X GET https://cd.shapeventures.net/api/v1/shipments/CD-48215 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "data": {
    "courier": "Royal Mail",
    "despatch_code": "RM48-LS1-2610-K",
    "despatched_at": "2026-10-26T15:02:11Z",
    "order_id": "CD-48215",
    "service": "Royal Mail Tracked 48",
    "status": "despatched",
    "tracking": "TT927510620GB"
  }
}

Webhooks

Add an endpoint in Developers, Webhooks and choose events. We send a JSON POST when each one happens, so your systems never need to ask.

EventSent when
order.createdA new order arrives from any channel or the API
order.updatedAn order's status, service or address changes
order.despatchedA parcel leaves, with tracking and despatch code
stock.lowStock at a location drops to its reorder point
product.updatedA product is created or changed
return.requestedA customer asks to send something back

Payload

POST to your endpoint
{
  "data": {
    "courier": "Royal Mail",
    "despatch_code": "RM48-LS1-2610-K",
    "despatched_at": "2026-10-26T15:02:11Z",
    "order_id": "CD-48215",
    "service": "Royal Mail Tracked 48",
    "status": "despatched",
    "tracking": "TT927510620GB"
  },
  "event": "order.despatched",
  "sent_at": "2026-10-26T15:02:11Z",
  "tenant": "acme"
}

Check the signature

Each request has an x-clouddespatch-signature header: the HMAC-SHA256 of the raw body using the endpoint's signing secret, as lowercase hex. Reject any request where it doesn't match.

PHP
$expected = hash_hmac('sha256', file_get_contents('php://input'), $signingSecret);\nif (!hash_equals($expected, $_SERVER['HTTP_X_CLOUDDESPATCH_SIGNATURE'] ?? '')) {\n    http_response_code(401);\n    exit;\n}
Node.js
const crypto = require('crypto');\nconst expected = crypto.createHmac('sha256', signingSecret).update(rawBody).digest('hex');\nconst ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.get('x-clouddespatch-signature') || ''));

Webstore plugins

For WooCommerce (WordPress), Magento, PrestaShop and OpenCart you don't need to write code. Install our plugin, paste your account address and a connection key, and it uses this API for you:

  • each paid order goes to POST /orders with the shop's order number as external_ref
  • stock stays in step through GET /products and POST /inventory/adjust
  • orders are marked shipped in the shop with tracking when order.despatched arrives

Set it up from Developers, Plugins in your account; it walks you through each step and tests the connection.

Tills and POS

Any till that can make an HTTPS request can share stock with your online channels. Create a till key, then:

  • send each sale to POST /sales; it takes the items off stock and appears in sales reports
  • read stock with GET /products so the till knows what sold out online
  • after a stock count, send exact numbers with set on POST /inventory/adjust

File import

If a system can only export a spreadsheet, use Import in your account. Upload a CSV, match its columns to ours, check the rows, and import. It uses the same rules as the API, including duplicate checks on order references.