MeuPedido/developer
Pedidos

Webhooks

Entrega por webhook: envelope idêntico ao polling, cabeçalhos X-MeuPedido-*, verificação da assinatura HMAC, retentativas e desativação.

Extensão MeuPedido

Com o modo de entrega WEBHOOK ou BOTH, o MeuPedido faz um POST na sua URL a cada evento, em vez de esperar a sua consulta. O corpo é o mesmo envelope que o polling devolve, byte a byte: um único desserializador atende os dois modos.

Webhook é uma extensão MeuPedido. Não é o /v1/newEvent descrito no padrão Open Delivery; a diferença está em Compatibilidade.

Fluxo de entrega por webhookO MeuPedido faz um POST na sua URL HTTPS com o envelope do evento e os cabeçalhos X-MeuPedido-Signature, X-MeuPedido-Timestamp, X-MeuPedido-Event-Id e X-MeuPedido-Event-Type. Se o seu servidor responder 2xx em até 10 segundos, o evento é entregue. Se não, o MeuPedido tenta de novo em 5, 10 e 20 segundos; após a quarta falha o evento fica como WEBHOOK_FAILED e disponível no polling. Nos dois casos é obrigatório confirmar o evento em POST /v1/events/acknowledgment.MeuPedidoPOST {sua URL HTTPS}X-MeuPedido-Signature: hmac-sha256 em hexX-MeuPedido-Timestamp: 1758290531X-MeuPedido-Event-Id · X-MeuPedido-Event-TypeContent-Type: application/jsoncorpo: o envelope do evento, idêntico ao do pollingSeu servidor2xx em até 10 s?simEntreguea resposta é ignoradanãotenta de novoem 5 s, 10 s e 20 s4ª falha, cerca de 35 s depoisWEBHOOK_FAILEDfica disponível no polling20 falhas seguidasdesativam a URL.Reative em Limpar fila.Confirme o evento, mesmo quando chega por webhookPOST /v1/events/acknowledgment [{ "id": eventId }]Entregue não é confirmado.Sem ack, o evento continua no polling e nunca é limpo.
  • Envio do MeuPedido
  • Retentativa: um POST novo na mesma URL em 5 s, 10 s e 20 s
  • Entregue: qualquer 2xx em até 10 s
  • WEBHOOK_FAILED: o evento continua no polling, nada se perde
  • Obrigatório nos dois caminhos: confirmar o evento

Configuração

A URL é configurada pelo lojista (ou por quem tem acesso ao painel dele) em Integrações > Open Delivery, ao criar ou editar a credencial:

  • Modo de entrega WEBHOOK (só push) ou BOTH (push e polling).
  • URL obrigatoriamente HTTPS.
  • Segredo do webhook, gerado pelo MeuPedido e mostrado uma única vez ao salvar a URL. Guarde-o no seu cofre de segredos. Salvar a URL de novo rotaciona o segredo, e o anterior deixa de valer.

Não há como recuperar um segredo perdido: rotacione e atualize o seu servidor.

A requisição

POST {sua URL}
Content-Type: application/json
X-MeuPedido-Signature: 5f1d6a9c2b3e4f7a8d9c0b1e2f3a4d5c6b7a8f9e0d1c2b3a4f5e6d7c8b9a0f1e
X-MeuPedido-Timestamp: 1758290531
X-MeuPedido-Event-Id: e1b9c0d2-4a5b-4c6d-8e7f-9a0b1c2d3e4f
X-MeuPedido-Event-Type: CREATED
Corpo
{
  "eventId": "e1b9c0d2-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
  "eventType": "CREATED",
  "orderId": "3f8a1c2d-5b6e-4f7a-9c8d-1e2f3a4b5c6d",
  "orderURL": "https://api.meupedido.io/open-delivery/v1/orders/3f8a1c2d-5b6e-4f7a-9c8d-1e2f3a4b5c6d",
  "createdAt": "2026-09-19T14:02:11Z"
}
CabeçalhoConteúdo
X-MeuPedido-SignatureHMAC-SHA256 em hexadecimal minúsculo, calculado com o segredo sobre a string {timestamp}.{body}.
X-MeuPedido-TimestampInstante do envio, em segundos Unix. Entra no cálculo da assinatura.
X-MeuPedido-Event-IdO mesmo eventId do corpo, para roteamento e deduplicação sem desserializar.
X-MeuPedido-Event-TypeO mesmo eventType do corpo.

Um envio por evento. O corpo nunca traz o pedido: busque em orderURL, como no polling.

Responder

Responda qualquer 2xx em até 10 segundos. Isso é tudo que o MeuPedido espera; o corpo da resposta é ignorado.

Faça o mínimo dentro desses 10 segundos: verifique a assinatura, enfileire o evento e responda. Buscar o pedido, gravar no banco e imprimir ficam para depois da resposta. Um 2xx tardio conta como timeout.

Entregue não é confirmado

O 2xx diz ao MeuPedido que a requisição chegou. Ele não tira o evento do feed. É obrigatório chamar POST /v1/events/acknowledgment com o eventId depois de processar, mesmo recebendo por webhook. Sem isso o evento continua no polling e nunca é limpo.

Verificar a assinatura

Toda requisição deve ser verificada antes de qualquer processamento. A verificação tem duas partes:

  1. Tempo. Rejeite se X-MeuPedido-Timestamp estiver a mais de 5 minutos do relógio do seu servidor, para frente ou para trás. Isso impede o reenvio de uma requisição capturada.
  2. Assinatura. Calcule HMAC-SHA256(secret, "{timestamp}.{body}"), converta para hexadecimal minúsculo e compare com X-MeuPedido-Signature em tempo constante.

body é o corpo bruto da requisição, exatamente como chegou. Não desserialize e serialize de novo antes de calcular: qualquer mudança de espaço ou ordem de chaves invalida a assinatura.

As funções abaixo são puras: recebem o segredo, o timestamp, o corpo bruto e a assinatura, e devolvem true ou false. O parâmetro now é opcional e existe para testes; em produção, deixe o padrão.

verify-signature.js
import { createHmac, timingSafeEqual } from 'node:crypto';

const TOLERANCE_SECONDS = 5 * 60;

export function verifySignature(secret, timestamp, body, signature, now = Math.floor(Date.now() / 1000)) {
  const sent = Number(timestamp);
  if (!Number.isInteger(sent) || Math.abs(now - sent) > TOLERANCE_SECONDS) return false;

  const expected = createHmac('sha256', secret).update(`${timestamp}.${body}`).digest('hex');
  const received = String(signature ?? '').toLowerCase();

  const a = Buffer.from(expected, 'utf8');
  const b = Buffer.from(received, 'utf8');
  return a.length === b.length && timingSafeEqual(a, b);
}

Um endpoint completo

import express from 'express';
import { verifySignature } from './verify-signature.js';

const app = express();

// Corpo bruto: a assinatura é calculada sobre os bytes originais.
app.post('/webhooks/meupedido', express.raw({ type: 'application/json' }), async (req, res) => {
  const body = req.body.toString('utf8');
  const ok = verifySignature(
    process.env.MEUPEDIDO_WEBHOOK_SECRET,
    req.get('X-MeuPedido-Timestamp'),
    body,
    req.get('X-MeuPedido-Signature'),
  );
  if (!ok) return res.status(401).end();

  await queue.push(JSON.parse(body)); // processa fora da requisição
  res.status(204).end();
});

Retentativas e falha

Uma tentativa falha quando a sua URL responde algo fora de 2xx, recusa a conexão ou não responde em 10 segundos. Nesse caso o MeuPedido tenta de novo com intervalos de 5, 10 e 20 segundos: são 4 tentativas em cerca de 35 segundos.

tentativa 1   t = 0 s
tentativa 2   t = 5 s
tentativa 3   t = 15 s
tentativa 4   t = 35 s

Esgotadas as tentativas, o evento é marcado como WEBHOOK_FAILED e continua disponível no polling, de onde você o recupera na próxima consulta. Nada é perdido por causa de uma queda da sua URL.

O lojista vê cada tentativa, o status e o motivo em Integrações > Open Delivery, e pode reenviar um evento manualmente.

Desativação da URL

Após 20 falhas consecutivas, a URL é desativada e o MeuPedido para de enviar. Os eventos continuam sendo gerados normalmente e ficam disponíveis no polling. Quando o seu servidor voltar, o lojista reativa a entrega em Limpar fila, no painel.

Enquanto a URL estiver desativada, o polling é o único caminho. Uma integração resiliente em modo BOTH não percebe a desativação: o loop de polling continua trazendo tudo.

Recomendações

  • Idempotência pelo eventId. Retentativa e modo BOTH fazem o mesmo evento chegar mais de uma vez. Use o eventId como chave única, exatamente como no polling.
  • Responda antes de processar. Enfileire e devolva 2xx; o processamento pesado fica para um worker.
  • Trate o webhook como um aviso, não como a fonte da verdade. Busque o pedido em orderURL. Se um webhook falhar por completo, o polling entrega o mesmo evento.
  • Restrinja o segredo. Ele vive só no servidor que recebe o webhook. Nunca em cliente, repositório ou log.
  • Rotacione ao suspeitar de vazamento. Salvar a URL de novo no painel gera um segredo novo; atualize o servidor no mesmo momento, porque o anterior deixa de valer imediatamente.

Próximos passos

  • Polling: a confirmação obrigatória e o loop que recupera eventos com falha de webhook.
  • Catálogo de eventos: todos os eventType que a sua URL pode receber.
  • Obter credenciais: onde a URL e o modo de entrega são configurados.

Nesta página