Desenvolvedores
Webhooks
Um webhook é o caminho inverso da chave de API: em vez de o seu sistema perguntar de tempos em tempos se algo mudou, a loja avisa assim que o fato acontece. Cada entrega é um POST assinado com HMAC-SHA256 para o endereço HTTPS cadastrado pelo lojista.
Cadastrando
Quem cadastra é o lojista, no admin da loja: Configurações → Webhooks → Novo webhook. Ele informa a URL, escolhe os eventos e recebe o segredo de assinatura whsec_… uma única vez; combine um canal seguro para recebê-lo. São até 10 endpoints por loja, e o recurso exige um plano com acesso à API.
A URL precisa ser https e resolver para um endereço público: endereços de rede interna são recusados no cadastro e revalidados a cada entrega. Redirecionamentos não são seguidos; aponte direto para o destino final.
O que chega
POST /seu-endpoint HTTP/1.1
Content-Type: application/json
X-MeWeb-Event: OrderPaid
X-MeWeb-Delivery: 01J8ZQ3M4K5N6P7Q8R9S0T1U2V
X-MeWeb-Signature: t=1770000000,v1=5257a869e7ec...
{
"id": "01J8ZQ3M4K5N6P7Q8R9S0T1U2V",
"type": "OrderPaid",
"v": 1,
"occurredAt": "2026-08-08T12:00:00.000Z",
"data": {
"orderId": "01J8ZQ3M4K5N6P7Q8R9S0T1U2V",
"number": 1042,
"customerId": "01J8ZQ3M4K5N6P7Q8R9S0T1U30",
"totalCents": 13300,
"items": [
{ "quantity": 2, "totalCents": 9800 },
{ "quantity": 1, "totalCents": 3500 }
]
}
}O campo data traz os identificadores e os fatos imutáveis do evento, nunca a entidade inteira. Para o detalhe completo, busque o recurso pela API Admin com a sua chave. Assim o payload não envelhece nem vaza mais do que o necessário.
Payload mínimo e dados pessoais
Existem dois modos, escolhidos pelo lojista em cada endpoint:
- Payload mínimo (padrão): o
dataleva apenas identificadores (orderId,customerId,productId,sku, número do pedido), status, valores em centavos, quantidades, datas e código de rastreio. Nome, e-mail, telefone, endereço e qualquer texto livre (inclusive o título dos itens) não são enviados. - Com dados pessoais: o lojista marca “Incluir dados pessoais do cliente no payload” no cadastro do endpoint e passa a receber também esses campos. É opt-in por endpoint, e a responsabilidade pelos dados no destino é dele (LGPD).
"data": {
"orderId": "01J8ZQ3M4K5N6P7Q8R9S0T1U2V",
"number": 1042,
"customerId": "01J8ZQ3M4K5N6P7Q8R9S0T1U30",
"email": "cliente@exemplo.com",
"customerName": "Maria Silva",
"totalCents": 13300,
"items": [
{ "title": "Camiseta P", "quantity": 2, "totalCents": 9800 },
{ "title": "Caneca", "quantity": 1, "totalCents": 3500 }
]
}Escreva o consumidor para o payload mínimo. Ele é o padrão de todo endpoint novo, e é o que você recebe se o lojista desligar a opção depois. Precisa do nome ou do e-mail do comprador? Busque o pedido pela API Admin usando o orderId que chegou; o dado vem sempre atualizado e você não guarda uma cópia que precisa manter em dia.
Trate o campo ausente como normal, não como erro: um campo fora da lista acima simplesmente não aparece no objeto. Endpoints cadastrados antes de 22/08/2026 continuam recebendo o payload completo até o lojista desligar a opção.
Verificando a assinatura
O header X-MeWeb-Signature tem o formato t=<unix>,v1=<hmac>, onde v1 é o HMAC-SHA256 de "<t>.<corpo cru>" usando o segredo do endpoint. Sempre verifique antes de processar: sem isso, qualquer um que descubra a sua URL pode forjar um pedido pago.
import { createHmac, timingSafeEqual } from 'node:crypto';
const TOLERANCE_SECONDS = 300; // 5 minutos
export function verificar(corpoCru, header, segredo) {
const partes = Object.fromEntries(
header.split(',').map((p) => {
const [k, ...resto] = p.trim().split('=');
return [k, resto.join('=')];
}),
);
const t = Number(partes.t);
if (!Number.isFinite(t) || !partes.v1) return false;
// Recusa entregas antigas (anti-replay)
const agora = Math.floor(Date.now() / 1000);
if (Math.abs(agora - t) > TOLERANCE_SECONDS) return false;
const esperado = createHmac('sha256', segredo)
.update(`${t}.${corpoCru}`)
.digest('hex');
const a = Buffer.from(esperado, 'utf8');
const b = Buffer.from(partes.v1, 'utf8');
return a.length === b.length && timingSafeEqual(a, b);
}Recebendo com segurança
import express from 'express';
const app = express();
// IMPORTANTE: a assinatura cobre o corpo CRU. Se o JSON for
// interpretado antes, a re-serialização muda os bytes e a
// verificação falha: use express.raw() nesta rota.
app.post('/meweb', express.raw({ type: 'application/json' }), (req, res) => {
const corpoCru = req.body.toString('utf8');
if (!verificar(corpoCru, req.get('X-MeWeb-Signature'), process.env.MEWEB_WEBHOOK_SECRET)) {
return res.sendStatus(401);
}
const evento = JSON.parse(corpoCru);
// Deduplicação: a mesma entrega pode chegar mais de uma vez.
if (jaProcessamos(req.get('X-MeWeb-Delivery'))) return res.sendStatus(200);
// Responda 2xx RÁPIDO e processe depois: o MeWeb desiste em 10 s.
enfileirar(evento);
res.sendStatus(200);
});Novas tentativas e duplicatas
- Sucesso é qualquer 2xx, respondido em até 10 segundos. Responda primeiro e processe depois: trabalho pesado dentro da requisição vira timeout, e timeout vira nova tentativa.
- Falhou? São até 5 tentativas, esperando 1 min, 5 min, 30 min e 2 h entre elas. Depois disso a entrega fica marcada como falha e o lojista pode reenviá-la à mão pelo painel.
- Entrega ao menos uma vez: a mesma ocorrência pode chegar em duplicidade (uma resposta perdida no caminho, por exemplo). Guarde o
X-MeWeb-Deliveryjá processado e ignore repetições. Um reenvio manual chega com um id novo; deduplique também pelo conteúdo quando a operação não for idempotente. - Sem ordem garantida: use o campo
occurredAtpara ordenar, nunca a ordem de chegada. - 20 falhas seguidas desativam o endpoint e avisam o lojista por e-mail. Enquanto estiver desativado, nenhum evento é entregue, nem retroativamente ao reativar.
Trocando o segredo
O lojista pode gerar um segredo novo a qualquer momento. A troca vale na hora: o segredo anterior para de assinar imediatamente, então atualize o seu lado antes, ou as entregas seguintes serão recusadas pela sua própria verificação.
Testando
O botão Enviar teste no painel dispara um evento ping e mostra na hora o código que o seu endereço respondeu: é a forma mais rápida de conferir a assinatura e o roteamento antes de depender de um pedido de verdade. O painel também lista todas as entregas dos últimos 30 dias com status, tentativas e o código devolvido.