Catálogo

Produtos

Criar, atualizar, listar e desativar produtos — o caminho para subir um catálogo inteiro.

Todas as páginas▾

O catálogo é a razão de existir desta API. Se o seu sistema tem um id próprio para cada produto, comece pela chave externa: ela é o que torna reenviar o catálogo inteiro seguro, sem duplicar nada.

  • GET/v1/productsLista produtos. Filtros: query (nome ou SKU), active=true|false, page, pageSize.
  • POST/v1/productsCria um produto. 201 no sucesso.
  • GET/v1/products/{id}Um produto, com imagens, estoque por depósito, categorias e variações.
  • PATCH/v1/products/{id}Atualização parcial — só os campos enviados mudam.
  • DELETE/v1/products/{id}Apaga o produto sem pedidos; desativa o que já foi vendido.
  • PUT/v1/products/external/{source}/{externalId}Cria ou atualiza pelo id do SEU sistema — reenviar nunca duplica.
  • GET/v1/products/external/{source}/{externalId}O produto da sua chave, ou 404 product_not_found.
  • PUT/v1/products/{id}/stockDefine o estoque absoluto de um depósito (produto sem variação).
  • GET/v1/categoriesAs categorias da loja — os slugs que categorySlugs aceita.

Criar um produto

Requisição
curl -X POST https://api.zankhapi.com.br/v1/products \
  -H "Authorization: Bearer zk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Placa de vídeo RTX 4070",
    "sku": "GPU-4070",
    "regularPriceCents": 349990,
    "compareAtPriceCents": 399990,
    "shortDescription": "12GB GDDR6X",
    "imageUrls": ["https://exemplo.com/rtx4070-frente.jpg", "https://exemplo.com/rtx4070-verso.jpg"],
    "categorySlugs": ["placas-de-video"],
    "boxWidthCm": 30, "boxHeightCm": 8, "boxLengthCm": 40, "boxWeightKg": 1.4,
    "stockQuantity": 5
  }'

Campos do produto

CampoTipoDescrição
nameobrigatóriostringO nome do produto.
regularPriceCentsobrigatóriointegerPreço de venda em centavos.
slugstringO endereço do produto na vitrine. Gerado a partir do nome quando omitido. É único na rede inteira (o marketplace acha o produto só pelo slug): um já usado, mesmo por outra loja, é gravado com um sufixo curto — leia o slug de volta na resposta.
skustringO seu código interno. É por ele que a sua próxima importação reencontra o item.
shortDescriptionstringUma linha, mostrada nas listagens.
longDescriptionHtmlstringA descrição completa da página do produto.
compareAtPriceCentsintegerO preço “de”, riscado. Envie null num PATCH para encerrar a promoção.
storeInstallmentPriceCentsintegerPreço parcelado exibido. Não altera o que é cobrado no checkout.
imageUrlsstring[]Até 12 fotos, na ordem da galeria. Copiadas para a zankh — veja Imagens. Substitui a galeria inteira; [] limpa.
imageUrlstringLegado: uma foto só. Envie imageUrl ou imageUrls, nunca os dois. Numa atualização troca só a foto principal.
categorySlugsstring[]Até 20 slugs de categorias da loja. Substitui as categorias do produto.
gtinstringCódigo de barras (EAN/GTIN).
brandstringA marca.
boxWidthCm / boxHeightCm / boxLengthCm / boxWeightKgnumberA caixa de envio — é ela que calcula o frete.
preparationDaysintegerDias úteis de preparo antes do despacho (0–90).
stockQuantityintegerEstoque inicial. Depois disso use PUT /stock.
stockLocationNamestringO depósito do estoque. Omitido, usa o padrão da loja.
isActivebooleanSe o produto aparece na vitrine. Padrão true.
isPurchasablebooleanSe aceita compra. Um produto ativo e não comprável fica visível como vitrine.

A resposta traz, além do que você enviou: images, stock (os depósitos do PRODUTO — o depósito de uma variação aparece na variação, nunca aqui), totalStock (a soma desses depósitos), `sellableStock` (esses depósitos + o estoque das variações ATIVAS — o número que dá para vender; use este), categories, hasVariants, variationTypes, variants e externalRef.

Imagens

Toda URL de imageUrls (e de imageUrl) é baixada e copiada para o armazenamento da zankh durante a chamada; o produto guarda a URL da zankh, não a sua. Nada é linkado direto do seu servidor.

  • A origem só precisa funcionar durante a chamada — uma URL assinada de curta duração (10 minutos bastam) de um bucket privado serve.
  • Só https:// na porta padrão; JPEG, PNG ou WebP (conferido pelo conteúdo, não pela extensão); até 10 MB e 10 s cada; no máximo 3 redirecionamentos. Endereço de rede privada é recusado.
  • Toda foto vira WebP (lado maior de até 1600 px) em https://img.zankh.com.br/imported/{storeId}/{hash}.webp. O hash é do arquivo convertido: reenviar a mesma origem baixa de novo, mas cai na mesma URL — nada muda no produto.
  • Tudo ou nada: se uma foto falha, nada é gravado e a resposta é 422 image_fetch_failed com index (base 0), url e reason (invalid_url, blocked_address, too_many_redirects, timeout, http_status, too_large, not_an_image ou decode_failed — o arquivo parece imagem mas o conteúdo não é legível).
  • URLs que o produto já mostra (como vieram em images) e URLs da zankh da SUA loja no formato acima não são baixadas de novo — reordene a galeria reenviando-as na ordem nova. URL da zankh de outra loja é baixada como qualquer outra.
  • A cópia é síncrona: 12 fotos grandes podem levar ~30 s. Dê a essas chamadas um timeout de 60–90 s.
  • No máximo 2 chamadas copiando fotos ao mesmo tempo por loja; a terceira recebe 429 too_many_requests.
  • Sem o armazenamento disponível a resposta é 503 image_storage_unavailable — chamadas sem foto continuam funcionando.

Categorias

GET /v1/categories lista as categorias da loja: id, slug, name, parentId, additionalParentIds (uma categoria pode ficar também sob outros pais), position, productCount (direto), productCountWithDescendants e isCatchAll. A API nunca cria categoria: um slug que não existe na loja responde 422 unknown_category com os slugs que faltaram. Todo produto continua também em "Todos os produtos" (isCatchAll: true).

Chave externa (upsert)

PUT /v1/products/external/{source}/{externalId} cria ou atualiza o produto que o SEU sistema chama de externalId. Reenviar nunca duplica — use isto no lugar de "procurar pelo SKU e decidir entre POST e PATCH" (SKU não é único na zankh).

 
curl -X PUT https://api.zankhapi.com.br/v1/products/external/meuerp/sku-985561 \
  -H "Authorization: Bearer zk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Gato articulado",
    "regularPriceCents": 6990,
    "imageUrls": ["https://arquivos.exemplo.com/assinada/gato-1.png?sig=..."],
    "categorySlugs": ["animais-e-esculturas"],
    "preparationDays": 3
  }'
  • source nomeia a sua integração (2–32 caracteres: minúsculas, dígitos e -). externalId: 1–128 caracteres entre letras, dígitos, ., _, : e -. A chave é por loja.
  • O corpo é o de criação (name e regularPriceCents sempre obrigatórios) mais adoptProductId opcional.
  • Chave nova → cria (201). Chave existente → atualiza (200): o que veio sobrescreve, null limpa, o ausente fica. Na atualização, slug é ignorado (renomear URL publicada quebra link e SEO) e um isActive ausente mantém o que a loja decidiu.
  • `adoptProductId` (migração, uma vez só): liga a chave nova a um produto que já existe em vez de criar outro. O produto precisa ser seu (404) e não ter outra chave (409 external_ref_conflict) — nem ter sido importado de um marketplace (Shopee, Mercado Livre...), que é quem o mantém sincronizado: um produto tem um dono só.
  • stockQuantity num produto com variação ativa responde 409 product_has_variants.
  • Mudar o preço do produto também move as variações que estavam exatamente no preço antigo — a zankh trata essas como herdeiras dele; variação com preço próprio fica. Se você controla preço por variação, envie o produto primeiro e as variações depois: aquela chamada é absoluta e tem a última palavra.

Atualizar

PATCH é parcial: o que você não enviar fica como está. É o que permite um script de preço mexer só em preço, sem arrastar a descrição junto.

 
curl -X PATCH https://api.zankhapi.com.br/v1/products/PRODUTO_ID \
  -H "Authorization: Bearer zk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "regularPriceCents": 329990, "compareAtPriceCents": null }'

Apagar ou desativar

DELETE apaga de verdade um produto que nunca foi vendido (resposta { id, deleted: true }) e desativa o que já está em algum pedido ({ id, deleted: false, isActive: false }): ele sai da vitrine e continua existindo para os pedidos que o referenciam. Um pedido do mês passado não pode ficar sem saber o que foi vendido.

Subir um catálogo inteiro

  1. Envie cada produto com PUT /v1/products/external/{source}/{seuId} — cria o que falta e atualiza o que já existe, um por chamada (não há rota em lote no v1).
  2. Produtos criados antes da chave: case pelo sku (GET /v1/products?pageSize=200) e mande adoptProductId uma vez.
  3. Depois do produto, as variações dele, se houver.
  4. 409 slug_taken só aparece quando duas chamadas disputam o mesmo slug livre no mesmo instante — repita a chamada.