Introdução
A API é REST sobre HTTPS com corpos JSON (criação de produto também aceita multipart/form-data para envio direto de imagem). Todas as rotas ficam sob /api/v1 e operam exclusivamente sobre o catálogo da empresa dona da chave — nunca é possível ler ou alterar dados de outra empresa.
As chaves de API são geradas no painel da empresa em Configurações → Chaves de API (requer assinatura ativa).
Autenticação
Toda requisição deve enviar a chave no header Authorization (ou, alternativamente, em X-Api-Key):
Authorization: Bearer mo_live_XXXXXXXXXXXX_YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
- A chave completa é exibida uma única vez na criação — armazenamos apenas um hash.
- Chaves podem ser revogadas individualmente no painel; a revogação é imediata.
- Se a assinatura da empresa expirar, a API responde
403 subscription_inactive— a mesma chave volta a funcionar quando o pagamento é regularizado.
Formato de erros
Erros sempre retornam o envelope:
{"error": {"code": "conflict", "message": "já existe um produto com este SKU na sua empresa"}}| HTTP | code | Quando ocorre |
|---|---|---|
400 | invalid_argument | Corpo/parâmetros inválidos (JSON malformado, image_url inacessível, campo obrigatório ausente) |
401 | unauthorized | Chave de API ausente, inválida ou revogada |
403 | subscription_inactive | Assinatura da empresa não está ativa — a chave volta a funcionar ao regularizar o pagamento |
404 | not_found | Recurso não encontrado (ou não pertence à sua empresa) |
409 | conflict | Conflito — ex.: SKU duplicado na empresa |
422 | plan_limit_reached | Regra de plano — ex.: limite de produtos atingido |
429 | rate_limited | Muitas requisições — aguarde e tente novamente |
500 | internal | Erro interno — tente novamente mais tarde |
Rate limits
- ~120 requisições/minuto por chave de API (todas as rotas).
- Criações de produto com download de
image_urltêm um limite adicional mais restrito (~30/min). - Ao exceder, a resposta é
429 rate_limited— aguarde e repita com backoff.
Criar produto
POST/api/v1/products
Cria um produto no catálogo da sua empresa. Aceita JSON (com image_url para o servidor baixar a imagem) ou multipart/form-data (com o arquivo no campo "image" — o arquivo tem prioridade sobre image_url). Marca e categoria podem ser informadas por nome (brand/category — criadas automaticamente se não existirem) ou por id (brand_id/category_id). Adoção do catálogo pré-cadastrado ("Adiciona fácil"): informe shared_product_id (obtido em GET /shared-products) ou apenas o gtin (código de barras) — nome, descrição, foto, marca e categoria são herdados e você só precisa enviar o seu preço (e SKU, se quiser).
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | sim* | Nome do produto (*opcional na adoção via shared_product_id/gtin — é herdado) |
price | number | sim | Preço em reais (> 0) |
shared_product_id | uuid | não | Adota um produto do catálogo pré-cadastrado — campos descritivos são herdados |
gtin | string | não | Código de barras (EAN-8/UPC-A/EAN-13/GTIN-14). Se já existir no catálogo pré-cadastrado, adota a entrada; senão registra o código na entrada nova |
description | string | não | Descrição curta |
sku | string | não | Código interno/ERP — único por empresa (máx. 64) |
in_stock | boolean | não | Visibilidade na vitrine (padrão true) |
sale_unit | string | não | Unidade de venda: unidade (padrão), caixa, pacote, fardo, engradado, palete, kit, combo, dúzia, saco, bandeja ou galão |
wholesale_price | number | não | Preço de atacado em reais (> 0 e menor que price). Deve vir junto de wholesale_min_qty |
wholesale_min_qty | number | não | Quantidade mínima para valer o preço de atacado (mínimo 2). Deve vir junto de wholesale_price |
brand | string | não | Marca por nome (cria se não existir) |
category | string | não | Categoria por nome (cria se não existir) |
brand_id / category_id | uuid | não | Ids existentes (têm prioridade sobre o nome) |
image_url | string | não | URL http(s) pública da imagem — baixada e convertida para WebP |
image | arquivo | não | Somente multipart: JPG, PNG ou WEBP até 10 MB |
201— Produto criado — retorna {"id": "...", "message": "produto criado"}409— SKU duplicado na sua empresa422— Sem plano ativo ou limite de produtos do plano atingido
# JSON com image_url
curl -X POST https://SEU-DOMINIO.com/api/v1/products \
-H "Authorization: Bearer SUA_CHAVE_API" \
-H "Content-Type: application/json" \
-d '{
"name": "Café Torrado 500g",
"description": "Café torrado e moído",
"price": 19.90,
"sale_unit": "unidade",
"wholesale_price": 17.90,
"wholesale_min_qty": 6,
"sku": "CAF-500",
"brand": "Pilão",
"category": "Mercearia",
"image_url": "https://exemplo.com/cafe.jpg"
}'
# multipart com arquivo local
curl -X POST https://SEU-DOMINIO.com/api/v1/products \
-H "Authorization: Bearer SUA_CHAVE_API" \
-F "name=Arroz Tipo 1 5kg" \
-F "price=25.50" \
-F "sku=ARZ-5KG" \
-F "category=Mercearia" \
-F "image=@/caminho/arroz.jpg"Sincronizar por SKU (upsert)
PUT/api/v1/products/by-sku/{sku}
Criação ou atualização idempotente pelo SKU — feito para sincronização recorrente de ERP: pode reenviar o estoque inteiro toda noite com segurança. SKU novo: cria o produto (mesmos campos do create, incluindo adoção via shared_product_id/gtin). SKU existente: atualiza somente os campos enviados (price, in_stock, name, description, image_url, sale_unit, wholesale_price, wholesale_min_qty).
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
sku | path | sim | SKU do produto na sua empresa (máx. 64) |
price | number | na criação | Preço em reais (> 0); na atualização, envie para alterar |
wholesale_price / wholesale_min_qty | number | não | Preço de atacado + quantidade mínima (sempre juntos). Na atualização: omitir mantém, enviar 0 em qualquer um remove o atacado |
demais campos | — | não | Iguais ao POST /products (name, gtin, shared_product_id, in_stock, sale_unit, ...) |
201— Produto criado (SKU não existia)200— Produto atualizado422— Sem plano ativo ou limite de produtos atingido (só na criação)
curl -X PUT https://SEU-DOMINIO.com/api/v1/products/by-sku/CAF-500 \
-H "Authorization: Bearer SUA_CHAVE_API" \
-H "Content-Type: application/json" \
-d '{"gtin": "7896089012347", "price": 18.90, "in_stock": true, "wholesale_price": 16.90, "wholesale_min_qty": 6}'Listar produtos
GET/api/v1/products
Lista apenas os produtos da empresa dona da chave, incluindo os ocultos (in_stock=false). Suporta paginação e filtros. Cada item traz também sale_unit, wholesale_price e wholesale_min_qty (null quando o produto não tem preço de atacado).
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
limit | query | não | Máx. de itens (padrão 30, máx. 100) |
offset | query | não | Deslocamento da paginação |
search | query | não | Busca por nome ou SKU (parcial) |
sku | query | não | SKU exato |
200— {"products": [...], "has_more": bool}
curl "https://SEU-DOMINIO.com/api/v1/products?limit=50&search=cafe" \ -H "Authorization: Bearer SUA_CHAVE_API"
Atualizar produto
PATCH/api/v1/products/{id}
Atualização parcial: envie apenas os campos que deseja alterar — os demais são mantidos. Mesmos campos do create (exceto brand/category por nome; use brand_id/category_id). Atacado: envie wholesale_price e wholesale_min_qty juntos para definir; envie 0 em qualquer um deles para remover; omita ambos para manter.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
sale_unit | string | não | Unidade de venda (unidade, caixa, pacote, fardo, engradado, palete, kit, combo, dúzia, saco, bandeja, galão) |
wholesale_price | number | não | Preço de atacado (> 0 e menor que price). Enviar 0 remove o atacado |
wholesale_min_qty | number | não | Quantidade mínima do atacado (mínimo 2). Enviar 0 remove o atacado |
200— Produto atualizado404— Produto não encontrado (ou não pertence à sua empresa)409— SKU duplicado
curl -X PATCH https://SEU-DOMINIO.com/api/v1/products/ID_DO_PRODUTO \
-H "Authorization: Bearer SUA_CHAVE_API" \
-H "Content-Type: application/json" \
-d '{"price": 18.50, "in_stock": true}'
# definir preço de atacado (sempre o par junto)
curl -X PATCH https://SEU-DOMINIO.com/api/v1/products/ID_DO_PRODUTO \
-H "Authorization: Bearer SUA_CHAVE_API" \
-H "Content-Type: application/json" \
-d '{"wholesale_price": 16.90, "wholesale_min_qty": 6}'
# remover o preço de atacado
curl -X PATCH https://SEU-DOMINIO.com/api/v1/products/ID_DO_PRODUTO \
-H "Authorization: Bearer SUA_CHAVE_API" \
-H "Content-Type: application/json" \
-d '{"wholesale_price": 0}'Criar marca
POST/api/v1/brands
Cria uma marca na sua empresa. Nomes são únicos sem diferenciar maiúsculas: se já existir, a marca existente é reaproveitada (resposta 200 com "reused": true).
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | sim | Nome da marca |
201— Marca criada200— Marca já existente reaproveitada ("reused": true)
curl -X POST https://SEU-DOMINIO.com/api/v1/brands \
-H "Authorization: Bearer SUA_CHAVE_API" \
-H "Content-Type: application/json" \
-d '{"name": "Pilão"}'Criar categoria
POST/api/v1/categories
Cria uma categoria na sua empresa (reaproveita se o nome já existir). O campo icon é opcional.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string | sim | Nome da categoria |
icon | string | não | Identificador de ícone exibido na vitrine |
201— Categoria criada200— Categoria já existente reaproveitada
curl -X POST https://SEU-DOMINIO.com/api/v1/categories \
-H "Authorization: Bearer SUA_CHAVE_API" \
-H "Content-Type: application/json" \
-d '{"name": "Mercearia"}'Webhook de estoque
POST/api/v1/webhooks/stock
Endpoint dedicado para o seu ERP sincronizar disponibilidade: altera SOMENTE o campo in_stock do produto, identificado por sku ou product_id. Com in_stock=false o produto some da vitrine, das buscas e do mapa imediatamente; com true ele volta.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
sku | string | um dos dois | SKU do produto na sua empresa |
product_id | uuid | um dos dois | Id do produto |
in_stock | boolean | sim | true = visível, false = oculto |
200— Estoque atualizado404— Produto não encontrado
curl -X POST https://SEU-DOMINIO.com/api/v1/webhooks/stock \
-H "Authorization: Bearer SUA_CHAVE_API" \
-H "Content-Type: application/json" \
-d '{"sku": "CAF-500", "in_stock": false}'