Ein Produkt hat eine SKU, einen Listenpreis und optionale Marke, Kategorie, Lagerstatus und zusätzliche Details. Preise werden immer für Sie berechnet: siehe Preise.
Produkte können auch familyId und orderStep (Standard 1) haben. Ein einzelnes Produkt enthält optionale family-Metadaten mit Namen, Variantenattribute und Voreinstellungen. Ein konfigurierter Preis enthält orderingFields, configurationRequired, definitionVersion und optional kundenspezifische quantityBreaks. Bestellfelder sind Eingaben des Käufers; details bleiben Produktfakten.
POST/api/v1/products/{id}/quote
Diese schreibgeschützte Operation akzeptiert { "customer": "ACME01", "qty": 3, "configuration": { "length": 2.75, "reference": "Kitchen" } }. customer ist für die Staff-Listenpreisberechnung optional. Schreibgeschützte Schlüssel können es verwenden. Sie gibt data.productId, qty, validierte fields, definitionVersion und price mit unit, line und der gewählten Spezifikation zurück. Fehlende erforderliche Felder führen zur Konfiguration für den Preis; ungültige Kombinationen werden abgelehnt. Es wird kein Warenkorb, Auftrag oder Zahlung erstellt. Verwenden Sie price.line anstelle der Multiplikation des Einzelpreises.
Imports akzeptieren optionale family, orderStep und orderingDefinition. Eine Definition verwendet das Schema in OpenAPI, erfordert Pro und globale Preisberechtigung und gibt ruleDrafts zur Überprüfung über den bestehenden Preisänderungsworkflow zurück. Fehlende Struktur belässt bestehende Einstellungen unverändert. Siehe trade catalogue setup.
Das ProduktobjektLink zu diesem Abschnitt#
{
"id": "3f8a2c1e-9b4d-4e6f-a7c8-1d2e3f4a5b6c",
"sku": "HX-8510",
"name": "Hex bolt M10 x 50 zinc",
"description": "Grade 8.8, zinc plated, fully threaded.",
"brandId": "a1b2c3d4-0000-4000-8000-000000000001",
"categoryId": "a1b2c3d4-0000-4000-8000-000000000002",
"manufacturerRef": "DIN933-M10-50",
"unitLabel": "Box of 100",
"minOrderQty": 1,
"availability": "in_stock",
"stockQty": 240,
"lowStockAt": null,
"leadTimeNote": null,
"dealText": null,
"active": true,
"visibility": "all",
"details": { "Finish": "Zinc", "Thread": "M10" },
"createdAt": "2026-08-14T09:30:00.000Z",
"updatedAt": "2026-09-20T16:02:11.000Z",
"price": {
"unit": 1850,
"line": 1850,
"list": 1850,
"discountBp": 0,
"source": "list",
"label": ""
}
}
| Feld | Typ | Beschreibung |
|---|---|---|
id | string | Produkt-ID. |
sku | string | Ihre Lagernummer. Eindeutig im Workspace, unabhängig von Groß-/Kleinschreibung. |
name | string | Produktname. |
description | string oder null | Längere Beschreibung. |
brandId, categoryId | string oder null | Siehe Marken und Kategorien. |
manufacturerRef | string oder null | Herstellerartikelnummer. Suchbar wie die SKU. |
unitLabel | string | Was eine Einheit ist, z. B. „Stück“ oder „Karton mit 100“. |
minOrderQty | integer | Kleinste Menge, die Käufer bestellen können. |
availability | string | not_tracked, in_stock, low_stock, out_of_stock oder made_to_order. Ein Label für Käufer: Jedes Produkt kann bestellt werden. Wenn Sie es ohne stockQty senden, löschen Sie den gespeicherten Zähler (Sie setzen den Status manuell). Mit stockQty berechnet es sich für Sie: 0 oder weniger ist out_of_stock, bis zum Mindestniveau ist low_stock, darüber ist in_stock. made_to_order wird niemals durch die Zahl verändert. |
stockQty | integer oder null | Einheiten auf Lager, oder null, wenn Sie dieses Produkt nicht zählen. Wenn festgelegt, folgt availability darauf. Wird ausgelassen, wenn Sie als Kunde abfragen (customer). |
lowStockAt | integer oder null | Dieses Produkt’s eigenes Niedrigbestandsniveau, oder null, um das Workspace-Niveau zu verwenden. Wird ausgelassen, wenn Sie als Kunde abfragen. |
leadTimeNote | string oder null | Z. B. „3–5 Arbeitstage“. |
dealText | string oder null | Eine Notiz neben dem Preis, z. B. „min 12 btls 45“. Der Titel ist das Deal-Label des Workspaces (Einstellungen, Kataloganzeige). |
active | boolean | Inaktive Produkte werden Käufern nicht angezeigt. |
visibility | string | all, oder selected, wenn nur ausgewählte Kunden es sehen können. |
details | object | Zusätzliche Spalten aus Importen, z. B. { "Finish": "Zinc" }. |
price | object oder null | Der Preis bei Menge 1 (oder qty). Siehe Preise. unit ist null, wenn das Produkt noch keinen Preis hat. |
Lagerbestände sind Ihre Aufgabe, um sie aktuell zu halten: Bestellungen ändern stockQty nie, senden Sie es also immer, wenn sich Ihr Lagerbestand ändert. Kunden sehen nur den availability-Status, nie die Zahl. Bei Null kann ein Produkt immer noch bestellt werden; es wird einfach als ausverkauft gekennzeichnet.
GET /products/{id} beinhaltet auch brand und category-Namen.
Bilder werden von der API nicht zurückgegeben. Sie können eines mit imageUrl beim Erstellen oder Aktualisieren festlegen.
Produkte auflisten oder suchenLink zu diesem Abschnitt#
GET/api/v1/products
Ohne q werden Produkte nach Namen aufgelistet. Mit q kommen die Ergebnisse in dieser Reihenfolge:
- exakte SKU oder Herstellerreferenz
- SKU oder Herstellerreferenz, die mit
qbeginnt - Volltextsuche in Namen, Marke, Kategorie und Beschreibung (3 oder mehr Zeichen; kürzere Abfragen passen zum Anfang des Namens)
Geben Sie customer an, um jede Zeile für diesen Kunden zu bewerten und nur die Produkte zurückzugeben, die dieser Kunde sehen kann (aktiv und entweder für alle oder für ihn ausgewählt). Das ist genau das, was der Kunde in ihrem Handelsportal sieht.
| Name | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
q | string | nein | SKU, Herstellerreferenz, Name, Marke oder Kategorie. Bis zu 200 Zeichen. |
category | string | nein | Kategorie-ID. |
brand | string | nein | Marken-ID. |
availability | string | nein | Einer der Verfügbarkheitswerte. |
active | true oder false | nein | true gibt nur aktive Produkte zurück. Standardmäßig true, wenn customer gesetzt ist; andernfalls werden alle Produkte zurückgegeben. |
customer | string | nein | Kunden-ID oder Kontocod. |
limit | integer | nein | 1 bis 50. Standard 50. |
offset | integer | nein | Zeilen, die übersprungen werden. Standard 0. |
curl "https://tradecatalog.app/api/v1/products?q=hex%20bolt&customer=OAK01&limit=20" \
-H "Authorization: Bearer $TRADECATALOG_API_KEY"
const params = new URLSearchParams({
q: 'hex bolt',
customer: 'OAK01',
limit: '20',
})
const res = await fetch(`https://tradecatalog.app/api/v1/products?${params}`, {
headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: products, nextOffset } = await res.json()
{
"data": [
{
"id": "3f8a2c1e-9b4d-4e6f-a7c8-1d2e3f4a5b6c",
"sku": "HX-8510",
"name": "Hex bolt M10 x 50 zinc",
"description": "Grade 8.8, zinc plated, fully threaded.",
"brandId": "a1b2c3d4-0000-4000-8000-000000000001",
"categoryId": "a1b2c3d4-0000-4000-8000-000000000002",
"manufacturerRef": "DIN933-M10-50",
"unitLabel": "Box of 100",
"minOrderQty": 1,
"availability": "in_stock",
"leadTimeNote": null,
"active": true,
"visibility": "all",
"details": { "Finish": "Zinc", "Thread": "M10" },
"createdAt": "2026-08-14T09:30:00.000Z",
"updatedAt": "2026-09-20T16:02:11.000Z",
"price": {
"unit": 1573,
"line": 1573,
"list": 1850,
"discountBp": 1500,
"source": "brand",
"label": "Brand discount −15%"
}
}
],
"nextOffset": null,
"currency": "GBP"
}
Ein Produkt abrufenLink zu diesem Abschnitt#
GET/api/v1/products/{id}
| Name | In | Typ | Erforderlich | Beschreibung |
|---|---|---|---|---|
id | path | string | ja | Produkt-ID. |
customer | query | string | nein | Kunden-ID oder Kontocod. Gibt 404 zurück, wenn dieser Kunde das Produkt nicht sehen kann. |
qty | query | integer | nein | Menge zum Preis, 1 bis 1.000.000. Standard 1. price.line ist unit × qty. |
curl "https://tradecatalog.app/api/v1/products/3f8a2c1e-9b4d-4e6f-a7c8-1d2e3f4a5b6c?customer=OAK01&qty=5" \
-H "Authorization: Bearer $TRADECATALOG_API_KEY"
const res = await fetch(
`https://tradecatalog.app/api/v1/products/${productId}?customer=OAK01&qty=5`,
{ headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` } },
)
const { data: product } = await res.json()
{
"data": {
"id": "3f8a2c1e-9b4d-4e6f-a7c8-1d2e3f4a5b6c",
"sku": "HX-8510",
"name": "Hex bolt M10 x 50 zinc",
"description": "Grade 8.8, zinc plated, fully threaded.",
"brandId": "a1b2c3d4-0000-4000-8000-000000000001",
"categoryId": "a1b2c3d4-0000-4000-8000-000000000002",
"manufacturerRef": "DIN933-M10-50",
"unitLabel": "Box of 100",
"minOrderQty": 1,
"availability": "in_stock",
"leadTimeNote": null,
"active": true,
"visibility": "all",
"details": { "Finish": "Zinc", "Thread": "M10" },
"createdAt": "2026-08-14T09:30:00.000Z",
"updatedAt": "2026-09-20T16:02:11.000Z",
"brand": "Northgate",
"category": "Bolts",
"price": {
"unit": 1573,
"line": 7865,
"list": 1850,
"discountBp": 1500,
"source": "brand",
"label": "Brand discount −15%"
}
},
"currency": "GBP"
}
Um ein Produkt anhand der SKU zu suchen, verwenden Sie GET /products?q=HX-8510: eine exakte SKU-Übereinstimmung wird immer zuerst angezeigt. Oder verwenden Sie GET /prices, das eine SKU annimmt.
Produkt erstellen oder aktualisierenLink zu diesem Abschnitt#
PUT/api/v1/productsSchreibschlüssel
Senden Sie bis zu 100 Produkte pro Aufruf. Jedes wird anhand der SKU mit einem vorhandenen Produkt abgeglichen, unabhängig von der Groß-/Kleinschreibung. Wenn keine Übereinstimmung vorliegt, wird ein neues Produkt erstellt. Marken und Kategorien werden anhand des Namens abgeglichen und erstellt, wenn sie nicht vorhanden sind.
Wie Listenpreise behandelt werdenLink zu diesem Abschnitt#
| Das Produkt… | Was passiert mit dem Preis |
|---|---|
| ist neu | Wird sofort bei listPrice live. |
| existiert, hat aber noch keinen Preis | Wird sofort bei listPrice live. |
| existiert mit dem gleichen Preis | Nichts. |
| existiert mit einem anderen Preis | Nichts ändert sich noch. Der neue Preis geht in eine Entwurfpreisänderung für diese Liste. priceChangeId ist der Entwurf für die Liste von listPrice. priceChangesByList hat einen Eintrag pro Liste, die sich geändert hat. |
Entwurfpreisänderungen sind der Weg, auf dem TradeCatalog einen Tippfehler daran hindert, Kunden zu erreichen. Der Besitzer oder das Personal überprüft den Entwurf in der App (Preisänderungen), prüft die größten Bewegungen und Warnungen, wählt ein Datum und ob Kunden per E-Mail benachrichtigt werden sollen, und veröffentlicht dann. Sie können einen Entwurf mit GET /price-changes/{id} vorab ansehen.
Eine Synchronisierung, ein Entwurf pro Liste
Ein Aufruf, der Preise ändert, startet einen Entwurf pro Liste, der „API-Import (Schlüsselname)“ genannt wird für Standard und mit dem Listenamen, wenn Sie mehr als eine senden. Um einen gesamten Sync in diesen Entwürfen zu halten, senden Sie priceChangeId (die Liste für listPrice) und drafts (die anderen Listen) aus dem ersten Aufruf mit jedem späteren Aufruf. Jeder muss immer noch ein Entwurf sein: sobald er veröffentlicht, storniert oder verworfen wurde, lassen Sie diese ID aus, um einen neuen zu starten.
KörperLink zu diesem Abschnitt#
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
products | array | ja | 1 bis 100 Produkte. SKUs müssen innerhalb des Aufrufs eindeutig sein. |
priceChangeId | string | nein | Ein Entwurf für die Liste listPrice aus einem früheren Aufruf. Geänderte Preise für diese Liste werden ihm hinzugefügt. 404, wenn er nicht existiert, 400, wenn er nicht mehr ein Entwurf ist. |
drafts | array | nein | Bis zu 30 Einträge von { list, priceChangeId }, damit andere Listen ihre eigenen Entwürfe fortsetzen. |
products[].sku | Zeichenfolge | ja | Bis zu 64 Zeichen. |
products[].name | Zeichenfolge | ja | Bis zu 200 Zeichen. |
products[].listPrice | Ganzzahl oder null | ja | Kleine Einheiten, 0 bis 1.000.000.000. null ist Preis auf Anfrage: Ein neues Produkt hat keinen Preis, ein vorhandenes behält seinen eigenen. |
products[].priceList | Zeichenfolge oder null | nein | Name einer vorhandenen Preisliste für listPrice. Weglassen oder null ist Standard. Ein unbekannter Name wird abgelehnt. |
products[].listPrices | Array | nein | Andere Listen, wie { list, price }. price ist kleine Einheiten, oder null, um diese Liste unverändert zu lassen. Ein unbekannter Name wird abgelehnt. |
products[].unitLabel | Zeichenfolge | ja | Bis zu 60 Zeichen, z. B. „Stück“. |
products[].brand | Zeichenfolge | nein | Markenname, bis zu 120 Zeichen. Wird gelöscht, wenn nicht angegeben. |
products[].category | Zeichenfolge | nein | Kategorienname, bis zu 120 Zeichen. Wird gelöscht, wenn nicht angegeben. |
products[].manufacturerRef | Zeichenfolge | nein | Bis zu 120 Zeichen. Wird gelöscht, wenn nicht angegeben. |
products[].description | Zeichenfolge | nein | Bis zu 4.000 Zeichen. Wird gelöscht, wenn nicht angegeben. |
products[].availability | Zeichenfolge | nein | Einer der Verfügbare-Werte. Wird beibehalten, wenn nicht angegeben; neue Produkte beginnen als not_tracked. |
products[].stockQty | Ganzzahl oder null | nein | Lagerbestand, von -1.000.000 bis 1.000.000.000. Setzt availability (siehe Produktobjekt); ein Status, den Sie zusammen mit ihm senden, wird ignoriert, außer made_to_order. Wird beibehalten, wenn nicht angegeben, es sei denn, Sie senden ein availability ohne es: Dann wird der Bestand gelöscht und dieser Status wird verwendet. null stoppt das Zählen; der availability, der in derselben Zeile gesendet wird, gilt, oder der aktuelle Status, falls keiner vorhanden ist. |
products[].lowStockAt | Ganzzahl oder null | nein | Niedrigster Lagerbestand für dieses Produkt, von 0. null verwendet den Arbeitsbereichsniveau (Einstellungen, 5 als Startwert). Wird beibehalten, wenn nicht angegeben. |
products[].leadTimeNote | Zeichenfolge | nein | Bis zu 200 Zeichen. Wird beibehalten, wenn nicht angegeben. |
products[].dealText | string | nein | Bis zu 200 Zeichen, neben dem Preis angezeigt. Wird beibehalten, wenn ausgelassen. |
products[].details | object | nein | Bis zu 30 zusätzliche Spalten: Namen bis zu 60 Zeichen, Werte bis zu 500. Ersetzt alle Details, wenn gesendet; wird beibehalten, wenn ausgelassen. |
products[].imageUrl | string | nein | Eine https Bild-URL. Wird im Hintergrund in TradeCatalog kopiert, wenn sie sich ändert. Wird beibehalten, wenn ausgelassen. |
products[].onlyFor | array | nein | Bis zu 200 Kontonummern oder Kontaktemails. Machen Sie das Produkt sichtbar nur für diese Kunden, ersetzt jede frühere Liste. Wird beibehalten, wenn ausgelassen. |
Senden Sie jedes Feld, das Sie betreut
brand, category, manufacturerRef und description werden wie gesendet geschrieben, also lässt man eines aus, wird es gelöscht. Senden Sie jedes Mal das vollständige Produkt, wie eine Tabellenzeile.
Diese können noch nicht über die API geändert werden: Produkt inaktivieren, minOrderQty, Bild entfernen, Lead-Time-Hinweis löschen und ein Kundenprodukt erneut für alle sichtbar machen. Machen Sie dies in der App.
curl -X PUT https://tradecatalog.app/api/v1/products \
-H "Authorization: Bearer $TRADECATALOG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"products": [
{
"sku": "HX-8510",
"name": "Hex bolt M10 x 50 zinc",
"listPrice": 1950,
"unitLabel": "Box of 100",
"brand": "Northgate",
"category": "Bolts",
"manufacturerRef": "DIN933-M10-50",
"availability": "in_stock",
"details": { "Finish": "Zinc", "Thread": "M10" }
},
{
"sku": "OAK-HNG-75",
"name": "Oak-finish butt hinge 75mm",
"listPrice": 695,
"unitLabel": "Pair",
"category": "Hinges",
"onlyFor": ["OAK01"]
}
]
}'
const res = await fetch('https://tradecatalog.app/api/v1/products', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ products }),
})
const { data: result } = await res.json()
if (result.priceChangeId)
console.log('Review the draft price change in TradeCatalog')
{
"data": {
"created": 1,
"updated": 1,
"priceChanges": 1,
"priceChangeId": "c4d5e6f7-1234-4abc-9def-0123456789ab",
"priceChangesByList": [
{
"list": "Standard",
"priceChangeId": "c4d5e6f7-1234-4abc-9def-0123456789ab"
}
],
"unknownCustomers": []
}
}
| Feld | Beschreibung |
|---|---|
created | Neue Produkte. |
updated | Bestehende Produkte, die anhand der SKU übereinstimmen. |
priceChanges | Wie viele Preise in einen Entwurf eingegangen sind, über alle Listen in dem Aufruf. |
priceChangeId | Der Entwurf für die listPrice-Liste, oder null, wenn diese Liste keine Änderungen hatte. |
priceChangesByList | Ein { list, priceChangeId } für jede Liste, die in diesem Aufruf einen Preisanpassung hatte. |
unknownCustomers | Werte in onlyFor, die keinem Kunden entsprachen (maximal 50). Diese Kunden erhielten keinen Zugriff. |
Änderungen werden im Aktivitätsprotokoll als Name des Schlüssels angezeigt, und die Suche wird sofort aktualisiert.
Marken und KategorienLink zu diesem Abschnitt#
GET/api/v1/brands
GET/api/v1/categories
Beide geben jede Zeile zurück, nach Namen sortiert, ohne Paging. Nutzen Sie die IDs mit den brand- und category-Filtern auf GET /products.
curl https://tradecatalog.app/api/v1/categories \
-H "Authorization: Bearer $TRADECATALOG_API_KEY"
const res = await fetch('https://tradecatalog.app/api/v1/categories', {
headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: categories } = await res.json()
{
"data": [
{ "id": "a1b2c3d4-0000-4000-8000-000000000002", "name": "Bolts" },
{ "id": "a1b2c3d4-0000-4000-8000-000000000003", "name": "Hinges" }
]
}
Um eine Marke oder Kategorie zu erstellen, benennen Sie sie auf einem Produkt in PUT /products.