Sincronizar catálogo de produtos¶
Esta jornada descreve como um ERP integrador deve sincronizar um catálogo grande (dezenas de milhares de produtos) com o Gubee seguindo a ordem correta de dependências entre domínios.
Recomendado: use POST /integration/products/v2/createupdate para tudo
O endpoint /v2/createupdate cria automaticamente categoria, marca e
atributos que não existirem ainda, na mesma chamada em que cria o
produto. Em 95% dos casos de integração, você não precisa criar categorias,
marcas ou atributos separadamente — basta enviar tudo no payload do
produto.
POST /integration/products/v2/createupdate
{
"externalId": "X",
"name": "...",
"mainCategory": "Esportes > Calçados > Tênis", # criado em cascata
"brand": "MarcaDemo", # criado se não existe
"specifications": [{ "name": "Gênero", "values": ["Masculino"] }],
"variations": [
{
"sku": "SKU-X",
"variantSpecification": [{ "name": "Cor", "values": ["Preto"] }],
"prices": [...], "stocks": [...], "images": [...]
}
]
}
Use os passos abaixo (bulk de categoria/atributo/marca) apenas quando:
- Você quer reutilizar categorias em 10k+ produtos sem reenviar a string a cada chamada (economia de processamento)
- Você precisa criar categorias com metadados adicionais (externalId próprio, attributes obrigatórios por categoria) antes de subir produtos
- Você está migrando um catálogo legado comHierquia rígida que precisa ser validada primeiro
Para a maioria dos ERPs, o fluxo é simplesmente: loop de POST /v2/createupdate para cada produto. Fim.
Pré-requisitos¶
sellerIdjá cadastrado no Gubee (vem da autorização do seller, extraído do token).- Access token de um app OAuth2 autorizado pelo seller, com
product:edit. Veja o Guia do desenvolvedor. - Os domínios abaixo têm dependência estrita de ordem (quando você escolhe criar separadamente):
Visão geral do fluxo¶
Fluxo recomendado (90% dos casos):
- Produtos — loop de
POST /integration/products/v2/createupdateenviandomainCategory(string com>),brand,specificationsevariations[].variantSpecificationno mesmo payload. Backend resolve tudo.
Fluxo alternativo (catálogo massivo, 10k+ produtos):
- Categorias — enviar em bulk, pais antes dos filhos (uma vez).
- Atributos — enviar em bulk (uma vez).
- Marcas — enviar em loop ou bulk (uma vez).
- Produtos — upsert via
POST /integration/products/v2/createupdate(passamainCategorycomo string; backend reutiliza a categoria já criada sem duplicar). - Mapear SKUs entre o ERP e o
skuIdinterno do Gubee.
A separação entre externalId (chave do ERP) e skuId/hubeeId (chaves
internas) é central no modelo — leia Identificadores: externalId, skuId, hubeeId
antes de codificar.
Passo 1 — Criar categorias em bulk¶
POST /integration/categories/bulk aceita uma lista de CategoryApiDTO e cria
em uma única transação. A ordem dentro do array importa: categorias pai devem
vir antes das filhas, porque o campo parent referencia o name (chave natural)
da categoria pai já integrada.
Request¶
curl -X POST 'https://api.gubee.com.br/integration/categories/bulk' \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '[
{
"id": "cat-erp-1",
"name": "Eletrônicos",
"description": "Categoria raiz de eletrônicos",
"active": true,
"enabledAutoIntegration": true
},
{
"id": "cat-erp-2",
"parent": "Eletrônicos",
"name": "Smartphones",
"active": true
},
{
"id": "cat-erp-3",
"parent": "Eletrônicos > Smartphones",
"name": "Acessórios para Smartphones",
"active": true
}
]'
Response 200¶
[
{ "id": "cat-erp-1", "hubeeId": "60f8a5c2e3b2a87654321000" },
{ "id": "cat-erp-2", "hubeeId": "60f8a5c2e3b2a87654321001" },
{ "id": "cat-erp-3", "hubeeId": "60f8a5c2e3b2a87654321002" }
]
Campos da CategoryApiDTO¶
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
não | externalId no ERP. Recomendado p/ idempotência. |
name |
string |
sim | Chave natural única por seller. |
parent |
string |
não | Nome da categoria pai. Para níveis profundos use "A > B > C". |
description |
string |
não | — |
active |
boolean |
não (false) | — |
enabledAutoIntegration |
boolean |
não (true) | Replicar automaticamente para marketplaces. |
Atualização em bulk¶
PUT /integration/categories/bulk com o mesmo formato, localizando por
externalId (id). Se a categoria não existir, retorna 404. Para atualizar uma
única, use PUT /integration/categories/{externalId}.
Passo 2 — Criar atributos em bulk¶
POST /integration/attributes/bulk segue o mesmo padrão. Atributos podem ser
de produto (aplicados ao produto todo) ou variantes (definem combinações de
variação — ex.: "Cor", "Tamanho").
Request¶
curl -X POST 'https://api.gubee.com.br/integration/attributes/bulk' \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '[
{
"id": "attr-cor",
"name": "Cor",
"label": "Cor",
"attrType": "TEXT",
"variant": true,
"required": true,
"options": ["Preto", "Branco", "Azul", "Vermelho"]
},
{
"id": "attr-tamanho",
"name": "Tamanho",
"attrType": "TEXT",
"variant": true,
"options": ["P", "M", "G", "GG"]
},
{
"id": "attr-material",
"name": "Material",
"attrType": "TEXT",
"variant": false,
"options": ["Plástico", "Alumínio", "Vidro"]
}
]'
Campos da AttributeApiDTO¶
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
string |
não | externalId no ERP. |
name |
string |
sim | Chave natural única. |
label |
string |
não | Rótulo exibido. |
attrType |
TEXT |
não (TEXT) | Apenas TEXT é tratado atualmente. |
variant |
boolean |
não (false) | Se true, define variações do produto. |
required |
boolean |
não (false) | Obriga preenchimento no produto. |
options |
List<String> |
não | Lista de valores permitidos para o atributo. |
Passo 3 — Criar marcas¶
Marcas não têm endpoint bulk. Crie uma a uma (paralelize, se necessário):
curl -X POST 'https://api.gubee.com.br/integration/brands' \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"id": "brand-samsung",
"name": "Samsung",
"description": "Samsung Electronics"
}'
Para atualizar: PUT /integration/brands/byExternalId/{externalId}.
Para remover uma marca obsoleta que está bloqueando upsert por nome:
DELETE /integration/brands/byExternalId/{externalId} (caso raro — Magento
recria marcas e o registro antigo conflita).
Passo 4 — Upsert de produtos via V2 (recomendado)¶
POST /integration/products/v2/createupdate é o endpoint canônico para
sincronização em massa. Ele é idempotente por externalId: se já existe,
atualiza; se não, cria.
A vantagem principal sobre o V1 (POST /integration/products) é que o campo
mainCategory aceita a string completa "level1 > level2 > level3" — o
backend resolve o hubeeId internamente, sem precisar de lookup prévio.
Request completo¶
curl -X POST 'https://api.gubee.com.br/integration/products/v2/createupdate' \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"externalId": "prod-erp-1001",
"mainSku": "PHONE-1001-BLK",
"name": "Samsung Galaxy S24 256GB Preto",
"mainCategory": "Eletrônicos > Smartphones",
"brand": "Samsung",
"status": "ACTIVE",
"type": "SIMPLE",
"origin": "NATIONAL",
"ncm": "8517.12.31",
"specifications": [
{ "name": "Material", "value": "Alumínio" }
],
"variations": [
{
"sku": "PHONE-1001-BLK",
"main": true,
"ean": "7891234567890",
"eans": [
{ "value": "7891234567890", "main": true },
{ "value": "17891234567897", "main": false }
],
"cost": 3200.00,
"warrantyTime": 365,
"warrantyType": "FACTORY_WARRANTY",
"handlingTime": 1,
"dimension": {
"weight": { "value": 0.167, "unit": "KILOGRAM" },
"length": 7.0, "width": 15.0, "height": 0.6
},
"images": [
{ "url": "https://cdn.erp.com/img/s24-front.jpg", "main": true },
{ "url": "https://cdn.erp.com/img/s24-back.jpg" }
],
"stocks": [
{ "qty": 50, "warehouseId": "DEFAULT", "crossDockingTime": { "value": 1, "unit": "DAY" } }
],
"prices": [
{ "value": 4999.90, "type": "DEFAULT" }
]
},
{
"sku": "PHONE-1001-WHT",
"main": false,
"ean": "7891234567891",
"cost": 3200.00,
"prices": [{ "value": 4999.90, "type": "DEFAULT" }],
"stocks": [{ "qty": 25, "warehouseId": "DEFAULT" }]
}
]
}'
Response 200¶
O endpoint retorna o productId (externalId) em text/plain:
Campos principais da CreateUpdateProductApiDTO¶
| Campo | Tipo | Obrigatório | Notas |
|---|---|---|---|
externalId |
string |
recomendado | Chave do ERP. Idempotência baseada aqui. |
mainSku |
string |
recomendado | SKU da variação principal. |
name |
string |
sim | — |
mainCategory |
string |
sim | "Cat1" ou "Cat1 > Cat2 > Cat3". |
brand |
string |
sim | Nome exato da marca já integrada. |
status |
ACTIVE/INACTIVE |
não (ACTIVE) | — |
type |
SIMPLE/KIT |
não (SIMPLE) | — |
origin |
NATIONAL/IMPORTED |
não (NATIONAL) | — |
ncm |
string |
não | — |
specifications |
Set<Attribute> |
não | Atributos de produto (não variantes). |
variations |
List<Variation> |
sim | Pelo menos 1. Variação main=true será a principal. |
Variação (CreateUpdateVariationApiDTO)¶
| Campo | Tipo | Obrigatório | Notas |
|---|---|---|---|
skuId |
string |
não no create | Gerado automaticamente no create; obrigatório no update. No PUT, variação sem skuId é recriada — veja PUT sem skuId. |
sku |
string |
sim | SKU natural do ERP. |
main |
boolean |
não (false) | Exatamente uma variação deve ser main=true. |
eans |
List<Ean> |
não | Códigos EAN/GTIN da variação: itens { "value", "main" }, até 20, um deles principal. Veja Lista de EANs. |
ean |
string |
não | Depreciado (sem data de remoção): EAN principal. Continua aceito. Ao enviar eans, envie também ean com o valor do item principal. |
cost |
BigDecimal |
não | Custo para cálculo de rentabilidade. |
dimension |
Dimension |
não | Peso + medidas para cálculo de frete. |
warrantyTime |
Long |
não | Dias. |
warrantyType |
SELLER_WARRANTY/FACTORY_WARRANTY |
não | Tipo de garantia. Omitido = mantém o valor atual. Valor fora da lista → 400. |
handlingTime |
Long |
não | Dias para despacho. |
images |
Set<Image> |
não | Veja Gestão de imagens. |
stocks |
Set<Stock> |
não | Atalho para criaar estoque junto. |
prices |
Set<Price> |
não | Atalho para criar preço junto. |
videos |
Set<Video> |
não | Merge cirúrgico (somente main/order editáveis). |
Importante sobre
videos: o campo no payload de produto serve apenas para metadados de apresentação (main,order). Não é possível criar vídeo via PUT de produto — use o fluxo dedicado em Upload de vídeos.
Lista de EANs¶
Uma variação pode ter mais de um código de barras (unidade e embalagem, GTIN-13
e GTIN-14, troca de fornecedor). O campo eans leva todos; um deles é o
principal.
{
"ean": "7891234567890",
"eans": [
{ "value": "7891234567890", "main": true },
{ "value": "17891234567897", "main": false }
]
}
| Campo | Tipo | Descrição |
|---|---|---|
eans[].value |
string |
Valor do EAN. |
eans[].main |
boolean |
true no EAN principal. Exatamente um item por lista. |
- A lista aceita até 20 itens, contados depois de juntar os repetidos.
eanestá depreciado, sem data de remoção. Na leitura, devolve o EAN principal deeans.- Os valores são gravados sem espaços nas pontas. Formato e dígito verificador não são validados.
- Valor repetido na lista não é erro: os repetidos viram um item só, na
posição do primeiro, e ele é o principal se alguma das ocorrências estiver
marcada. Item com
valueem branco é descartado; uma lista só com itens em branco vale comoeans: [].
Leitura. As consultas de produto devolvem ean e eans em cada variação.
Variação sem EAN devolve eans: [].
Escrita. Vale para POST /integration/products,
POST /integration/products/v2/createupdate, PUT /integration/products/{externalId}
e para o PATCH de variação.
Envie sempre ean junto com eans
A gravação da lista eans é habilitada por ambiente. Enquanto não estiver
habilitada no seu, o campo eans da requisição é ignorado e só o ean
é gravado: um payload só com eans não grava os EANs enviados, mesmo
quando a resposta é de sucesso. Envie os dois campos, com ean igual ao
value do item main: true — como nos exemplos desta página. Esse par
grava o principal com a lista desabilitada e a lista inteira com ela
habilitada.
Com a lista desabilitada, eans: [] sem ean só limpa o EAN em
POST e PUT de produto; no PATCH de variação é ignorado e nada muda.
Para remover os EANs em qualquer caso, envie ean: "".
Quando o payload traz um campo só:
| O que o payload traz | Resultado |
|---|---|
Nem ean nem eans (ausentes ou null) |
Os EANs gravados não são alterados. |
Só ean com valor |
O valor vira o principal. Se já era um secundário, é promovido e o principal anterior continua na lista; se não estava na lista, substitui o principal. Os demais secundários são mantidos. |
Só ean: "" |
Remove todos os EANs da variação. |
Só eans com itens |
A lista enviada substitui a gravada. O principal é o item com main: true; sem item marcado, o primeiro da lista. |
Só eans: [] |
Remove todos os EANs da variação. Com a gravação da lista desabilitada, só em POST e PUT; no PATCH é ignorado. |
Quem envia só ean não precisa mudar nada: o valor é gravado como principal e
a leitura seguinte devolve o mesmo valor em ean.
Quando o payload traz os dois campos (ean com valor e eans) numa
variação que já existe, cada um é comparado com o que está gravado — é o que
protege quem lê o produto, altera um campo e devolve o corpo inteiro:
eans enviada × gravada |
ean enviado × principal gravado |
Resultado |
|---|---|---|
| igual | igual | Nada muda. |
| igual | diferente | Vale o ean: vira o principal, como em "Só ean com valor". A lista enviada é o eco do que estava gravado e não é validada. |
| diferente | igual ou diferente | Vale a lista, e o par tem de ser coerente: ean precisa estar em eans e, se a lista traz um item com main: true, esse item precisa ser o ean. Se a lista não traz item marcado, o ean é o principal. Par incoerente → 400. |
"eans diferente da gravada" quer dizer: depois de tirar espaços, itens em
branco e repetidos, os valores ou a ordem mudaram. A marca main só entra na
comparação quando a lista enviada traz algum item marcado.
Na criação (produto novo ou variação nova) não há estado gravado: vale a
lista, o principal é o item com main: true (sem item marcado, o ean
enviado; sem ean, o primeiro) e o ean enviado precisa estar na lista.
ean: "" com eans com itens, na criação, grava a lista: o ean em branco
conta como ean não enviado.
Quando o payload traz ean: "" (vazio ou só com espaços) junto com eans,
numa variação que já existe:
eans enviada × gravada |
Resultado |
|---|---|
eans: [] |
Remove todos os EANs da variação. |
| igual | Remove todos os EANs da variação. É o caso de quem lê o produto, esvazia ean e devolve o corpo inteiro. |
| com itens e diferente | 400, motivo EAN_NOT_MAIN_OF_EANS. Nada é gravado. |
Com a gravação da lista desabilitada, eans é ignorado e ean: "" remove
todos os EANs, qualquer que seja a lista enviada.
Para remover todos os EANs, a forma que vale sempre — POST, PUT e PATCH,
com a gravação da lista habilitada ou não — é enviar só ean: "", sem
eans.
Exemplos. Trecho da variação no corpo da requisição. A variação está
gravada com 7891234567890 (principal) e 17891234567897.
Trocar o principal enviando só ean — aceito, o secundário é mantido:
Devolver o corpo lido alterando só ean — aceito, mesmo resultado (a lista é
o eco do gravado):
{
"ean": "7891234567999",
"eans": [
{ "value": "7891234567890", "main": true },
{ "value": "17891234567897", "main": false }
]
}
Acrescentar um secundário, par coerente — aceito, vale a lista:
{
"ean": "7891234567890",
"eans": [
{ "value": "7891234567890", "main": true },
{ "value": "17891234567897", "main": false },
{ "value": "27891234567894", "main": false }
]
}
Devolver o corpo lido com ean em branco — aceito, remove os dois EANs (a
lista é o eco do gravado):
{
"ean": "",
"eans": [
{ "value": "7891234567890", "main": true },
{ "value": "17891234567897", "main": false }
]
}
ean em branco com uma lista diferente da gravada — 400, motivo
EAN_NOT_MAIN_OF_EANS, nada é gravado:
{
"ean": "",
"eans": [
{ "value": "7891234567890", "main": true },
{ "value": "27891234567894", "main": false }
]
}
Lista alterada e ean fora dela — 400, motivo EAN_NOT_IN_EANS:
{
"ean": "7891234567999",
"eans": [
{ "value": "7891234567890", "main": true },
{ "value": "27891234567894", "main": false }
]
}
Lista alterada e ean na lista, mas o item marcado é outro — 400, motivo
EAN_NOT_MAIN_OF_EANS:
{
"ean": "17891234567897",
"eans": [
{ "value": "7891234567890", "main": true },
{ "value": "17891234567897", "main": false },
{ "value": "27891234567894", "main": false }
]
}
Repetir a mesma requisição. Um par coerente pode ser reenviado (retry)
quantas vezes for: o resultado é o mesmo. Um par incoerente que foi aceito
na primeira vez porque a lista era o eco do gravado (segundo exemplo acima)
recebe 400 se for reenviado igual — na segunda vez a lista já não é a
gravada. O EAN gravado pela primeira requisição continua lá. Antes de alterar
de novo, leia o produto ou envie o par coerente.
O corpo com ean: "" e a lista lida segue a mesma lógica: a primeira
requisição remove os EANs; repetida igual, a lista enviada já difere da gravada
(agora vazia) e a resposta é 400, motivo EAN_NOT_MAIN_OF_EANS. Os EANs
continuam removidos. Isso com a gravação da lista habilitada; com ela
desabilitada, a repetição é aceita e nada muda. Para remover de forma que
possa ser repetida sem erro, envie só ean: "", sem eans.
Erros de EAN¶
400 — lista recusada. Nada é gravado. A resposta é
application/problem+json, com type terminado em /invalid-ean-list e o
motivo, em texto, em detail (outros campos podem acompanhar):
{
"type": "http://www.gubee.com.br/problem/invalid-ean-list",
"status": 400,
"detail": "ean enviado nao esta na lista eans"
}
| Motivo | Quando | detail |
|---|---|---|
MULTIPLE_MAIN |
eans tem mais de um item com main: true. |
eans com mais de um item principal |
TOO_MANY |
eans tem mais de 20 itens, depois de juntar os repetidos. |
eans acima do limite de itens |
EAN_NOT_IN_EANS |
ean com valor enviado junto com eans, e o valor não está na lista. Na atualização, só quando a lista difere da gravada. |
ean enviado nao esta na lista eans |
EAN_NOT_MAIN_OF_EANS |
Atualização, quando a lista difere da gravada: o ean está nela, mas o item com main: true é outro; ou ean: "" enviado com eans com itens. |
ean enviado diverge do item principal de eans |
O nome do motivo não vem como campo da resposta: identifique o erro pelo type
e use o detail como mensagem. Só quem envia eans recebe esses erros.
409 — conflito no PATCH de variação que traz ean ou eans. type
terminado em /ean-patch-conflict: outra escrita alterou o EAN da mesma
variação enquanto o PATCH era gravado, em todas as tentativas internas.
Nada do PATCH foi gravado (nem os outros campos do corpo); repita a
requisição.
PUT sem skuId¶
Na atualização completa (PUT), a variação enviada sem skuId é tratada
como nova, mesmo com o sku de uma variação que já existe: a variação gravada
é removida e recriada só com o que veio no payload. Se ela tinha EANs
secundários e o payload traz só ean, os secundários são perdidos, sem erro.
Envie o skuId de cada variação existente (ele vem na leitura do produto e em
POST /integration/products/skuIds/bySkus) ou use o PATCH de variação.
Atenção
Omitir ean (ou enviar null) numa atualização completa não apaga o
EAN. Para remover, envie ean: "".
Busca. O filtro ean de POST /integration/products/list/search casa com
qualquer EAN da lista, principal ou secundário, de qualquer variação.
Passo 5 — Atualizações incrementais¶
Atualização total (PUT)¶
Use PUT /integration/products/{externalId} quando precisar reescrever o
produto inteiro (variações, dimensões, atributos, imagens, etc.). Substitui o
estado anterior:
curl -X PUT 'https://api.gubee.com.br/integration/products/prod-erp-1001' \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '{ ...produto completo... }'
Retorna 202 Accepted (sem body). Atualização é assíncrona via evento assíncrono na plataforma de streaming.
Também disponível por mainSkuId: PUT /integration/products/byMainSkuId/{mainSkuId}.
Atualização parcial (PATCH) — recomendada para correções pontuais¶
PATCH /integration/products/variations/sku/{sku} altera apenas os campos
informados no body. Nulos são ignorados. ean e eans seguem as regras de
Lista de EANs: ean: "" remove todos; eans: null mantém,
eans: [] remove todos e uma lista substitui a gravada (enviada junto com
ean com valor, o par tem de ser coerente). Enquanto a gravação da lista não
estiver habilitada no ambiente, o PATCH ignora eans — inclusive eans: [],
que sozinho não altera nada; use ean: "" para remover. O PATCH com ean ou
eans pode responder 409 — veja
Erros de EAN. Semântica de images:
null→ preserva atual[]→ remove todas[...]→ substitui atomicamente (URLs/existentes são preservadas via merge server-side porurl+uuid)
curl -X PATCH 'https://api.gubee.com.br/integration/products/variations/sku/PHONE-1001-BLK' \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"cost": 2999.00,
"ean": "7891234567999",
"warrantyTime": 540,
"warrantyType": "SELLER_WARRANTY",
"handlingTime": 2,
"dimension": { "weight": { "value": 0.170, "unit": "KILOGRAM" } },
"name": "Galaxy S24 256GB Preto Edição 2025"
}'
Response:
PATCH /integration/products/{externalId} atualiza campos escalares do produto
(name, nbm, origin, originCountry, disableIntegration).
Endpoints granulares de imagem¶
Para operações cirúrgicas sem reescrever a lista:
| Operação | Endpoint |
|---|---|
| Adicionar imagem | POST /integration/products/{externalId}/variations/{skuId}/images |
| Remover imagem | DELETE /integration/products/{externalId}/variations/{skuId}/images/{imageUuid} |
| Definir como principal | PUT /integration/products/{externalId}/variations/{skuId}/images/{imageUuid}/main |
| Reordenar | PUT /integration/products/{externalId}/variations/{skuId}/images/order |
| Patch metadados | PATCH /integration/products/{externalId}/variations/{skuId}/images/{imageUuid} |
Passo 6 — Mapear SKUs do ERP para skuId interno¶
A função mais usada em integração contínua. O ERP trabalha com o SKU natural;
os endpoints de leitura internos (estoque, preço, pedidos) frequentemente
retornam o skuId interno do Gubee.
POST /integration/products/skuIds/bySkus resolve um lote de SKUs em skuIds:
curl -X POST 'https://api.gubee.com.br/integration/products/skuIds/bySkus' \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '["PHONE-1001-BLK", "PHONE-1001-WHT", "PHONE-1002-BLK"]'
Response¶
[
{
"id": "60f8a5c2e3b2a87654321100",
"skuId": "sku-uuid-1001-blk",
"sku": "PHONE-1001-BLK",
"ean": "7891234567890"
},
{
"id": "60f8a5c2e3b2a87654321101",
"skuId": "sku-uuid-1001-wht",
"sku": "PHONE-1001-WHT",
"ean": "7891234567891"
}
]
SKUs não encontrados são omitidos do array de resposta (não geram erro). Compare o tamanho do array retornado com o enviado para detectar SKUs pendentes.
Também disponível:
- GET /integration/products/skuIds/bySkus?skus=A,B,C (limitado pela URL).
- GET /integration/products/skus/bySkuIds?skuIds=X,Y — caminho inverso.
- POST /integration/products/skus/bySkuIds — versão POST do anterior.
Variação retornada: VariationSkuApiMapDTO¶
| Campo | Tipo | Descrição |
|---|---|---|
id |
string |
ID interno da variação. |
skuId |
string |
skuId usado em endpoints internos. |
sku |
string |
SKU natural do ERP. |
ean |
string |
EAN principal, se cadastrado. Esta resposta não traz a lista eans. |
Tratamento de erros¶
409 Conflict — EAN alterado durante o PATCH¶
type terminado em /ean-patch-conflict, só no PATCH de variação que traz
ean ou eans. Nada foi gravado; repita a requisição. Veja
Erros de EAN.
409 Conflict — externalId duplicado¶
Ocorre quando duas requests concorrentes tentam criar o mesmo externalId, ou
quando há inconsistência no banco (extremamente raro). Solução:
- Faça
GET /integration/products/{externalId}para checar o estado atual. - Se existir, use
PUT(ouPATCH) ao invés dePOST. - Se não existir mas ainda assim receber 409, aguarde 5s e refaça o GET.
404 — Relação não integrada¶
Some of relation [attribute, category, brand] not integrated before. Causa:
você está referenciando no payload um nome/ID que ainda não foi integrado.
Verifique se categoria/marca/atributo já existem (via GET /byName ou
GET /{externalId} dos respectivos endpoints) antes de reenviar o produto.
400 — Validação¶
mainSkuem branco.variations[].skuem branco.- Mais de uma variação
main=true. - Lista de EANs recusada (
typeterminado em/invalid-ean-list): mais de um itemmain=true, mais de 20 itens, ou — quando a lista difere da gravada —eanenviado que não está emeansou que não é o item principal.eannovo com a lista igual à gravada não é erro. Veja Erros de EAN. - Categoria como
hubeeIdao invés do nome completo (no V2, use sempre o caminho"A > B > C").
Performance — sincronizando 10k+ produtos¶
Paralelização¶
O limite prático por seller é ~20 requisições concorrentes no endpoint
/v2/createupdate. Acima disso o backend começa a aplicar backpressure.
Estratégia recomendada:
┌── worker 1 (POST produtos 1..500)
├── worker 2 (POST produtos 501..1000)
fila ERP ────┼── worker 3 (POST produtos 1001..1500)
├── ...
└── worker N (POST produtos 9501..10000)
Cada worker consome um batch local de 100-500 itens em sequência. A paralelização real acontece entre workers, não dentro de um único worker.
Tamanho de batch¶
- Categorias/atributos em bulk: até 200 por chamada.
- Produtos: o endpoint
/v2/createupdateaceita um produto por chamada. Use pool de conexões HTTP persistente (Keep-Alive) e pipelining.
Idempotência¶
O campo externalId é a âncora de idempotência. Pode-se reexecutar a
sincronização integral do catálogo a qualquer momento — produtos existentes
serão atualizados em vez de duplicados.
Carga inicial vs incremental¶
- Carga inicial: rode à noite ou em janela de baixo tráfego. 10k produtos com 3 variações em média = ~30k chamadas. A ~5/s por worker com 10 workers = ~10 min.
- Incremental: monitore mudanças no ERP (timestamp de última alteração) e sincronize apenas o delta.
Read-after-write¶
O Gubee usa CQRS com eventual consistência entre comando e leitura. Após um
POST /v2/createupdate retornar 200, o produto pode levar de 1 a 5 segundos
para aparecer em GET /integration/products/{externalId}. Veja
Eventual consistency e CQRS.
Checklist final¶
- Categorias integradas (pais antes dos filhos).
- Atributos integrados (variantes e não-variantes).
- Marcas integradas.
- Produtos upsertados via
/v2/createupdate. - Tabela de mapeamento
sku → skuIdpersistida no ERP (via/skuIds/bySkus). - Estoque e preço sincronizados — veja Estoque e preço.
- Worker de pedidos em pé — veja Receber pedidos.
Próximos passos¶
- Estoque e preço — sync de quantidade e preço por SKU.
- Receber pedidos — fila de pedidos por status.
- Faturamento e DANFE — emissão de notas via invoicer.
- Etiquetas de envio — geração de tags e PLP.