Visão geral
A loja pode falar com outros sistemas de duas formas: receber um aviso quando algo acontece (webhooks) ou consultar os pedidos e o catálogo quando quiser (API de leitura). As duas são do plano Pro e se configuram em Gestão › Integrações, no painel. Só o dono da loja vê essa tela.
- Webhooks: o Catálogo Super envia um POST assinado ao endereço que você cadastrar a cada pedido novo, mudança de status e pagamento recebido.
- API de leitura: com uma chave, você lista e busca pedidos e lista o catálogo. Não é possível criar nem alterar nada por ela.
- Valores em centavos, datas em ISO 8601 (UTC) e moeda sempre BRL.
Os dados do pedido incluem nome, telefone e endereço do cliente. Guarde-os só onde a sua loja já trata dados de clientes, conforme a Política de Privacidade.
Chaves de API e autenticação
Crie a chave em Gestão › Integrações › Chaves de API. Ela aparece inteira uma única vez, na criação: copie e guarde. Depois disso o painel mostra só o começo (cs_live_ e mais alguns caracteres). Perdeu? Revogue e crie outra.
Envie a chave no cabeçalho Authorization de toda requisição:
curl "https://app.catalogosuper.com.br/api/v1/orders?status=placed&limit=20" \
-H "Authorization: Bearer cs_live_SUA_CHAVE"| Escopo | O que libera |
|---|---|
| orders:read | Listar e buscar pedidos. |
| catalog:read | Listar categorias e produtos. |
- Cada chave pertence a uma loja e só enxerga os dados dela.
- Até 10 chaves por loja. Revogar uma chave a derruba na hora.
- A chave é guardada só como resumo (hash): nem a equipe consegue ler a sua de volta.
- Chave em repositório público, mensagem ou código de aplicativo vaza. Se acontecer, revogue.
Endpoints
Endereço base: https://app.catalogosuper.com.br. Todos os métodos abaixo são GET.
| Rota | Escopo | O que devolve |
|---|---|---|
| /api/v1/orders | orders:read | Pedidos, do mais novo ao mais antigo. |
| /api/v1/orders/{id} | orders:read | Um pedido, ou 404 se não existir na sua loja. |
| /api/v1/products | catalog:read | Categorias e produtos da loja. |
Parâmetros de /api/v1/orders
| Parâmetro | Padrão | Descrição |
|---|---|---|
| limit | 50 | Quantos pedidos vir, de 1 a 100. |
| status | todos | Filtra por um status: draft, pending_payment, placed, accepted, preparing, ready, out_for_delivery, completed ou cancelled. |
| updatedSince | nenhum | Data ISO 8601. Devolve só pedidos alterados desde então: use para sincronizar sem baixar tudo de novo. |
A resposta traz hasMore: true quando existem mais pedidos do que o limite. Com updatedSince a ordem muda: do mais antigo ao mais novo pela data de alteração. Para continuar, chame de novo com o updatedSince do updatedAt do último pedido recebido e ignore os que você já tem (o filtro inclui o próprio instante). Sem updatedSince, vêm só os mais novos, até o limite; para baixar o histórico todo, comece por updatedSince=2000-01-01T00:00:00Z.
{
"data": [ { "id": "ORD-8F3K2Q", "status": "placed", "total": 10100 } ],
"hasMore": false
}O pedido
| Campo | Valores |
|---|---|
| channel | storefront (vitrine) ou pos (balcão). |
| fulfillmentType | pickup (retirada), delivery (entrega) ou counter (balcão). |
| paymentMethod | pix, cash, debit, credit ou stripe. |
| paymentStatus | pending, approved, rejected, refunded, cancelled ou failed. |
| status | draft, pending_payment, placed, accepted, preparing, ready, out_for_delivery, completed ou cancelled. |
É o mesmo objeto na API e nos webhooks. Dados internos, como o código de acompanhamento do cliente, nunca saem. Os exemplos desta página usam valores inventados.
{
"id": "ORD-8F3K2Q",
"status": "placed",
"paymentStatus": "pending",
"paymentMethod": "pix",
"channel": "storefront",
"fulfillmentType": "delivery",
"items": [
{
"productId": "brigadeiro-gourmet",
"name": "Brigadeiro gourmet",
"variant": "Caixa com 12",
"quantity": 2,
"unitPrice": 4800,
"subtotal": 9600,
"modifiers": []
}
],
"subtotal": 9600,
"deliveryFee": 500,
"discount": 0,
"total": 10100,
"currency": "BRL",
"coupon": null,
"scheduledTime": null,
"tableNumber": null,
"notes": null,
"customer": { "name": "Ana Souza", "phone": "11999990000", "address": "Rua das Flores, 10" },
"createdAt": "2026-10-27T14:03:11.000Z",
"updatedAt": "2026-10-27T14:03:11.000Z"
}O catálogo (/api/v1/products) devolve categories (id, name, active) e data com os produtos (id, categoryId, name, description, price em centavos, active, stock e image).
Webhooks
Cadastre um endereço em Gestão › Integrações › Webhooks e escolha os eventos. Cada loja pode ter até 5 endereços. O endereço precisa ser https, na porta 443, sem usuário e senha na URL e com um nome de domínio público (IP e nomes internos são recusados). O Catálogo Super não segue redirecionamentos e espera até 5 segundos pela resposta.
| Evento | Quando acontece |
|---|---|
| order.placed | Um pedido novo entrou, pelo catálogo ou pelo balcão. |
| order.status_changed | O status do pedido mudou (aceito, em preparo, pronto, concluído, cancelado...). |
| payment.received | O pagamento do pedido foi confirmado. |
O botão Enviar teste manda um evento ping ao endereço, com a mesma assinatura, para você conferir a configuração sem esperar um pedido real. Só eventos criados depois do cadastro do endereço são enviados.
POST /seu-endereco HTTP/1.1
Content-Type: application/json
User-Agent: CatalogoSuper-Webhooks/1
Catalogo-Signature: t=1793110991,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Catalogo-Event-Id: evt_3b1f0c7a9d2e4f60a1b2c3d4
Catalogo-Event-Type: order.placed
Catalogo-Delivery-Attempt: 1
{
"id": "evt_3b1f0c7a9d2e4f60a1b2c3d4",
"type": "order.placed",
"createdAt": "2026-10-27T14:03:11.000Z",
"data": { "order": { "id": "ORD-8F3K2Q", "status": "placed", "total": 10100 } }
}- id: identificador do evento, o mesmo em todas as tentativas. Use-o para ignorar repetições.
- type e createdAt: o evento e o momento em que ele aconteceu.
- data.order: o pedido completo, no formato da API. Em order.status_changed e payment.received, data também traz status e paymentStatus.
Conferir a assinatura
Qualquer um pode enviar um POST ao seu endereço. Antes de confiar no conteúdo, confira o cabeçalho Catalogo-Signature, no formato t=<segundos>,v1=<assinatura>. A assinatura é um HMAC-SHA256, em hexadecimal, do texto "<t>.<corpo cru>", com o segredo do endereço.
- Leia o corpo exatamente como chegou, sem converter o JSON antes.
- Monte o texto t + "." + corpo e calcule o HMAC-SHA256 com o segredo do endereço.
- Compare com o v1 em tempo constante (timingSafeEqual ou equivalente).
- Recuse se t estiver a mais de 5 minutos do seu relógio: protege contra reenvio de uma entrega antiga.
import { createHmac, timingSafeEqual } from 'node:crypto';
const TOLERANCIA_SEGUNDOS = 300;
// `corpo` é o texto cru da requisição, antes de qualquer JSON.parse.
export function assinaturaValida(corpo, cabecalho, segredo) {
const partes = Object.fromEntries(cabecalho.split(',').map((parte) => parte.split('=')));
const t = Number(partes.t);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > TOLERANCIA_SEGUNDOS) return false;
const esperado = createHmac('sha256', segredo).update(`${t}.${corpo}`).digest('hex');
const recebido = Buffer.from(partes.v1 ?? '', 'hex');
const calculado = Buffer.from(esperado, 'hex');
return recebido.length === calculado.length && timingSafeEqual(recebido, calculado);
}O segredo aparece uma única vez, ao cadastrar o endereço. Guarde-o. Em Gerar novo segredo, o painel mostra o novo, também uma única vez, e o antigo para de valer na hora: atualize o seu sistema antes da próxima entrega.
Respostas, tentativas e limites
Responda com qualquer status 2xx em até 5 segundos. Qualquer outra resposta, ou a falta dela, conta como falha e a entrega é repetida, com espera crescente.
| Tentativa | Espera mínima |
|---|---|
| 1 | Sem espera: sai logo depois do evento. |
| 2 | 1 minuto depois da falha anterior. |
| 3 | 5 minutos depois. |
| 4 | 30 minutos depois. |
| 5 | 2 horas depois. |
| 6 | 6 horas depois. Se falhar, a entrega expira. |
São esperas mínimas, não horários. A repetição sai na próxima passada da fila: quando a loja recebe outro pedido ou na passada diária da madrugada. Numa loja com pouco movimento, uma entrega com falha pode levar até um dia para ser repetida.
- Depois de 20 falhas seguidas o endereço é desativado, e o painel mostra o motivo. Corrija e reative.
- A mesma entrega pode chegar mais de uma vez. Trate o id do evento como chave de idempotência.
- A ordem de chegada não é garantida, e data.order sempre mostra o pedido como ele está na hora do envio, não na hora do evento. Use data.order.updatedAt para saber qual versão é a mais nova, ou consulte o pedido na API.
- A API de leitura aceita 120 requisições por minuto por chave. Acima disso responde 429; espere e tente de novo.
- Perdeu eventos? Use /api/v1/orders com updatedSince para recuperar o que mudou.
Erros
Os erros vêm em JSON, com uma mensagem em português (error) e um código estável (code) para o seu programa tratar.
{
"error": "Esta chave não tem o escopo orders:read.",
"code": "insufficient_scope"
}| Status | code | Significado |
|---|---|---|
| 400 | invalid_parameter | Um parâmetro está fora do formato ou do intervalo. |
| 401 | invalid_api_key | Chave ausente, errada ou revogada. |
| 403 | insufficient_scope | A chave não tem o escopo da rota. |
| 403 | pro_required | A loja não está no plano Pro. |
| 404 | not_found | O pedido não existe nesta loja. |
| 429 | rate_limited | Requisições demais, por chave (120 por minuto) ou por endereço de IP (300 por minuto). Espere um pouco. |
| 503 | not_ready | A API ainda não está disponível neste ambiente. Tente de novo mais tarde. |
Precisa de algo que a API ainda não faz, como criar pedidos ou novos eventos? Conte para a equipe em Ajuda e contato.