Saltar al contenido
Menú de documentos

API REST

Productos

Lista, busca, lee y crea o actualiza productos, con precios de lista o cualquier precio del cliente.

Un producto tiene un SKU, un precio de lista y opciones de marca, categoría, estado de stock y detalles adicionales. Los precios siempre se calculan para usted: consulte Precios.

Los productos también pueden tener familyId y orderStep (predeterminado 1). Una lectura de un solo producto incluye metadatos opcionales family con su nombre, nombres de atributos de variante y configuración predeterminada. Un precio configurado contiene orderingFields, configurationRequired, definitionVersion y un quantityBreaks opcional con precio del cliente. Los campos de ordenación son entradas del comprador; details permanecen como hechos del producto.

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

Esta operación de solo lectura acepta { "customer": "ACME01", "qty": 3, "configuration": { "length": 2.75, "reference": "Kitchen" } }. customer es opcional para el precio de lista de personal. Las claves de solo lectura pueden usarla. Devuelve data.productId, qty, fields validados, definitionVersion y price con unit, line y la especificación elegida. Los campos obligatorios faltantes devuelven Configurar para precio; las combinaciones inválidas son rechazadas. No crea un carrito, pedido o pago. Use price.line en lugar de multiplicar el precio unitario.

Los imports aceptan family, orderStep y orderingDefinition opcionales. Una definición utiliza el esquema en OpenAPI, requiere el plan Pro y permiso para precios globales, y devuelve ruleDrafts para revisión a través del flujo de trabajo existente para cambios de precio. La estructura omitida deja las configuraciones existentes intactas. Vea configuración del catálogo de comercio.

El objeto de productoEnlace a esta sección#

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": ""
  }
}
CampoTipoDescripción
idstringIdentificador del producto.
skustringSu código de stock. Único en el espacio de trabajo, sin importar mayúsculas o minúsculas.
namestringNombre del producto.
descriptionstring o nuloDescripción más larga.
brandId, categoryIdstring o nuloVea marcas y categorías.
manufacturerRefstring o nuloNúmero de pieza del fabricante. Búsqueda como el SKU.
unitLabelstringQué es una unidad, por ejemplo, "Cada" o "Caja de 100".
minOrderQtyenteroCantidad mínima que los compradores pueden pedir.
availabilitystringnot_tracked, in_stock, low_stock, out_of_stock o made_to_order. Una etiqueta solo para compradores: cada producto puede ser comprado. Enviarlo sin un stockQty borra cualquier recuento almacenado (está estableciendo el estado manualmente). Con un stockQty se calcula para usted: 0 o menos es out_of_stock, hasta el nivel mínimo es low_stock, por encima de él es in_stock. made_to_order nunca cambia por el número.
stockQtyentero o nuloUnidades en mano, o null cuando no cuenta este producto. Cuando se establece, availability lo sigue. No se incluye cuando lo pide como cliente (customer).
lowStockAtentero o nuloNivel de bajo stock propio del producto, o null para usar el nivel del espacio de trabajo. No se incluye cuando lo pide como cliente.
leadTimeNotestring o nuloPor ejemplo, "3-5 días hábiles".
dealTextstring o nuloUna nota mostrada junto al precio, como "mínimo 12 botellas 45". Su título es la etiqueta de oferta del espacio de trabajo (Configuración, Visualización del catálogo).
activebooleanoLos productos inactivos están ocultos para los compradores.
visibilitystringall, o selected cuando solo ciertos clientes pueden verlo.
detailsobjetoColumnas adicionales de importaciones, como { "Finish": "Zinc" }.
priceobjeto o nuloEl precio a cantidad 1 (o qty). Vea Precios. unit es null cuando el producto no tiene precio aún.

Los recuentos de stock son suyos para mantener actualizados: los pedidos nunca cambian stockQty, por lo que puede enviarlos desde su sistema de stock cada vez que cambie. Los clientes ven solo el estado availability, nunca el número. A cero, un producto aún se puede pedir; simplemente se etiqueta como agotado.

GET /products/{id} también incluye los nombres brand y category.

Las imágenes no se devuelven por la API. Puede establecer una con imageUrl al crear o actualizar.

Listar o buscar productosEnlace a esta sección#

GET/api/v1/products

Sin q, los productos se listan por nombre. Con q, los resultados vienen en este orden:

  1. SKU exacto o referencia del fabricante
  2. SKU o referencia del fabricante que comience con q
  3. coincidencias de texto completo en nombre, marca, categoría y descripción (3 o más caracteres; las búsquedas más cortas coinciden con el comienzo del nombre)

Pase customer para calcular el precio de cada fila para ese cliente y devolver solo los productos que ese cliente puede ver (activos, y ya sea para todos o seleccionados para ellos). Ese es exactamente lo que el cliente ve en su portal comercial.

NombreTipoRequeridoDescripción
qstringnoSKU, referencia del fabricante, nombre, marca o categoría. Máximo 200 caracteres.
categorystringnoId de categoría.
brandstringnoId de marca.
availabilitystringnoUno de los valores de disponibilidad.
activetrue o falsenotrue devuelve solo productos activos. Por defecto true cuando customer está establecido; de lo contrario, se devuelven todos los productos.
customerstringnoId de cliente o código de cuenta.
limitintegerno1 a 50. Por defecto 50.
offsetintegernoFilas a omitir. Por defecto 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"
}

Obtener un productoEnlace a esta sección#

GET/api/v1/products/{id}

NombreEnTipoRequeridoDescripción
idpathstringyesId del producto.
customerquerystringnoId del cliente o código de cuenta. Devuelve 404 si ese cliente no puede ver el producto.
qtyqueryintegernoCantidad a precio, 1 a 1.000.000. Por defecto 1. price.line es 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"
}

Para buscar un producto por SKU, use GET /products?q=HX-8510: una coincidencia exacta de SKU siempre es la primera. O use GET /prices, que toma un SKU.

Crear o actualizar productosEnlace a esta sección#

PUT/api/v1/productsescribir clave

Envíe hasta 100 productos por llamada. Cada uno se compara con un producto existente por SKU, ignorando mayúsculas y minúsculas. Si no hay coincidencia, se crea un nuevo producto. Marcas y categorías se comparan por nombre y se crean si no existen.

Cómo se manejan los precios de listaEnlace a esta sección#

El producto…Qué le ocurre al precio
es nuevoSe pone en marcha en listPrice inmediatamente.
existe pero aún no tiene precioSe pone en marcha en listPrice inmediatamente.
existe al mismo precioNada.
existe a un precio diferenteNada cambia todavía. El nuevo precio entra en un cambio de precio en borrador para esa lista. priceChangeId es el borrador para la lista de listPrice. priceChangesByList tiene una entrada por lista que cambió.

Los cambios de precio en borrador son cómo TradeCatalog evita que un error de tipeo llegue a los clientes. El propietario o el personal revisa el borrador en la aplicación (Cambios de precio), revisa los mayores movimientos y advertencias, elige una fecha y si enviar un correo a los clientes, y luego publica. Puede previsualizar un borrador con GET /price-changes/{id}.

Una sincronización, un borrador por lista

Una llamada que cambia precios inicia un borrador por lista, llamado “Importación de API (nombre de clave)” para Standard y con el nombre de la lista cuando envía más de una. Para mantener una sincronización completa en esos borradores, envíe priceChangeId (la lista para listPrice) y drafts (las otras listas) desde la primera llamada con cada llamada posterior. Cada una aún debe ser un borrador: una vez que se publique, se cancele o se descarte, deje de incluir ese id para iniciar uno nuevo.

CuerpoEnlace a esta sección#

CampoTipoRequeridoDescripción
productsarrayyes1 a 100 productos. Los SKUs deben ser únicos dentro de la llamada.
priceChangeIdstringnoUn borrador para la lista listPrice de una llamada anterior. Los precios modificados para esa lista se añaden a éste. 404 si no existe, 400 si ya no es un borrador.
draftsarraynoHasta 30 entradas { list, priceChangeId } para que otras listas continúen con sus propios borradores.
products[].skustringyesMáximo 64 caracteres.
products[].namestringyesMáximo 200 caracteres.
products[].listPriceinteger o nullyesUnidades menores, de 0 a 1.000.000.000. null es precio bajo petición: un producto nuevo no tiene precio, uno existente mantiene el suyo.
products[].priceListstring o nullnoNombre de una lista de precios existente para listPrice. Omita o null es Standard. Un nombre desconocido se rechaza.
products[].listPricesarraynoOtras listas, como { list, price }. price es unidades menores, o null para dejar esa lista sin cambios. Un nombre desconocido se rechaza.
products[].unitLabelstringyesMáximo 60 caracteres, por ejemplo, “Cada”.
products[].brandstringnoNombre de marca, máximo 120 caracteres. Borrado si se omite.
products[].categorystringnoNombre de categoría, máximo 120 caracteres. Borrado si se omite.
products[].manufacturerRefstringnoMáximo 120 caracteres. Borrado si se omite.
products[].descriptionstringnoMáximo 4.000 caracteres. Borrado si se omite.
products[].availabilitystringnoUno de los valores de disponibilidad. Se mantiene si se omite; los productos nuevos comienzan como not_tracked.
products[].stockQtyinteger o nullnoUnidades en stock, de -1.000.000 a 1.000.000.000. Establece availability (ver el objeto de producto); un estado que envía junto con él se ignora, excepto made_to_order. Se mantiene si se omite, a menos que envíe un availability sin él: entonces el recuento se borra y se usa ese estado. null detiene el recuento; el availability enviado en la misma fila aplica, o el estado actual si no hay ninguno.
products[].lowStockAtinteger o nullnoNivel de bajo stock para este producto, de 0. null usa el nivel del espacio de trabajo (Configuración, 5 para empezar). Se mantiene si se omite.
products[].leadTimeNotestringnoMáximo 200 caracteres. Se mantiene si se omite.
products[].dealTextstringnoMáximo 200 caracteres, mostrado junto al precio. Se mantiene si se omite.
products[].detailsobjectnoMáximo 30 columnas adicionales: nombres de hasta 60 caracteres, valores de hasta 500. Reemplaza todos los detalles al enviarlos; se mantiene si se omite.
products[].imageUrlstringnoUna URL de imagen https. Se copia en TradeCatalog en segundo plano cuando cambia. Se mantiene si se omite.
products[].onlyForarraynoMáximo 200 códigos de cuenta o correos electrónicos de contacto. Hace que el producto sea visible solo para esos clientes, reemplazando cualquier lista anterior. Se mantiene si se omite.

Envía todos los campos que te importan

brand, category, manufacturerRef y description se escriben como se envían, por lo que omitir uno lo borra. Envíe siempre el producto completo, como una fila de una hoja de cálculo.

Estos no se pueden cambiar a través de la API aún: hacer un producto inactivo, minOrderQty, eliminar una imagen, borrar una nota de plazo de entrega y volver a hacer visible un producto exclusivo para clientes. Hágalo en la aplicación.

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": []
  }
}
CampoDescripción
createdProductos nuevos.
updatedProductos existentes coincidentes por SKU.
priceChangesCuántos precios entraron en un borrador, en cada lista de la llamada.
priceChangeIdEl borrador para la lista listPrice, o null si esa lista no tuvo cambios.
priceChangesByListUn { list, priceChangeId } por cada lista que tuvo un cambio de precio en esta llamada.
unknownCustomersValores en onlyFor que no coincidieron con ningún cliente (hasta 50). A esos clientes no se les dio acceso.

Los cambios aparecen en el registro de actividad con el nombre de la clave, y las búsquedas se actualizan de inmediato.

Marcas y categoríasEnlace a esta sección#

GET/api/v1/brands

GET/api/v1/categories

Ambos devuelven cada fila, ordenadas por nombre, sin paginación. Use los ids con los filtros brand y category en 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" }
  ]
}

Para crear una marca o categoría, nómbrala en un producto en PUT /products.