API Pública v1

Integre seu sistema externo com o Katalog via REST

Introdução

A API pública v1 permite que sistemas externos integrem com sua loja. Use-a para sincronizar produtos com seu ERP, importar pedidos, automatizar movimentações de estoque ou exportar clientes.

Base URL: https://katalog.com.br/api/v1

Autenticação

Todas as requisições devem incluir um Bearer token no header. Crie tokens no painel em /painel/api-tokens.

Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

O token está vinculado a uma loja específica. Não compartilhe seu token publicamente. Se for comprometido, revogue-o no painel e gere um novo.

Formato de resposta

Todas as respostas são JSON. Em caso de erro:

{
  "erro": "Mensagem descritiva do erro"
}

Códigos HTTP usados:

  • 200 — sucesso
  • 201 — recurso criado
  • 400 — erro de validação
  • 401 — token inválido ou ausente
  • 404 — recurso não encontrado
  • 500 — erro interno

Produtos

GET /produtos — Listar produtos

Query params:

  • busca — filtra por nome, código de barras, SKU ou codIntegracao (quando numérico)
  • categoriaId — filtra por categoria
  • apenasDisponiveistrue para esconder os indisponíveis
  • page, limite — paginação (default 1, 50)

Cada item da lista retorna também codIntegracao (Código ERP — inteiro identificador no sistema externo, ex.: Integreon) e as variações trazem o própriocodIntegracao.

URL pública: o campo url traz o link absoluto do produto no storefront (mesma URL do canonical/og:url) — formato https://{slug}.katalog.com.br/produto/{id} ou o domínio personalizado da loja quando configurado. Devolvido em GET /produtos, GET /produtos/[id], POST /produtos e PUT /produtos/[id].

curl
curl https://katalog.com.br/api/v1/produtos?limite=10 \ -H "Authorization: Bearer sk_live_..."

GET /produtos/[id] — Detalhes

curl
curl https://katalog.com.br/api/v1/produtos/PRODUTO_ID \ -H "Authorization: Bearer sk_live_..."

POST /produtos — Criar produto

Aceita codIntegracao (inteiro) para vincular o registro ao ID do produto no sistema externo (Integreon, ERP, etc). Se categoriaId não for enviado, o produto cai automaticamente em uma categoria "Sem categoria" criada na loja (uma única vez). Isso evita ter que fazer GET /categorias antes de cada importação.

Aceita também unidade (máx 10 chars, ex: UN, KG, L, M, PC; normalizado para maiúsculas; default PC) e multiploVenda (decimal > 0, default 1; ex: 10 para fardo de 10). Ambos podem ser editados depois via PUT.

Imagens externas: se você enviar imagens: ["https://outro-dominio/..."], baixamos cada URL e re-hospedamos no nosso S3 antes de gravar. Sem isso, o catálogo público bloqueia o domínio externo no <Image> do Next e mostra um quadrado vazio. URLs que já estão no nosso S3/CloudFront passam intactas.

curl
curl -X POST https://katalog.com.br/api/v1/produtos \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "nome": "Camiseta Branca", "preco": 49.90, "descricao": "100% algodão", "codigoBarras": "7891234567890", "sku": "CAM-BR-M", "codIntegracao": 1234, "controlaEstoque": true, "quantidadeEstoque": 100 }'

PUT /produtos/[id] — Atualizar

Atualiza campos parciais (envie só os que mudaram). Dispara o webhook produto.atualizado. Aceita:

  • nome, descricao, preco, precoPromocional
  • categoriaId (validado: precisa pertencer à loja)
  • imagens (array de URLs), sku, codigoBarras
  • codIntegracao — Código ERP (inteiro). Envie null para limpar
  • disponivel, destaque, ordem
  • controlaEstoque, quantidadeEstoque, estoqueMinimo
  • peso (kg), altura, largura, comprimento (cm) — para frete em marketplaces
curl
curl -X PUT https://katalog.com.br/api/v1/produtos/PRODUTO_ID \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "preco": 59.90, "quantidadeEstoque": 80, "peso": 0.250, "altura": 5, "largura": 30, "comprimento": 40 }'

Resposta 200: produto completo (mesmo formato do GET).

DELETE /produtos/[id] — Remover

Soft-delete: marca o produto como removido (deletadoEm = now() e disponivel = false). O produto somem dos GET da API mas permanece no histórico de pedidos. Idempotente — retorna 404 se já foi removido. Dispara o webhook produto.removido.

curl
curl -X DELETE https://katalog.com.br/api/v1/produtos/PRODUTO_ID \ -H "Authorization: Bearer sk_live_..."
{ "id": "PRODUTO_ID", "removido": true }

Variações

Variações são as combinações de atributos de um produto (ex.: tamanho/cor). Cada variação tem seu próprio preço, estoque, SKU, código de barras e codIntegracao.

GET /produtos/[id]/variacoes — Listar variações do produto

curl
curl https://katalog.com.br/api/v1/produtos/PRODUTO_ID/variacoes \ -H "Authorization: Bearer sk_live_..."

POST /produtos/[id]/variacoes — Criar variação

curl
curl -X POST https://katalog.com.br/api/v1/produtos/PRODUTO_ID/variacoes \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "nome": "Tamanho M", "valor1": "M", "valor2": "Branco", "preco": 49.90, "sku": "CAM-BR-M", "codigoBarras": "7891234567891", "codIntegracao": 5678, "quantidadeEstoque": 30 }'

GET /variacoes/[id] — Detalhes

PUT /variacoes/[id] — Atualizar

Aceita os mesmos campos do POST (todos opcionais). Envie codIntegracao: nullpara limpar.

curl
curl -X PUT https://katalog.com.br/api/v1/variacoes/VARIACAO_ID \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "preco": 54.90, "codIntegracao": 9999 }'

DELETE /variacoes/[id] — Remover

Remoção definitiva (hard delete). Não há soft-delete para variações.

Complementos (Grupos)

Grupos de complemento são reutilizáveis (um mesmo grupo "Sabores" pode ser vinculado a vários produtos). Os itens do grupo são armazenados como JSON e cada item aceita nome, preco, estoque e codIntegracao.

GET /grupos-complemento — Listar grupos

curl
curl https://katalog.com.br/api/v1/grupos-complemento \ -H "Authorization: Bearer sk_live_..."

POST /grupos-complemento — Criar grupo

curl
curl -X POST https://katalog.com.br/api/v1/grupos-complemento \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "nome": "Sabores", "tipo": "SELECAO", "obrigatoria": true, "minimo": 1, "maximo": 1, "itens": [ { "nome": "Chocolate", "preco": 0, "codIntegracao": 11 }, { "nome": "Morango", "preco": 0, "codIntegracao": 12 }, { "nome": "Baunilha", "preco": 0, "codIntegracao": 13 } ] }'

Campos do body:

  • tipoSELECAO (1 opção) ou MULTIPLA (várias)
  • obrigatoria, minimo, maximo — regras de seleção
  • itens — array de objetos { nome, preco, estoque?, codIntegracao? }. O codIntegracao dentro do item é o Código ERP daquele complemento.

GET /grupos-complemento/[id]

PUT /grupos-complemento/[id] — Atualizar

Se itens estiver presente, substitui completamente a lista anterior (não faz merge item-a-item). Reenvie a lista completa, com oscodIntegracao atualizados.

DELETE /grupos-complemento/[id] — Remover

Categorias

GET /categorias — Listar

Lista todas as categorias da loja.

POST /categorias — Criar

Cria categoria. nome é único por loja (case-insensitive). ordem é opcional — quando omitido, vai pro fim. Hierarquia Categoria → Subcategoria existe no schema mas é tratada em endpoint separado; categoriaPaiId no body é ignorado.

curl
curl -X POST https://katalog.com.br/api/v1/categorias \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "nome": "Hambúrgueres", "ordem": 1, "ativa": true }'

GET /categorias/[id] — Detalhes

PUT /categorias/[id] — Atualizar

Body (todos opcionais): nome, descricao, ordem, ativa. Renomear pra nome já usado por outra categoria retorna 400.

DELETE /categorias/[id] — Remover

Bloqueia (409) se a categoria tiver produtos ou subcategorias vinculados — sem isso, a cascata onDelete: Cascade apagaria os produtos junto. Realoque os produtos via PUT /produtos/[id] com novo categoriaId antes.

curl
curl -X DELETE https://katalog.com.br/api/v1/categorias/CAT_ID \ -H "Authorization: Bearer sk_live_..."
# 409 Conflict quando tem dependencia:
{ "erro": "Categoria tem 12 produto(s) vinculado(s). Realoque-os antes de remover.",
  "produtosVinculados": 12 }

Upsert de categoria no POST /produtos

Pra evitar 2 chamadas (criar categoria + criar produto), o POST /produtos aceita categoria (string) em vez de categoriaId. Se a categoria já existe (match case-insensitive), reusa; se não, cria. Ordem de prioridade no resolve:

  1. categoriaId explícito (valida pertencimento à loja)
  2. categoria (string) → upsert por nome
  3. Nenhum → cai em "Sem categoria" automática
curl
curl -X POST https://katalog.com.br/api/v1/produtos \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "nome": "X-Bacon", "preco": 28.90, "categoria": "Hambúrgueres" }'

Clientes

GET /clientes

Query params: busca, page, limite.

POST /clientes

Cria ou atualiza (upsert por documento). Campos:

{
  "nome": "João da Silva",
  "documento": "12345678900",
  "tipoDocumento": "CPF",
  "email": "joao@example.com",
  "celular": "11999999999",
  "cep": "01310100",
  "endereco": "Av. Paulista",
  "numero": "1000",
  "bairro": "Bela Vista",
  "cidade": "São Paulo",
  "uf": "SP"
}

Pedidos

GET /pedidos

Query params:

  • status — PENDENTE, CONFIRMADO, PREPARANDO, PRONTO, ENTREGUE, CANCELADO
  • desde — ISO date (filtro de criação >=)
  • ate — ISO date (filtro de criação <=)
  • page, limite
curl
curl "https://katalog.com.br/api/v1/pedidos?status=ENTREGUE&desde=2026-01-01" \ -H "Authorization: Bearer sk_live_..."

POST /pedidos — Criar pedido

Cria um pedido na loja. Preço é recalculado server-side (variação > promo > preço base) — o valor enviado no body é ignorado por segurança. Estoque é baixado em transação atômica: se um item não tem estoque, retorna 400 e nenhum item é baixado.

  • tipoEntregaDELIVERY, BALCAO (retirada no balcão; RETIRADA também é aceito como alias) ou MESA
  • cliente — objeto com snapshot. Se cliente.id for passado, valida que pertence à loja e usa o cadastro como fallback nos campos vazios
  • itens[] — cada um com produtoId, quantidade, opcional variacaoId, observacao e opcoes (JSON de complementos)
  • taxaEntrega, desconto, observacoes — opcionais

Dispara webhook pedido.criado ao sucesso. Não suporta cupons, cashback, mesas, múltiplas formas de pagamento, frete dinâmico, tabela de preço — use o painel pra esses fluxos.

Atenção ao múltiplo de venda: produtos com multiploVenda > 1 (ex: parafuso vendido de 10 em 10) têm a quantidade arredondada automaticamente para o próximo múltiplo. Pedir quantidade: 1 de um produto com multiploVenda: 10 grava 10 no pedido. Cheque esse campo no GET /produtos antes de enviar.

curl
curl -X POST https://katalog.com.br/api/v1/pedidos \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "tipoEntrega": "DELIVERY", "cliente": { "nome": "João da Silva", "celular": "11999999999", "email": "joao@example.com", "documento": "12345678900", "endereco": "Av. Paulista", "numero": "1000", "bairro": "Bela Vista", "cidade": "São Paulo", "uf": "SP", "cep": "01310100" }, "itens": [ { "produtoId": "PROD_ABC", "quantidade": 2 }, { "produtoId": "PROD_XYZ", "variacaoId": "VAR_123", "quantidade": 1, "observacao": "sem cebola" } ], "taxaEntrega": 8.50, "observacoes": "Entregar após 18h" }'

GET /pedidos/[id] — Detalhes

Retorna o pedido completo com itens, cliente e dados de rastreio.

PATCH /pedidos/[id] — Atualizar status / rastreio

Body (todos os campos exceto status são opcionais):

  • statusPENDENTE, CONFIRMADO, PREPARANDO, PRONTO, ENVIADO, ENTREGUE ou CANCELADO
  • motivoCancelamento — obrigatório quando status = CANCELADO
  • tempoEntrega — string livre (ex: "30-45 min")
  • codigoRastreio, urlRastreio — para marketplaces / Correios

Comportamento automático:

  • Ao mudar PENDENTE → CONFIRMADO: baixa estoque dos itens
  • Ao cancelar pedido confirmado: restaura estoque
  • Sempre dispara webhook pedido.status
  • Se status = CANCELADO: também dispara pedido.cancelado
curl
curl -X PATCH https://katalog.com.br/api/v1/pedidos/PEDIDO_ID \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "status": "ENVIADO", "codigoRastreio": "BR123456789", "urlRastreio": "https://rastreamento.correios.com.br/...", "tempoEntrega": "3-5 dias úteis" }'

Estoque

POST /estoque — Movimentação

Registra movimentação de estoque (entrada, saída ou ajuste).

{
  "produtoId": "PRODUTO_ID",
  "tipo": "ENTRADA",        // ou "SAIDA" ou "AJUSTE"
  "quantidade": 50,
  "variacaoId": null,        // opcional
  "observacao": "Compra do fornecedor X"
}
  • ENTRADA: soma à quantidade atual
  • SAIDA: subtrai da quantidade atual
  • AJUSTE: define a quantidade absoluta

Exemplo: importar produtos do ERP

javascript
const TOKEN = "sk_live_..."; const BASE = "https://katalog.com.br/api/v1"; async function criarProduto(produto) { const res = await fetch(`${BASE}/produtos`, { method: "POST", headers: { "Authorization": `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(produto), }); if (!res.ok) { const err = await res.json(); throw new Error(err.erro); } return res.json(); } // Loop sobre os produtos do ERP for (const item of produtosDoERP) { await criarProduto({ nome: item.descricao, preco: item.precoVenda, codigoBarras: item.ean, sku: item.codigo, controlaEstoque: true, quantidadeEstoque: item.estoqueAtual, }); }

Limites

Limite atual: 60 requisições por minuto por token. Em caso de excesso, retornamos 429 Too Many Requests com os headers:

  • X-RateLimit-Limit — limite total (60)
  • X-RateLimit-Remaining — quantas restam na janela
  • X-RateLimit-Reset — segundos até resetar
  • Retry-After — segundos sugeridos antes de tentar de novo

Recursos extras

Webhooks de saída

Receba notificações em uma URL externa quando eventos ocorrerem na sua loja. Pode configurar pelo painel em /painel/webhooks ou via API (abaixo).

Eventos disponíveis

  • pedido.criado — novo pedido recebido
  • pedido.status — status do pedido mudou
  • pedido.pago — pagamento confirmado
  • pedido.cancelado — pedido cancelado
  • cliente.criado — novo cliente cadastrado
  • produto.atualizado — produto foi alterado (PUT)
  • produto.removido — produto foi removido (DELETE)
  • produto.estoque_baixo — produto atingiu o mínimo

POST /webhooks — Cadastrar webhook

Cria um webhook scoped à loja autenticada. O secret é gerado pelo servidor (formato whsec_<48 hex>) e retornado apenas neste response — guarde-o, não há como recuperá-lo depois.

curl
curl -X POST https://katalog.com.br/api/v1/webhooks \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "nome": "Integreon", "url": "https://hub.integreon.com.br/webhooks/meucatalogo", "eventos": ["pedido.criado", "produto.atualizado", "produto.removido"] }'
{
  "id": "...",
  "nome": "Integreon",
  "url": "https://...",
  "eventos": ["pedido.criado", "produto.atualizado", "produto.removido"],
  "ativo": true,
  "secret": "whsec_abc123...",
  "criadoEm": "2026-04-27T..."
}

GET /webhooks — Listar

Retorna os webhooks da loja (sem expor o secret).

DELETE /webhooks/[id] — Remover

curl
curl -X DELETE https://katalog.com.br/api/v1/webhooks/WEBHOOK_ID \ -H "Authorization: Bearer sk_live_..."

Formato do payload

POST com Content-Type application/json e os headers:

X-MeuCatalogo-Event: pedido.criado
X-MeuCatalogo-Signature: sha256=<hex>
Content-Type: application/json

Body:

{
  "evento": "pedido.criado",
  "criadoEm": "2026-04-22T15:30:00Z",
  "dados": {
    "id": "...",
    "numero": 123,
    "total": 89.90,
    "status": "PENDENTE"
  }
}

Validar a assinatura HMAC

Use o secret mostrado no momento da criação do webhook. Calcule HMAC SHA-256 do body com o secret e compare com o header X-MeuCatalogo-Signature.

javascript
import crypto from "crypto"; function verificarAssinatura(secret, body, assinatura) { const hmac = crypto.createHmac("sha256", secret); hmac.update(body); const esperada = "sha256=" + hmac.digest("hex"); return crypto.timingSafeEqual( Buffer.from(esperada), Buffer.from(assinatura) ); }

Retentativas e timeout

Cada entrega tem timeout de 10 segundos. Sua URL deve responder com status 2xx para ser considerada sucesso.

Em caso de falha, retentamos com backoff exponencial: +30s, +2min, +10min, +1h (total de 5 tentativas). O header X-MeuCatalogo-Tentativa indica o número da tentativa.

Logs

Visualize histórico de entregas em /painel/webhooks. Cada log mostra: payload enviado, status HTTP recebido, resposta e duração.

Suporte

Encontrou um bug ou precisa de um endpoint que ainda não existe? Fale com nosso time pelo WhatsApp.