# Produtos

> Listar, pesquisar, ler e criar ou atualizar produtos, com preços de lista ou quaisquer preços de clientes.

Um produto tem um SKU, um preço de lista e uma marca, categoria, status de estoque e detalhes adicionais opcionais. Os preços são sempre calculados para você: veja [Preços](/docs/api/prices).

Os produtos também podem ter `familyId` e `orderStep` (padrão 1). Uma leitura de um único produto inclui metadados opcionais `family` com seu nome, nomes de atributos de variante e predefinição. Um preço configurado contém `orderingFields`, `configurationRequired`, `definitionVersion` e preço do cliente opcional `quantityBreaks`. Os campos de ordenação são entradas do comprador; `details` permanecem como fatos do produto.

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

Esta operação somente leitura aceita `{ "customer": "ACME01", "qty": 3, "configuration": { "length": 2.75, "reference": "Kitchen" } }`. `customer` é opcional para preços da lista de funcionários. Chaves somente leitura podem usá-lo. Ele retorna `data.productId`, `qty`, `fields` validados, `definitionVersion` e `price` com `unit`, `line` e a especificação escolhida. Campos obrigatórios ausentes retornam Configurar para preço; combinações inválidas são recusadas. Ele não cria um cesto, pedido ou pagamento. Use `price.line` em vez de multiplicar o preço unitário.

As importações aceitam os `family`, `orderStep` e `orderingDefinition` opcionais. Uma definição usa o esquema OpenAPI, requer o plano Pro e permissão para precificação global, e retorna o `ruleDrafts` para revisão por meio do fluxo de trabalho existente de alteração de preço. A estrutura omitida deixa as configurações existentes intactas. Veja [configuração do catálogo de comércio](/docs/guides/trade-catalogues).

## O objeto do produto

```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": ""
  }
}
```

| Campo                   | Tipo            | Descrição                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| ----------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                    | string          | ID do produto.                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `sku`                   | string          | Seu código de estoque. Único no espaço de trabalho, ignorando maiúsculas e minúsculas.                                                                                                                                                                                                                                                                                                                                                       |
| `name`                  | string          | Nome do produto.                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `description`           | string ou null  | Descrição mais longa.                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `brandId`, `categoryId` | string ou null  | Consulte [marcas e categorias](#brands-and-categories).                                                                                                                                                                                                                                                                                                                                                                                      |
| `manufacturerRef`       | string ou nulo  | Número de peça do fabricante. Pesquisável como o SKU.                                                                                                                                                                                                                                                                                                                                                                                        |
| `unitLabel`             | string          | O que uma unidade é, por exemplo, “Cada” ou “Caixa de 100”.                                                                                                                                                                                                                                                                                                                                                                                  |
| `minOrderQty`           | inteiro         | Quantidade mínima que os compradores podem pedir.                                                                                                                                                                                                                                                                                                                                                                                            |
| `availability`          | string          | `not_tracked`, `in_stock`, `low_stock`, `out_of_stock` ou `made_to_order`. Uma etiqueta apenas para compradores: cada produto pode ser comprado. Enviando-o sem um `stockQty` apaga qualquer contagem armazenada (você está definindo o status manualmente). Com um `stockQty` é calculado para você: 0 ou menos é `out_of_stock`, até o nível mínimo é `low_stock`, acima disso é `in_stock`. `made_to_order` nunca é alterado pelo número. |
| `stockQty`              | inteiro ou nulo | Unidades em estoque, ou `null` quando você não contabiliza este produto. Quando definido, `availability` segue-o. Excluído quando você o solicita como cliente (`customer`).                                                                                                                                                                                                                                                                 |
| `lowStockAt`            | inteiro ou nulo | Nível de estoque baixo próprio do produto, ou `null` para usar o nível do espaço de trabalho. Excluído quando você o pede como cliente.                                                                                                                                                                                                                                                                                                      |
| `leadTimeNote`          | string ou null  | ex. “3–5 dias úteis”.                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `dealText`              | string ou nulo  | Uma nota exibida ao lado do preço, como "mín 12 btls 45". Seu título é a etiqueta de negócios do espaço de trabalho (Configurações, Exibição do Catálogo).                                                                                                                                                                                                                                                                                   |
| `active`                | booleano        | Produtos inativos são ocultos dos compradores.                                                                                                                                                                                                                                                                                                                                                                                               |
| `visibility`            | string          | `all`, ou `selected` quando apenas clientes selecionados puderem vê-lo.                                                                                                                                                                                                                                                                                                                                                                      |
| `details`               | object          | Colunas adicionais de imports, como `{ "Finish": "Zinc" }`.                                                                                                                                                                                                                                                                                                                                                                                  |
| `price`                 | objeto ou nulo  | O preço na quantidade 1 (ou `qty`). Veja [Preços](/docs/api/prices#the-price-object). `unit` é `null` quando o produto não tem preço ainda.                                                                                                                                                                                                                                                                                                  |

Contagens de estoque são suas para manter atualizadas: os pedidos nunca mudam `stockQty`, então envie-o do seu sistema de estoque sempre que houver alterações. Os clientes veem apenas o status `availability`, nunca o número. Em zero, um produto ainda pode ser pedido; ele simplesmente é rotulado como fora de estoque.

Os nomes `GET /products/{id}` também incluem `brand` e `category`.

Imagens não são retornadas pela API. Você pode definir uma com `imageUrl` ao criar ou atualizar.

## Listar ou pesquisar produtos

**GET** `/api/v1/products`

Sem `q`, os produtos são listados por nome. Com `q`, os resultados aparecem nesta ordem:

1. SKU exato ou referência do fabricante
2. SKU ou referência do fabricante que começa com `q`
3. correspondências de texto completo no nome, marca, categoria e descrição (3 ou mais caracteres; consultas mais curtas correspondem ao início do nome)

Passe `customer` para definir o preço de cada linha para esse cliente e retorne apenas os produtos que esse cliente pode ver (ativos e tanto para todos quanto selecionados para eles). Isso é exatamente o que o cliente vê em seu portal comercial.

| Nome           | Tipo              | Obrigatório | Descrição                                                                                                                                |
| -------------- | ----------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `q`            | string            | não         | SKU, referência do fabricante, nome, marca ou categoria. Até 200 caracteres.                                                             |
| `category`     | string            | não         | ID da categoria.                                                                                                                         |
| `brand`        | string            | não         | ID da marca.                                                                                                                             |
| `availability` | string            | não         | Um dos valores de disponibilidade.                                                                                                       |
| `active`       | `true` ou `false` | não         | `true` retorna apenas produtos ativos. O padrão é `true` quando `customer` é definido; caso contrário, todos os produtos são retornados. |
| `customer`     | string            | não         | ID do cliente ou código da conta.                                                                                                        |
| `limit`        | inteiro           | não         | 1 a 50. Padrão 50.                                                                                                                       |
| `offset`       | inteiro           | não         | Linhas a pular. Padrão 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"
}
```

## Obter um produto

**GET** `/api/v1/products/{id}`

| Nome       | Entrada  | Tipo    | Obrigatório | Descrição                                                                                       |
| ---------- | -------- | ------- | ----------- | ----------------------------------------------------------------------------------------------- |
| `id`       | caminho  | string  | sim         | ID do produto.                                                                                  |
| `customer` | consulta | string  | não         | ID do cliente ou código da conta. Retorna `404` se esse cliente não puder visualizar o produto. |
| `qty`      | consulta | inteiro | não         | Quantidade a ser precificada, 1 a 1.000.000. Padrão 1. `price.line` é `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 pesquisar um produto pelo SKU, use `GET /products?q=HX-8510`: uma correspondência exata de SKU sempre aparece em primeiro lugar. Ou use [`GET /prices`](/docs/api/prices), que aceita um SKU.

## Criar ou atualizar produtos

**PUT** `/api/v1/products`

Envie até **100 produtos** por chamada. Cada um é correspondido a um produto existente pelo SKU, ignorando maiúsculas e minúsculas. Se não houver correspondência, um novo produto é criado. Marcas e categorias são correspondidas pelo nome e criadas se não existirem.

### Como os preços da lista são tratados

| O produto...                    | O que acontece com o preço                                                                                                                                                                                              |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| é novo                          | Fica disponível em `listPrice` imediatamente.                                                                                                                                                                           |
| existe, mas ainda não tem preço | Fica disponível em `listPrice` imediatamente.                                                                                                                                                                           |
| existe ao mesmo preço           | Nada.                                                                                                                                                                                                                   |
| existe a um preço diferente     | Nada muda ainda. O novo preço entra em uma **mudança de preço em rascunho** para essa lista. `priceChangeId` é o rascunho para a lista de `listPrice`. `priceChangesByList` tem uma entrada por lista que foi alterada. |

Alterações de preço em rascunho são como o TradeCatalog evita que um erro de digitação chegue aos clientes. O proprietário ou funcionários revisam o rascunho no aplicativo (**Alterações de preço**), verificam as maiores mudanças e alertas, escolhem uma data e se deseja enviar um e-mail aos clientes, e depois publicam. Você pode visualizar um rascunho com [`GET /price-changes/{id}`](/docs/api/price-changes#preview-a-price-change).

**Uma sincronização, um rascunho por lista:** Uma chamada que muda preços inicia um rascunho por lista, chamado “Importação de API (nome da chave)” para Standard e com o nome da lista quando você enviar mais de uma. Para manter uma sincronização inteira nesses rascunhos, envie `priceChangeId` (a lista para `listPrice`) e `drafts` (as outras listas) da primeira chamada com cada chamada posterior. Cada uma ainda deve ser um rascunho: uma vez publicada, cancelada ou descartada, deixe esse ID de fora para iniciar um novo.

### Corpo

| Campo                        | Tipo            | Obrigatório | Descrição                                                                                                                                                                                                                                                                                                                                                                                                              |
| ---------------------------- | --------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `products`                   | array           | sim         | 1 a 100 produtos. SKUs devem ser únicos dentro da chamada.                                                                                                                                                                                                                                                                                                                                                             |
| `priceChangeId`              | string          | não         | Um rascunho da lista `listPrice` de uma chamada anterior. Preços alterados para essa lista são adicionados a ela. 404 se não existir, 400 se não for mais um rascunho.                                                                                                                                                                                                                                                 |
| `drafts`                     | array           | não         | Até 30 entradas `{ list, priceChangeId }` para que outros listas continuem seus próprios rascunhos.                                                                                                                                                                                                                                                                                                                    |
| `products[].sku`             | string          | sim         | Até 64 caracteres.                                                                                                                                                                                                                                                                                                                                                                                                     |
| `products[].name`            | string          | sim         | Até 200 caracteres.                                                                                                                                                                                                                                                                                                                                                                                                    |
| `products[].listPrice`       | inteiro ou nulo | sim         | Unidades menores, de 0 a 1.000.000.000. `null` é preço sob consulta: um novo produto não tem preço, um existente mantém o seu próprio.                                                                                                                                                                                                                                                                                 |
| `products[].priceList`       | string ou nulo  | não         | Nome de uma lista de preços existente para `listPrice`. Omita ou `null` é Standard. Um nome desconhecido é rejeitado.                                                                                                                                                                                                                                                                                                  |
| `products[].listPrices`      | array           | não         | Outras listas, como `{ list, price }`. `price` são unidades menores, ou `null` para deixar essa lista inalterada. Um nome desconhecido é rejeitado.                                                                                                                                                                                                                                                                    |
| `products[].unitLabel`       | string          | sim         | Até 60 caracteres, por exemplo, “Cada”.                                                                                                                                                                                                                                                                                                                                                                                |
| `products[].brand`           | string          | não         | Nome da marca, até 120 caracteres. **Limpo se omitido.**                                                                                                                                                                                                                                                                                                                                                               |
| `products[].category`        | string          | não         | Nome da categoria, até 120 caracteres. **Limpo se omitido.**                                                                                                                                                                                                                                                                                                                                                           |
| `products[].manufacturerRef` | string          | não         | Até 120 caracteres. **Limpo se omitido.**                                                                                                                                                                                                                                                                                                                                                                              |
| `products[].description`     | string          | não         | Até 4.000 caracteres. **Limpo se omitido.**                                                                                                                                                                                                                                                                                                                                                                            |
| `products[].availability`    | string          | não         | Um dos valores de disponibilidade. Mantido se omitido; novos produtos começam como `not_tracked`.                                                                                                                                                                                                                                                                                                                      |
| `products[].stockQty`        | inteiro ou nulo | não         | Unidades em estoque, de -1.000.000 a 1.000.000.000. Define `availability` (veja o objeto do produto); um status que você envia junto é ignorado, exceto `made_to_order`. Mantido se omitido, a menos que você envie um `availability` sem ele: então a contagem é limpa e esse status é usado. `null` para parar de contar; o `availability` enviado na mesma linha aplica-se, ou o status atual se não houver nenhum. |
| `products[].lowStockAt`      | inteiro ou nulo | não         | Nível de estoque baixo para esse produto, de 0. `null` usa o nível da workspace (Configurações, 5 para começar). Mantido se omitido.                                                                                                                                                                                                                                                                                   |
| `products[].leadTimeNote`    | string          | não         | Até 200 caracteres. Mantido se omitido.                                                                                                                                                                                                                                                                                                                                                                                |
| `products[].dealText`        | string          | não         | Até 200 caracteres, mostrado ao lado do preço. Mantido se omitido.                                                                                                                                                                                                                                                                                                                                                     |
| `products[].details`         | objeto          | não         | Até 30 colunas extras: nomes até 60 caracteres, valores até 500. Substitui todos os detalhes quando enviado; mantido se omitido.                                                                                                                                                                                                                                                                                       |
| `products[].imageUrl`        | string          | não         | Uma URL de imagem `https`. Copiada para o TradeCatalog em segundo plano quando muda. Mantido se omitido.                                                                                                                                                                                                                                                                                                               |
| `products[].onlyFor`         | array           | não         | Até 200 códigos de conta ou e-mails de contato. Torna o produto visível **apenas** para esses clientes, substituindo qualquer lista anterior. Mantido se omitido.                                                                                                                                                                                                                                                      |

**Envie todos os campos que você se importa:** `brand`, `category`, `manufacturerRef` e `description` são escritos como enviados, então deixar um de fora os limpa. Envie o produto completo a cada vez, como uma linha de planilha.

Esses não podem ser alterados por meio da API ainda: tornar um produto inativo, `minOrderQty`, remover uma imagem, limpar uma observação de tempo de entrega e tornar um produto exclusivo do cliente visível para todos novamente. Faça essas alterações no aplicativo.

```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": []
  }
}
```

| Campo                | Descrição                                                                                                 |
| -------------------- | --------------------------------------------------------------------------------------------------------- |
| `created`            | Novos produtos.                                                                                           |
| `updated`            | Produtos existentes correspondentes pelo SKU.                                                             |
| `priceChanges`       | Quantos preços entraram em um rascunho, em cada lista da chamada.                                         |
| `priceChangeId`      | O rascunho para a lista `listPrice`, ou `null` se essa lista não tiver alterações.                        |
| `priceChangesByList` | Um `{ list, priceChangeId }` para cada lista que tiver uma alteração de preço nesta chamada.              |
| `unknownCustomers`   | Valores em `onlyFor` que não corresponderam a nenhum cliente (até 50). Esses clientes não tiveram acesso. |

As alterações aparecem no log de atividade pelo nome da chave, e as atualizações de pesquisa ocorrem imediatamente.

## Marcas e categorias

**GET** `/api/v1/brands`

**GET** `/api/v1/categories`

Ambos retornam todas as linhas, classificadas pelo nome, sem paginação. Use os ids com os filtros `brand` e `category` em `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 criar uma marca ou categoria, dê um nome a ela em um produto em `PUT /products`.
