Skip to Content
Documentação de integração da plataforma — em evolução contínua.
CatálogoProdutos e SKUs

Produtos e SKUs

O modelo

Na Grocers, produto é a entidade de vitrine (nome, descrição, categoria, marca) e SKU é a unidade vendável (EAN, peso, dimensões, embalagem). Um leite pode ser um produto com três SKUs: unidade, fardo de 6 e fardo de 12.

Preço e estoque nunca ficam no produto — sempre no SKU.

POST /v1/batch/Products

[ { "externalReference": "PROD-114970", "categoryExternalReference": "CAT-MASSAS", "brandExternalReference": "MARCA-001", "commercialPolicyId": "9c1f5a3e-7b2d-4c8e-9f01-2a3b4c5d6e7f", "name": "Macarrão Espaguete 500g", "title": "Macarrão Espaguete Grano Duro 500g", "shortDescription": "Massa de sêmola de grano duro", "longDescription": "Macarrão tipo espaguete produzido com sêmola de grano duro...", "similarTerms": "espaguete, spaghetti, talharim", "slug": "macarrao-espaguete-500g", "fiscalCode": "19021900", "ean": "7891234567890", "punctuation": 5, "displayInSite": true, "displayInSoldOut": true, "active": true, "skus": [ { "externalReference": "114970", "name": "Macarrão Espaguete 500g - Unidade", "ean": "7891234567890", "reference": "MAC-ESP-500", "unitOfMeasurement": "Un", "weightForFreight": 0.52, "heightForFreight": 5, "widthForFreight": 10, "lengthForFreight": 25, "weightReal": 0.5, "heightReal": 4, "widthReal": 9, "lengthReal": 24, "arrivalDate": "2026-07-29", "manufacturerCode": "ME500" }, { "externalReference": "114970-CS", "name": "Macarrão Espaguete 500g - Fardo 12un", "ean": "7891234567891", "reference": "MAC-ESP-500-CS", "unitOfMeasurement": "Un", "skuPackageMinAmount": 1, "skuPackageMaxAmount": 12, "weightForFreight": 6.2, "heightForFreight": 20, "widthForFreight": 30, "lengthForFreight": 40, "weightReal": 6, "heightReal": 19, "widthReal": 29, "lengthReal": 39, "arrivalDate": "2026-07-29", "manufacturerCode": "ME500-CS" } ] } ]

Resposta: 200 OK com o operationId do lote. O processamento é assíncrono — o resultado chega no webhook ProductWithSKUs_Created ou ProductWithSKUs_Updated.

Esta rota devolve o GUID cru no corpo, sem envelope:

"402fe685-2060-432b-ac57-223eb2e680f3"

As demais rotas em lote (Brands, Categories, Pricing, Warehouses, Warehouses/sku) devolvem { "operationId": "..." }. Se o seu cliente lê sempre body.operationId, ele perde exatamente o operationId do lote de produtos.

No SKU o campo da unidade de medida se chama unitOfMeasurement. Nos itens de pedido (PUT /v1/Orders/{orderNumber}) o mesmo conceito se chama unitOfMeasure, sem o -ment. Os valores aceitos são os mesmos; só o nome do campo muda entre os dois contratos.

Campos do produto

CampoObrigatórioLimiteObservação
externalReferencesim60chave de idempotência
commercialPolicyIdsimpolítica comercial a que o produto pertence
categoryExternalReferencerecomendado30sem ela o item não aparece na navegação
brandExternalReferencenão30habilita o filtro por marca
namesim5–400nome interno
slugsim400precisa ser único
titlesim3–400texto exibido na vitrine
shortDescriptionsim3–500descrição curta
longDescriptionsimmín. 3descrição completa
fiscalCodenão pela APINCM, usado na nota — ver aviso abaixo
eannão48código de barras do produto
punctuationsim≥ 0peso de relevância na busca
displayInSitenãofalse esconde o produto sem excluir
displayInSoldOutnãomostra o item mesmo com estoque zero (esgotado)
similarTermsnãosinônimos de busca, separados por vírgula
characteristicsnãocaracterísticas descritivas
activenãoativa ou desativa o produto

A atualização é parcial: a plataforma rastreia quais campos vieram no JSON. Enviar só { "externalReference": "PROD-1", "active": false } altera apenas active — os demais campos ficam como estão. Omitir é diferente de mandar null.

fiscalCode não tem validação nenhuma: a regra existe no validador, mas está vazia — sem obrigatoriedade, sem tamanho, sem formato. Produto sem NCM, com NCM de 4 dígitos ou com texto no lugar do código passa sem reclamação. O erro só aparece na emissão da nota, muito depois da carga. Valide o NCM no seu lado antes de enviar.

Campos do SKU

CampoObrigatórioLimiteObservação
externalReferencesim60chave usada por estoque e preço
namesim400nome da variação
eansim30código de barras
referencesim100referência interna do SKU
unitOfMeasurementsimUn (1), Kg (2), G (3) ou Mg (4)
weightForFreight e dimensõessim> 0weight/height/width/lengthForFreight — entram no frete
weightReal e dimensões reaissim> 0weight/height/width/lengthReal
arrivalDatesimdata de chegada
manufacturerCodesim50código do fabricante
arithmeticFactornãofator de conversão para itens fracionados
weightnão> 0peso, quando informado
minimumWeightForSalenão> 0peso mínimo para venda
skuPackageMinAmount / MaxAmountnão> 0quantidade mínima e máxima por embalagem
exhibitionBadge1..3não15selos exibidos no card do produto

O externalReference do SKU é o identificador usado em estoque, preço e itens do pedido. Mudá-lo depois da loja no ar quebra o vínculo com o estoque e preços já publicados.

Quando a validação falha

POST /v1/batch/Products não valida o conteúdo do lote. A API de integração recebe, gera o operationId e enfileira — responde 200 OK mesmo com um produto de nome vazio ou EAN de 200 caracteres.

Quem valida é o core, quando o processador aplica cada item. Um produto reprovado gera o evento ProductWithSKUs_CreationFailed (ou _UpdateFailed); um produto que entra mas com SKUs reprovados gera ProductWithSKUs_PartiallyFailed.

Nas notificações de catálogo o erro não está no campo error do envelope — ele fica dentro de data. Um consumidor que só olha evento.error não enxerga nenhuma falha de produto ou de SKU.

O data é um relatório do produto inteiro: um produto e seus SKUs viram uma notificação, com a contagem de sucessos e a lista dos SKUs que falharam.

{ "operationId": "402fe685-2060-432b-ac57-223eb2e680f3", "contractAccountId": "9f36a666-acd5-4987-a47f-3de247f65d82", "relatedEntity": "ProductWithSKUs", "eventType": "ProductWithSKUs_PartiallyFailed", "data": { "product": { "externalReference": "PROD-114970", "name": "Macarrão Espaguete 500g", "processed": true, "error": null }, "skus": { "total": 2, "successful": 1, "failed": 1, "successfulSKUs": ["114970"], "failedSKUs": [ { "externalReference": "114970-CS", "error": { "statusCode": 400, "message": "{\"errors\":{\"ManufacturerCode\":[\"Código do fabricante é obrigatório\"]}}", "title": "Requisição inválida", "detail": "Ocorreram um ou mais erros de validação", "validationErrors": [ { "id": 1, "key": "ManufacturerCode", "validation": "Código do fabricante é obrigatório" } ] } } ] }, "summary": { "status": "PartialFailure", "processedAt": "2026-07-29T14:32:10.512Z" } } }
CampoComo usar
data.product.externalReferencequal produto do seu lote esta notificação descreve
data.product.processedfalse = o produto não entrou e nenhum SKU foi tentado (fail-fast)
data.product.erroro erro do produto, quando processed é false
data.skus.successfulSKUsexternalReference dos SKUs que entraram
data.skus.failedSKUs[].externalReferencequal SKU falhou
data.skus.failedSKUs[].error.validationErrorskey é a propriedade reprovada, validation a mensagem do validador
data.summary.statusSuccess, PartialFailure ou Failed

error.message carrega o corpo cru devolvido pelo core — guarde-o quando validationErrors vier vazio.

Quando o produto falha, os SKUs nem são tentados: a lista successfulSKUs vem vazia e failedSKUs também. Corrigir só o produto e reenviar reprocessa o conjunto inteiro — é o comportamento desejado, mas significa que “0 SKUs falharam” não quer dizer “todos os SKUs entraram”. Leia data.product.processed primeiro.

Produto e SKU são validados em chamadas separadas ao core. Um SKU reprovado devolve key com o nome da propriedade do próprio SKU (Name, EAN, Reference, ManufacturerCode), sem prefixo do produto.

Não espere 400 do POST /v1/batch/Products para descobrir dado inválido. Ele nunca vem: a rota só declara 200 e 500, e a validação de modelo está desligada na API de integração. Um integrador que trata erro apenas pelo código HTTP conclui que o lote inteiro entrou — e a falha some.

Como o produto reprovado vira uma notificação isolada, é obrigatório assinar ProductWithSKUs_CreationFailed, ProductWithSKUs_UpdateFailed e ProductWithSKUs_PartiallyFailed junto com os eventos de sucesso. Veja Registrar o webhook.

Itens pesáveis

Para produtos vendidos por peso (frios, hortifruti, açougue), a combinação é:

Unidade de medida

unitOfMeasurement: "Kg" no SKU.

Fator aritmético

arithmeticFactor converte a quantidade escolhida pelo cliente para a unidade de cobrança — é o que permite vender “300 g” de um item precificado por quilo.

Peso real

weightReal alimenta o cálculo de frete e a conferência na separação.

Consultar um produto

curl $GROCERS_API/v1/batch/Products/PROD-114970 \ -H "Authorization: Basic $BASIC" \ -H "x-contractAccountId: $TENANT"

A resposta é o produto com marca, categoria e a lista de SKUs — é a forma de conferir o que a plataforma realmente gravou depois de um lote:

{ "id": "402fe685-2060-432b-ac57-223eb2e680f3", "contractAccountId": "9f36a666-acd5-4987-a47f-3de247f65d82", "externalReference": "PROD-114970", "name": "Macarrão Espaguete 500g", "title": "Macarrão Espaguete Grano Duro 500g", "slug": "macarrao-espaguete-500g", "shortDescription": "Massa de sêmola de grano duro", "longDescription": "Macarrão tipo espaguete produzido com sêmola de grano duro...", "similarTerms": "espaguete, spaghetti, talharim", "characteristics": null, "fiscalCode": "19021900", "ean": "7891234567890", "punctuation": 5, "displayInSite": true, "displayInSoldOut": true, "active": true, "hasImage": true, "commercialPoliticId": "9c1f5a3e-7b2d-4c8e-9f01-2a3b4c5d6e7f", "brandId": "7c8d9e0f-1a2b-4c3d-8e4f-5a6b7c8d9e0f", "brand": { "id": "7c8d9e0f-1a2b-4c3d-8e4f-5a6b7c8d9e0f", "name": "Grano Duro", "externalReference": "MARCA-001" }, "categoryId": "8d9e0f1a-2b3c-4d4e-9f5a-6b7c8d9e0f1a", "category": { "id": "8d9e0f1a-2b3c-4d4e-9f5a-6b7c8d9e0f1a", "name": "Massas", "externalReference": "CAT-MASSAS" }, "createdAt": "2026-07-29T14:32:10", "updatedAt": "2026-07-30T08:11:04", "approvedByUserId": null, "skus": [ { "id": "6b7c8d9e-0f1a-4b2c-9d3e-4f5a6b7c8d9e", "externalReference": "114970", "name": "Macarrão Espaguete 500g - Unidade", "ean": "7891234567890", "reference": "MAC-ESP-500", "unitOfMeasure": "Un", "manufacturerCode": "ME500", "arrivalDate": "2026-07-29", "weightForFreight": 0.52, "heightForFreight": 5, "widthForFreight": 10, "lengthForFreight": 25, "weightReal": 0.5, "heightReal": 4, "widthReal": 9, "lengthReal": 24, "active": true } ] }

Duas armadilhas de nome nesta resposta. O campo da política volta como commercialPoliticId — sem o y, e diferente de commercialPolicyId, que é o nome aceito no envio. E a unidade de medida do SKU volta em unitOfMeasure, enquanto no envio o campo se chama unitOfMeasurement. O valor é o mesmo texto ("Un") nos dois lados; só a chave muda. Um cliente que lê esta resposta e a reenvia sem tradução perde os dois campos silenciosamente — a atualização é parcial, e campo com nome desconhecido é simplesmente ignorado.

O Swagger declara o retorno como lista de produtos; o corpo real é um objeto. Um parser que espera array recebe erro de tipo. Produto inexistente devolve 404.

A unidade de medida muda de tipo conforme o serviço. Na API de integração ela é texto ("Un", "Kg") — no envio, no retorno desta rota e no payload do webhook de pedido. Nas rotas do core (/v1/gateway/..., como GET /v1/gateway/orders/{id}/details e Consultar clientes) ela vem como número:

TextoNúmero
Un1
Kg2
G3
Mg4

Um switch sobre unitOfMeasure que funciona no webhook não casa nenhum caso na resposta do core, e o item pesável passa a ser tratado como unitário.

Tirar um produto de circulação

Envie active: false no mesmo POST /v1/batch/Products. A atualização é parcial, então o corpo pode ter só a chave e o campo:

curl -X POST $GROCERS_API/v1/batch/Products \ -H "Authorization: Basic $BASIC" \ -H "x-contractAccountId: $TENANT" \ -H "Content-Type: application/json" \ -d '[{"externalReference": "PROD-114970", "active": false}]'

Não use DELETE /v1/batch/Products. A rota ainda existe e aparece no Swagger, mas a implementação lança NotSupportedException: toda chamada volta 500 com Product deletion is now handled through ProductWithSKUs flow. This method is obsolete. e nenhum produto é excluído. A exclusão de produto passou a ser feita pelo fluxo de ProductWithSKUs; hoje o caminho suportado é active: false.