List prices never change silently in TradeCatalog. New list prices go into a price change: a draft that staff review, date and publish, optionally emailing every customer their own new prices.
How price changes are madeLink to this section#
- From the API:
PUT /productswith a newlistPricefor an existing product adds it to a draft and returns the draft’spriceChangeId. - From the app: spreadsheet imports, and bulk changes like “+4% on the Bolts category”.
Publishing, scheduling and cancelling happen in the app, under Price changes. There’s no API for them: a person always checks a price change before customers see it.
StatusesLink to this section#
| Status | Meaning |
|---|---|
draft | Not published. No prices have changed. |
scheduled | Published with a future date. Prices change on that date. |
live | In effect. |
cancelled | Cancelled before it took effect. |
The price change objectLink to this section#
{
"id": "c4d5e6f7-1234-4abc-9def-0123456789ab",
"source": "import",
"title": "API import (Sage stock sync)",
"supplierNote": null,
"status": "draft",
"effectiveAt": null,
"notifyCustomers": true,
"raiseFixedPrices": false,
"createdBy": "Fq3L0cZ7tN1pW9aYx2Rk",
"publishedAt": null,
"cancelledAt": null,
"createdAt": "2026-09-27T02:00:14.000Z",
"items": 38
}
| Field | Type | Description |
|---|---|---|
source | string | import (spreadsheet or API) or bulk (a percentage change made in the app). |
title | string | Shown to staff. API drafts are titled “API import (key name)”. |
supplierNote | string or null | Message to customers in the price-change email. |
status | string | See statuses. |
effectiveAt | string or null | When the new prices apply. |
notifyCustomers | boolean | Whether customers are emailed their new prices. |
raiseFixedPrices | boolean | Whether agreed prices rise by the same percentage. |
createdBy | string or null | Id of the person who made it. For API drafts, the owner who made the key. |
items | integer | How many products it changes. Only in the list. |
List price changesLink to this section#
GET/api/v1/price-changes
Newest first. Covers the 100 most recent price changes.
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | no | 1 to 100. Default 100. |
offset | integer | no | Rows to skip. Default 0. |
curl https://tradecatalog.app/api/v1/price-changes \
-H "Authorization: Bearer $TRADECATALOG_API_KEY"
const res = await fetch('https://tradecatalog.app/api/v1/price-changes', {
headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: changes } = await res.json()
const drafts = changes.filter((change) => change.status === 'draft')
Preview a price changeLink to this section#
GET/api/v1/price-changes/{id}
Everything staff see before publishing: how many prices go up and down, the biggest moves, warnings, and how many agreed prices it touches. Pass customer to see that customer’s own prices before and after.
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Price change id. |
customer | query | string | no | Customer id or account code. |
curl "https://tradecatalog.app/api/v1/price-changes/c4d5e6f7-1234-4abc-9def-0123456789ab?customer=OAK01" \
-H "Authorization: Bearer $TRADECATALOG_API_KEY"
const res = await fetch(
`https://tradecatalog.app/api/v1/price-changes/${priceChangeId}?customer=OAK01`,
{ headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` } },
)
const { data: preview } = await res.json()
if (preview.warnings.length)
console.warn('Check these before publishing', preview.warnings)
{
"data": {
"change": {
"id": "c4d5e6f7-1234-4abc-9def-0123456789ab",
"source": "import",
"title": "API import (Sage stock sync)",
"supplierNote": null,
"status": "draft",
"effectiveAt": null,
"notifyCustomers": true,
"raiseFixedPrices": false,
"createdBy": "Fq3L0cZ7tN1pW9aYx2Rk",
"publishedAt": null,
"cancelledAt": null,
"createdAt": "2026-09-27T02:00:14.000Z"
},
"counts": { "total": 38, "up": 36, "down": 2 },
"biggest": [
{
"productId": "3f8a2c1e-9b4d-4e6f-a7c8-1d2e3f4a5b6c",
"oldPrice": 1850,
"newPrice": 1950,
"sku": "HX-8510",
"name": "Hex bolt M10 x 50 zinc",
"changeBp": 541
}
],
"warnings": [],
"fixedAffected": 3,
"customer": {
"name": "Oakfield Joinery",
"rows": [
{
"sku": "HX-8510",
"name": "Hex bolt M10 x 50 zinc",
"before": 1573,
"after": 1658,
"label": "Brand discount −15%"
}
]
}
},
"currency": "GBP"
}
| Field | Description |
|---|---|
counts | Products in the change, and how many go up and down. |
biggest | Up to 20 items with the largest percentage move. changeBp is the change in basis points (541 = +5.41%). |
warnings | Items that move by more than 30% either way, or go to zero. Usually typos. |
fixedAffected | Agreed prices on these products that are valid now. They only change if the change raises agreed prices. |
customer | With customer: that customer’s unit prices before and after, for up to the first 50 items. null without it. |
404 not_found if there’s no such price change in this workspace.