Un prodotto ha un SKU, un prezzo di listino e un marchio, una categoria, uno stato di magazzino e dettagli aggiuntivi opzionali. I prezzi vengono sempre calcolati per voi: consulta Prezzi.
I prodotti possono anche avere familyId e orderStep (valore predefinito 1). Una lettura di un singolo prodotto include metadati opzionali family con il suo nome, i nomi degli attributi delle varianti e i valori predefiniti. Un prezzo configurato contiene orderingFields, configurationRequired, definitionVersion e un prezzo clienti opzionale quantityBreaks. I campi di ordinamento sono input dell'acquirente; details rimangono fatti del prodotto.
POST/api/v1/products/{id}/quote
Questa operazione in sola lettura accetta { "customer": "ACME01", "qty": 3, "configuration": { "length": 2.75, "reference": "Kitchen" } }. customer è opzionale per il listino prezzi del personale. Le chiavi in sola lettura possono utilizzarla. Restituisce data.productId, qty, fields validati, definitionVersion e price con unit, line e la specifica scelta. I campi obbligatori mancanti restituiscono Configura per il prezzo; le combinazioni non valide vengono rifiutate. Non crea un carrello, un ordine o un pagamento. Usa price.line invece di moltiplicare il prezzo unitario.
Gli import accettano family, orderStep e orderingDefinition opzionali. Una definizione utilizza lo schema in OpenAPI, richiede il piano Pro e il permesso per i prezzi globali, e restituisce ruleDrafts per la revisione attraverso il flusso di lavoro esistente per modifiche al prezzo. La struttura omessa lascia invariati i settaggi esistenti. Vedi setup del catalogo commerciale.
L'oggetto prodottoCollegamento a questa sezione#
{
"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": ""
}
}
| Campo | Tipo | Descrizione |
|---|---|---|
id | stringa | ID del prodotto. |
sku | stringa | Il tuo codice di magazzino. Unico nello spazio di lavoro, ignorando la maiuscola/minuscola. |
name | string | Nome del prodotto. |
description | string o null | Descrizione più lunga. |
brandId, categoryId | string o null | Vedi brand e categorie. |
manufacturerRef | string o null | Numero di pezzo del produttore. Cercabile come lo SKU. |
unitLabel | string | Cosa rappresenta una singola unità, ad esempio "Ogni" o "Scatola da 100". |
minOrderQty | intero | Quantità minima che i clienti possono ordinare. |
availability | string | not_tracked, in_stock, low_stock, out_of_stock o made_to_order. Un'etichetta solo per i compratori: ogni prodotto può essere ordinato. Inviandolo senza un stockQty si cancella il conteggio memorizzato (stai impostando lo stato manualmente). Con un stockQty viene calcolato per te: 0 o meno è out_of_stock, fino al livello minimo è low_stock, sopra di esso è in_stock. made_to_order non viene mai modificato dal numero. |
stockQty | integer o null | Unità disponibili, o null quando non conti questo prodotto. Quando impostato, availability lo segue. Escluso quando lo chiedi come cliente (customer). |
lowStockAt | integer o null | Livello di scorta minimo del prodotto, o null per utilizzare il livello dell'ambiente di lavoro. Escluso quando lo chiedi come cliente. |
leadTimeNote | string o null | Ad esempio "3-5 giorni lavorativi". |
dealText | string o null | Un nota visualizzata accanto al prezzo, ad esempio "min 12 btls 45". La sua intestazione è l'etichetta di offerta del workspace (Impostazioni, Visualizzazione catalogo). |
active | boolean | I prodotti non attivi sono nascosti ai clienti. |
visibility | string | all, o selected quando solo i clienti selezionati possono vederlo. |
details | object | Colonne aggiuntive dagli import, come { "Finish": "Zinc" }. |
price | object o null | Il prezzo alla quantità 1 (o qty). Vedere Prezzi. unit è null quando il prodotto non ha ancora un prezzo. |
I conteggi di magazzino sono tuoi da tenere aggiornati: gli ordini non cambiano mai stockQty, quindi invialo dal tuo sistema di magazzino ogni volta che cambia. I clienti vedono solo lo stato availability, mai il numero. A zero un prodotto può ancora essere ordinato; è semplicemente contrassegnato come esaurito.
GET /products/{id} include anche i nomi brand e category.
Le immagini non vengono restituite dall'API. Puoi impostarne una con imageUrl quando crei o aggiorni.
Elenco o ricerca prodottiCollegamento a questa sezione#
GET/api/v1/products
Senza q, i prodotti vengono elencati per nome. Con q, i risultati vengono restituiti in questo ordine:
- SKU esatto o riferimento del produttore
- SKU o riferimento del produttore che inizia con
q - corrispondenze complete del testo sul nome, marca, categoria e descrizione (3 o più caratteri; le query più brevi corrispondono all'inizio del nome)
Passa customer per applicare il prezzo a ogni riga per quel cliente e restituire solo i prodotti che quel cliente può visualizzare (attivi e che sono per tutti o selezionati per loro). È esattamente ciò che il cliente vede nel loro portale di commercio.
| Nome | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
q | stringa | no | SKU, riferimento del produttore, nome, marchio o categoria. Fino a 200 caratteri. |
category | stringa | no | ID della categoria. |
brand | stringa | no | ID del marchio. |
availability | stringa | no | Uno dei valori di disponibilità. |
active | true o false | no | true restituisce solo i prodotti attivi. Il valore predefinito è true quando customer è impostato; altrimenti vengono restituiti tutti i prodotti. |
customer | stringa | no | ID del cliente o codice account. |
limit | intero | no | Da 1 a 50. Predefinito 50. |
offset | intero | no | Righe da saltare. Predefinito 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"
}
Ottieni un prodottoCollegamento a questa sezione#
GET/api/v1/products/{id}
| Nome | In | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|---|
id | path | stringa | sì | ID del prodotto. |
customer | query | stringa | no | ID del cliente o codice account. Restituisce 404 se il cliente non può visualizzare il prodotto. |
qty | query | intero | no | Quantità da prezzare, da 1 a 1.000.000. Predefinito 1. price.line è 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"
}
Per cercare un prodotto per SKU, utilizzare GET /products?q=HX-8510: un match esatto dello SKU è sempre prioritario. Oppure utilizzare GET /prices, che accetta un SKU.
Crea o aggiorna prodottiCollegamento a questa sezione#
PUT/api/v1/productschiave di scrittura
Invia fino a 100 prodotti per chiamata. Ogni prodotto viene abbinato a un prodotto esistente tramite SKU, ignorando il caso. Se non c'è corrispondenza, viene creato un nuovo prodotto. Marche e categorie vengono abbinati per nome e creati se non esistono.
Come vengono gestiti i prezzi di listinoCollegamento a questa sezione#
| Il prodotto... | Cosa succede al prezzo |
|---|---|
| è nuovo | Va in vendita a listPrice immediatamente. |
| esiste ma non ha ancora un prezzo | Va in vendita a listPrice immediatamente. |
| esiste allo stesso prezzo | Niente. |
| esiste a un prezzo diverso | Niente cambia ancora. Il nuovo prezzo va in un cambiamento di prezzo in bozza per quella lista. priceChangeId è la bozza per la lista di listPrice. priceChangesByList ha un'entrata per ogni lista che è cambiata. |
I cambiamenti di prezzo in bozza sono il modo in cui TradeCatalog evita che un errore di battitura raggiunga i clienti. Il proprietario o il personale recensisce la bozza nell'app (Cambiamenti prezzo), controlla i movimenti più importanti e gli avvisi, sceglie una data e se inviare una e-mail ai clienti, quindi pubblica. È possibile visualizzare in anteprima una bozza con GET /price-changes/{id}.
Una sincronizzazione, un bozzo per lista
Una chiamata che modifica i prezzi inizia una bozza per lista, chiamata "Importazione API (nome chiave)" per Standard e con il nome della lista quando invii più di una. Per mantenere una sincronizzazione intera in quelle bozze, invia priceChangeId (la lista per listPrice) e drafts (le altre liste) dalla prima chiamata con ogni chiamata successiva. Ogni lista deve ancora essere una bozza: una volta pubblicata, annullata o eliminata, lascia fuori quell'id per iniziare una nuova bozza.
CorpoCollegamento a questa sezione#
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
products | array | sì | 1 a 100 prodotti. I codici SKU devono essere unici all'interno della chiamata. |
priceChangeId | string | no | Una bozza per l'elenco listPrice da una chiamata precedente. I prezzi modificati per quell'elenco vengono aggiunti ad essa. 404 se non esiste, 400 se non è più una bozza. |
drafts | array | no | Fino a 30 voci { list, priceChangeId } in modo che altri elenchi possano continuare le proprie bozze. |
products[].sku | string | sì | Fino a 64 caratteri. |
products[].name | string | sì | Fino a 200 caratteri. |
products[].listPrice | intero o null | sì | Unità minori, da 0 a 1.000.000.000. null è prezzo su richiesta: un prodotto nuovo non ha prezzo, un prodotto esistente mantiene il proprio. |
products[].priceList | string o null | no | Nome di un elenco prezzi esistente per listPrice. Omettere o null è Standard. Un nome non noto viene rifiutato. |
products[].listPrices | array | no | Altre liste, come { list, price }. price è unità minori, o null per lasciare quella lista invariata. Un nome sconosciuto viene rifiutato. |
products[].unitLabel | stringa | sì | Fino a 60 caratteri, ad esempio “Ogni”. |
products[].brand | string | no | Nome del marchio, fino a 120 caratteri. Pulito se omesso. |
products[].category | string | no | Nome della categoria, fino a 120 caratteri. Cancellato se omesso. |
products[].manufacturerRef | string | no | Fino a 120 caratteri. Cancellato se omesso. |
products[].description | string | no | Fino a 4.000 caratteri. Cancellato se omesso. |
products[].availability | string | no | Uno dei valori di disponibilità. Conservato se omesso; i nuovi prodotti iniziano come not_tracked. |
products[].stockQty | intero o null | no | Unità in magazzino, da -1.000.000 a 1.000.000.000. Imposta availability (vedi l'oggetto prodotto); uno stato che invii insieme a esso viene ignorato, tranne made_to_order. Mantenuto se omesso, a meno che non invii un availability senza di esso: in tal caso, il conteggio viene cancellato e viene utilizzato quel stato. null smette di contare; il availability inviato nella stessa riga si applica, o lo stato corrente se non ce n'è uno. |
products[].lowStockAt | intero o null | no | Livello di scorta bassa per questo prodotto, da 0. null utilizza il livello del workspace (Impostazioni, 5 per iniziare). Mantenuto se omesso. |
products[].leadTimeNote | string | no | Fino a 200 caratteri. Conservato se omesso. |
products[].dealText | string | no | Fino a 200 caratteri, visualizzati accanto al prezzo. Conservato se omesso. |
products[].details | oggetto | no | Fino a 30 colonne aggiuntive: nomi fino a 60 caratteri, valori fino a 500. Sostituisce tutti i dettagli quando inviato; mantenuto se omesso. |
products[].imageUrl | stringa | no | Un URL di immagine https. Copiato in TradeCatalog in background quando cambia. Mantenuto se omesso. |
products[].onlyFor | array | no | Fino a 200 codici account o e-mail di contatto. Rende visibile il prodotto solo per quei clienti, sostituendo qualsiasi elenco precedente. Mantenuto se omesso. |
Invia ogni campo che ti interessa
brand, category, manufacturerRef e description vengono scritti come inviati, quindi lasciarne uno fuori li cancella. Invia sempre il prodotto completo, come una riga di un foglio di calcolo.
Queste operazioni non possono essere eseguite tramite l'API per il momento: rendere un prodotto non attivo, minOrderQty, rimuovere un'immagine, cancellare un'appunto sul tempo di attesa e rendere nuovamente visibile a tutti un prodotto riservato ai clienti. Esegui queste operazioni nell'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": []
}
}
| Campo | Descrizione |
|---|---|
created | Nuovi prodotti. |
updated | Prodotti esistenti corrispondenti al SKU. |
priceChanges | Quanti prezzi sono entrati in un bozza, su ogni elenco nella chiamata. |
priceChangeId | La bozza per l'elenco listPrice, o null se quell'elenco non aveva modifiche. |
priceChangesByList | Un { list, priceChangeId } per ogni elenco che ha avuto un cambiamento di prezzo in questa chiamata. |
unknownCustomers | Valori in onlyFor che non hanno corrisposto a nessun cliente (fino a 50). A quei clienti non è stato dato l'accesso. |
I cambiamenti appaiono nel registro delle attività con il nome della chiave, e le ricerche vengono aggiornate immediatamente.
Marche e categorieCollegamento a questa sezione#
GET/api/v1/brands
GET/api/v1/categories
Entrambi restituiscono ogni riga, ordinata per nome, senza paginazione. Utilizza gli id con i filtri brand e category su 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" }
]
}
Per creare un marchio o una categoria, assegnane un nome a un prodotto in PUT /products.