Aller au contenu
Menu documentation

API REST

Produits

Listez, recherchez, lisez et créez ou mettez à jour des produits, avec des prix de liste ou les prix de n'importe quel client.

Un produit a un SKU, un prix de liste, et des informations supplémentaires facultatives comme marque, catégorie, statut de stock et détails supplémentaires. Les prix sont toujours calculés pour vous : voir Prix.

Les produits peuvent avoir aussi familyId et orderStep (par défaut 1). Une lecture d'un seul produit inclut des métadonnées family facultatives avec son nom, les noms des attributs de variante et les préréglages. Un prix configuré contient orderingFields, configurationRequired, definitionVersion et un prix client facultatif quantityBreaks. Les champs de tri sont des entrées de l'acheteur ; details restent des faits sur le produit.

POST/api/v1/products/{id}/quote

Cette opération en lecture seule accepte { "customer": "ACME01", "qty": 3, "configuration": { "length": 2.75, "reference": "Kitchen" } }. customer est facultatif pour la tarification par liste du personnel. Les clés en lecture seule peuvent l'utiliser. Elle retourne data.productId, qty, fields validés, definitionVersion et price avec unit, line et la spécification choisie. Les champs requis manquants renvoient Configurez pour le prix ; les combinaisons invalides sont refusées. Elle ne crée pas de panier, de commande ou de paiement. Utilisez price.line plutôt que de multiplier le prix unitaire.

Les imports acceptent des family, orderStep et orderingDefinition facultatifs. Une définition utilise le schéma d'OpenAPI, nécessite le Pro et les autorisations de tarification globale, et retourne ruleDrafts pour révision via le processus de changement de prix existant. Une structure omise laisse les paramètres existants inchangés. Voir configuration du catalogue de commerce.

L'objet produitLien vers cette section#

json
{
  "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": ""
  }
}
ChampTypeDescription
idchaîneIdentifiant du produit.
skuchaîneVotre code de stock. Unique dans l'espace de travail, indépendamment de la casse.
namechaîneNom du produit.
descriptionchaîne ou nullDescription plus longue.
brandId, categoryIdchaîne ou nullVoir marques et catégories.
manufacturerRefchaîne ou nullNuméro de pièce du fabricant. Recherchable comme le SKU.
unitLabelchaîneCe qu’est une unité, par exemple « Chaque » ou « Boîte de 100 ».
minOrderQtyentierQuantité minimale que les acheteurs peuvent commander.
availabilitychaînenot_tracked, in_stock, low_stock, out_of_stock ou made_to_order. Une étiquette uniquement pour les acheteurs : chaque produit peut être commandé. L’envoi sans stockQty efface tout compteur stocké (vous définissez manuellement le statut). Avec un stockQty, il est calculé pour vous : 0 ou moins est out_of_stock, jusqu’au seuil bas est low_stock, au-dessus de ce seuil in_stock. made_to_order n’est jamais modifié par le nombre.
stockQtyentier ou nullUnités en stock, ou null si vous ne comptez pas ce produit. Lorsqu’il est défini, availability lui est associé. Omis lorsque vous demandez en tant que client (customer).
lowStockAtentier ou nullNiveau de stock propre à ce produit, ou null pour utiliser le niveau de l’espace de travail. Omis lorsque vous demandez en tant que client.
leadTimeNotechaîne ou nullPar exemple « 3–5 jours ouvrables ».
dealTextchaîne ou nullUne note affichée à côté du prix, telle que « min 12 btls 45 ». Son titre est l’étiquette de l’offre de l’espace de travail (Paramètres, Affichage du catalogue).
activebooléenLes produits inactifs sont masqués des acheteurs.
visibilitychaîneall, ou selected lorsqu’uniquement certains clients peuvent le voir.
detailsobjetColonnes supplémentaires provenant des importations, comme { "Finish": "Zinc" }.
priceobjet ou nullLe prix à la quantité 1 (ou qty). Voir Prix. unit est null lorsqu’un produit n’a pas encore de prix.

Les comptes de stock sont à votre charge pour les maintenir à jour : les commandes ne modifient jamais stockQty, envoyez-le donc depuis votre système de stock chaque fois qu’il change. Les clients ne voient que le statut availability, jamais le nombre. À zéro, un produit peut toujours être commandé ; il est simplement étiqueté comme épuisé.

GET /products/{id} inclut également les noms brand et category.

Les images ne sont pas renvoyées par l’API. Vous pouvez en définir une avec imageUrl lors de la création ou de la mise à jour.

Liste ou recherche de produitsLien vers cette section#

GET/api/v1/products

Sans q, les produits sont listés par nom. Avec q, les résultats s’affichent dans cet ordre :

  1. SKU exact ou référence du fabricant
  2. SKU ou référence du fabricant commençant par q
  3. recherches par mots-clés sur le nom, la marque, la catégorie et la description (3 caractères ou plus ; les requêtes plus courtes correspondent au début du nom)

Passez customer pour définir le prix de chaque ligne pour ce client et ne retournez que les produits que ce client peut voir (actifs, et soit pour tout le monde, soit sélectionnés pour lui). C’est exactement ce que le client voit dans leur portail de commerce.

NomTypeObligatoireDescription
qchaînenonSKU, référence fabricant, nom, marque ou catégorie. Jusqu'à 200 caractères.
categorychaînenonIdentifiant de catégorie.
brandchaînenonIdentifiant de marque.
availabilitychaînenonUne des valeurs d'indisponibilité.
activetrue ou falsenontrue retourne uniquement les produits actifs. La valeur par défaut est true lorsqu'customer est définie ; sinon, tous les produits sont retournés.
customerchaînenonIdentifiant du client ou code de compte.
limitentiernon1 à 50. Valeur par défaut 50.
offsetentiernonLignes à ignorer. Valeur par défaut 0.
bash
curl "https://tradecatalog.app/api/v1/products?q=hex%20bolt&customer=OAK01&limit=20" \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
js
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()
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"
}

Obtenir un produitLien vers cette section#

GET/api/v1/products/{id}

NomDansTypeObligatoireDescription
idcheminchaîneouiIdentifiant du produit.
customerrequêtechaînenonIdentifiant du client ou code de compte. Retourne 404 si ce client ne peut pas voir le produit.
qtyrequêteentiernonQuantité à facturer, de 1 à 1 000 000. Valeur par défaut 1. price.line est unit × qty.
bash
curl "https://tradecatalog.app/api/v1/products/3f8a2c1e-9b4d-4e6f-a7c8-1d2e3f4a5b6c?customer=OAK01&qty=5" \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
js
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()
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"
}

Pour rechercher un produit par SKU, utilisez GET /products?q=HX-8510 : une correspondance exacte de SKU est toujours prioritaire. Ou utilisez GET /prices, qui prend un SKU.

Créer ou mettre à jour des produitsLien vers cette section#

PUT/api/v1/productsclé d'écriture

Envoyez jusqu'à 100 produits par appel. Chacun est associé à un produit existant par SKU, en ignorant la casse. S'il n'y a pas de correspondance, un nouveau produit est créé. Les marques et les catégories sont associées par nom et créées si elles n'existent pas.

Comment les prix sont gérésLien vers cette section#

Le produit…Ce qui se passe avec le prix
est nouveauDevient actif à listPrice immédiatement.
existe mais n’a pas encore de prixDevient actif à listPrice immédiatement.
existe au même prixRien ne se passe.
existe à un prix différentRien ne change pour l'instant. Le nouveau prix est placé dans un changement de prix en brouillon pour cette liste. priceChangeId est le brouillon pour la liste de listPrice. priceChangesByList a une entrée par liste modifiée.

Les changements de prix en brouillon sont comment TradeCatalog empêche une erreur de frappe d'atteindre les clients. Le propriétaire ou le personnel examine le brouillon dans l'application (Changements de prix), vérifie les plus grandes variations et les avertissements, choisit une date et si les clients doivent être informés par courriel, puis publie. Vous pouvez prévisualiser un brouillon avec GET /price-changes/{id}.

Une synchronisation, un brouillon par liste

Un appel qui modifie les prix démarre un brouillon par liste, appelé « Import API (nom de clé) » pour le plan Standard et avec le nom de la liste lorsqu'on en envoie plus d'une. Pour garder une synchronisation entière dans ces brouillons, envoyez priceChangeId (la liste pour listPrice) et drafts (les autres listes) depuis le premier appel avec chaque appel ultérieur. Chaque appel doit toujours être un brouillon : une fois publié, annulé ou supprimé, laissez cet identifiant de côté pour en démarrer un nouveau.

CorpsLien vers cette section#

ChampTypeRequisDescription
productstableauoui1 à 100 produits. Les SKU doivent être uniques dans l'appel.
priceChangeIdchaînenonUn brouillon pour la liste listPrice d'un appel antérieur. Les prix modifiés pour cette liste y sont ajoutés. 404 si elle n'existe pas, 400 si ce n'est plus un brouillon.
draftstableaunonJusqu'à 30 entrées { list, priceChangeId } afin que les autres listes continuent leurs propres brouillons.
products[].skuchaîneouiJusqu'à 64 caractères.
products[].namechaîneouiJusqu'à 200 caractères.
products[].listPriceentier ou nullouiUnités mineures, de 0 à 1 000 000 000. null est le prix sur demande : un nouveau produit n’a pas de prix, un produit existant conserve le sien.
products[].priceListchaîne ou nullnonNom d'une liste de prix existante pour listPrice. Omettre ou null est Standard. Un nom inconnu est rejeté.
products[].listPricestableaunonAutres listes, comme { list, price }. price est des unités mineures, ou null pour laisser cette liste inchangée. Un nom inconnu est rejeté.
products[].unitLabelchaîneouiJusqu'à 60 caractères, par exemple « Chaque ».
products[].brandchaînenonNom de marque, jusqu'à 120 caractères. Effacé si omis.
products[].categorychaînenonNom de catégorie, jusqu'à 120 caractères. Effacé si omis.
products[].manufacturerRefchaînenonJusqu'à 120 caractères. Effacé si omis.
products[].descriptionchaînenonJusqu'à 4 000 caractères. Effacé si omis.
products[].availabilitychaînenonUne des valeurs d'indisponibilité. Conservé si omis ; les nouveaux produits commencent par not_tracked.
products[].stockQtyentier ou nullnonUnités en stock, de -1 000 000 à 1 000 000 000. Définit availability (voir l'objet produit) ; un statut que vous envoyez avec est ignoré, sauf made_to_order. Conservé si omis, sauf si vous envoyez un availability sans lui : alors le compte est effacé et ce statut est utilisé. null arrête le comptage ; le availability envoyé dans la même ligne s'applique, ou le statut actuel s'il n'y en a pas.
products[].lowStockAtentier ou nullnonNiveau de stock bas pour ce produit, à partir de 0. null utilise le niveau de l'espace de travail (Paramètres, 5 au départ). Conservé si omis.
products[].leadTimeNotechaînenonJusqu'à 200 caractères. Conservé si omis.
products[].dealTextchaînenonJusqu'à 200 caractères, affichés à côté du prix. Conservé si omis.
products[].detailsobjetnonJusqu'à 30 colonnes supplémentaires : noms jusqu'à 60 caractères, valeurs jusqu'à 500. Remplace toutes les détails lors de l'envoi ; conservé si omis.
products[].imageUrlchaînenonUne URL d'image https. Copiée dans TradeCatalog en arrière-plan lorsqu'elle change. Conservé si omis.
products[].onlyFortableaunonJusqu'à 200 codes de compte ou adresses e-mail de contact. Rend le produit visible uniquement pour ces clients, remplaçant toute liste précédente. Conservé si omis.

Envoyez chaque champ qui vous intéresse

brand, category, manufacturerRef et description sont écrits tels qu'envoyés, donc laisser l'un d'eux vide le vide. Envoyez le produit complet à chaque fois, comme une ligne de feuille de calcul.

Ces éléments ne peuvent pas encore être modifiés via l'API : rendre un produit inactif, minOrderQty, supprimer une image, effacer une note de délai de livraison, et rendre à nouveau visible un produit réservé aux clients. Faites-le dans l'application.

bash
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"]
      }
    ]
  }'
js
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')
json
{
  "data": {
    "created": 1,
    "updated": 1,
    "priceChanges": 1,
    "priceChangeId": "c4d5e6f7-1234-4abc-9def-0123456789ab",
    "priceChangesByList": [
      {
        "list": "Standard",
        "priceChangeId": "c4d5e6f7-1234-4abc-9def-0123456789ab"
      }
    ],
    "unknownCustomers": []
  }
}
ChampDescription
createdNouveaux produits.
updatedProduits existants correspondant au SKU.
priceChangesNombre de prix intégrés dans un brouillon, sur chaque liste de l'appel.
priceChangeIdLe brouillon pour la liste listPrice, ou null si cette liste n'avait pas de modifications.
priceChangesByListUn { list, priceChangeId } pour chaque liste ayant eu un changement de prix dans cet appel.
unknownCustomersValeurs dans onlyFor qui n'ont pas trouvé de client (jusqu'à 50). Ces clients n'ont pas eu accès.

Les modifications apparaissent dans le journal d'activité sous le nom de la clé, et les recherches sont mises à jour immédiatement.

Marques et catégoriesLien vers cette section#

GET/api/v1/brands

GET/api/v1/categories

Les deux retournent toutes les lignes, triées par nom, sans pagination. Utilisez les identifiants avec les filtres brand et category sur GET /products.

bash
curl https://tradecatalog.app/api/v1/categories \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
js
const res = await fetch('https://tradecatalog.app/api/v1/categories', {
  headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: categories } = await res.json()
json
{
  "data": [
    { "id": "a1b2c3d4-0000-4000-8000-000000000002", "name": "Bolts" },
    { "id": "a1b2c3d4-0000-4000-8000-000000000003", "name": "Hinges" }
  ]
}

Pour créer une marque ou une catégorie, donnez-lui un nom sur un produit dans PUT /products.