Buyers place orders in their trade portal. TradeCatalog doesn’t take payment: you invoice as usual. Use the API to pull orders into your ERP or accounts system and to keep their status up to date.
Order statusesLink to this section#
| Status | Meaning | Can move to |
|---|---|---|
new | Just placed | acknowledged, cancelled |
acknowledged | You’ve seen it and it’s being picked | dispatched, cancelled |
dispatched | On its way | completed |
completed | Done | nothing |
cancelled | Cancelled, with a reason | nothing |
List ordersLink to this section#
GET/api/v1/orders
Orders newest first. The list covers the 200 most recent orders that match your filters. To pick up new orders, poll with status=new.
| Name | Type | Required | Description |
|---|---|---|---|
status | string | no | One of the statuses above. |
customer | string | no | Customer id or account code. |
limit | integer | no | 1 to 100. Default 100. |
offset | integer | no | Rows to skip. Default 0. |
curl "https://tradecatalog.app/api/v1/orders?status=new" \
-H "Authorization: Bearer $TRADECATALOG_API_KEY"
const res = await fetch('https://tradecatalog.app/api/v1/orders?status=new', {
headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: orders } = await res.json()
{
"data": [
{
"id": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
"reference": "SO-000142",
"status": "new",
"total": 31460,
"currency": "GBP",
"poNumber": "PO-88231",
"createdAt": "2026-09-27T07:48:12.000Z",
"customer": "Oakfield Joinery"
}
],
"nextOffset": null
}
| Field | Type | Description |
|---|---|---|
id | string | Order id. |
reference | string | Order number buyers see, like SO-000142. |
status | string | See statuses. |
total | integer | Order total before tax, in minor units of currency. |
currency | string | Fixed when the order was placed. |
poNumber | string or null | The buyer’s purchase order number. |
createdAt | string | When it was placed. |
customer | string | Customer name. Get the account code from GET /orders/{id}. |
Get one orderLink to this section#
GET/api/v1/orders/{id}
The order with its lines and status history.
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Order id. |
curl https://tradecatalog.app/api/v1/orders/5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e \
-H "Authorization: Bearer $TRADECATALOG_API_KEY"
const res = await fetch(`https://tradecatalog.app/api/v1/orders/${orderId}`, {
headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: order } = await res.json()
{
"data": {
"id": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
"customerAccountId": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
"placedBy": "Fq3L0cZ7tN1pW9aYx2Rk",
"reference": "SO-000142",
"poNumber": "PO-88231",
"notes": "Deliver to the Hebden Road site, gate 2.",
"status": "new",
"cancelReason": null,
"currency": "GBP",
"total": 31460,
"createdAt": "2026-09-27T07:48:12.000Z",
"updatedAt": "2026-09-27T07:48:12.000Z",
"customer": { "name": "Oakfield Joinery", "accountCode": "OAK01" },
"lines": [
{
"id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
"orderId": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
"productId": "3f8a2c1e-9b4d-4e6f-a7c8-1d2e3f4a5b6c",
"sku": "HX-8510",
"name": "Hex bolt M10 x 50 zinc",
"unitLabel": "Box of 100",
"qty": 20,
"unitPrice": 1573,
"listPrice": 1850,
"priceLabel": "Brand discount −15%",
"lineTotal": 31460
}
],
"events": [
{
"status": "new",
"note": null,
"createdAt": "2026-09-27T07:48:12.000Z",
"actor": "Sam Patel"
}
]
}
}
| Field | Description |
|---|---|
customerAccountId | The customer’s id. |
placedBy | Id of the buyer who placed it. |
notes | The buyer’s delivery or order notes. |
cancelReason | Why it was cancelled, if it was. |
lines | What was ordered at what price. A snapshot: later price changes never alter it. productId is null if the product has since been deleted. |
lines[].priceLabel | The rule that set the price, as on Prices. |
events | Status history, oldest first. actor is the person’s name, or null. |
Move an order onLink to this section#
PATCH/api/v1/orders/{id}write key
Changes an order’s status. Returns the updated order in the same shape as GET /orders/{id}.
- Only the moves in the status table are allowed. Anything else is
400 invalid_request. - Cancelling needs a
note, which is shown to the buyer as the reason. - The buyer who placed the order gets an email each time, with the note if you send one.
- The change is recorded in the order’s history against the owner who made the key.
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Order id. |
status | body | string | yes | The new status. |
note | body | string | no | Up to 1,000 characters. Required when cancelling. |
curl -X PATCH https://tradecatalog.app/api/v1/orders/5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e \
-H "Authorization: Bearer $TRADECATALOG_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "dispatched", "note": "DPD tracking 1554 2210 0931" }'
const res = await fetch(`https://tradecatalog.app/api/v1/orders/${orderId}`, {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ status: 'acknowledged' }),
})
const { data: order } = await res.json()
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | The move isn’t allowed, e.g. “An order that is received can’t be marked completed”, or cancelling without a note. |
| 403 | insufficient_scope | The key is read-only. |
| 404 | not_found | No such order in this workspace. |
Retrying a PATCH that already succeeded returns 400 invalid_request, because the order has moved on. Check the order’s status before treating that as a failure.
Not availableLink to this section#
The API can’t place orders for a buyer, or change an order’s lines, quantities or prices. Buyers place orders in their trade portal; changes to an order are agreed with the customer and invoiced in your own system.