Documentação da API Paylog Documentação da API Paylog API pública v1
Webhook

Webhooks v2

Receba eventos assinados de pedidos e rastreamento com controle de entrega.

Nesta página 19

Webhooks v2 entregam eventos de pedido e rastreamento em endpoints configurados em Ferramentas → Integrações → Webhooks. Cada endpoint pertence ao produtor autenticado e assina os eventos escolhidos no painel.

Observação

O painel não oferece filtro de produtos por endpoint: todo endpoint ativo recebe os eventos assinados de todos os pedidos do produtor. O filtro por produto existe apenas no formulário de reenvio em lote, descrito no final desta página.

Requisição HTTP

Cada evento é enviado por POST, com JSON e timeout de resposta de 30 segundos.

Header Presença Valor
Content-Type Sempre application/json
X-Webhook-Event Sempre Código exato do evento, por exemplo shipment.delivered.
X-Webhook-Signature Opcional sha256=<hex>; omitido somente quando o endpoint não possui segredo. Endpoints criados pelo painel recebem um segredo.
X-Webhook-Test Condicional: somente teste do painel true

Eventos disponíveis

Pedido

Evento Rótulo Disparo real
order.created Pedido Criado Após a criação do pedido, exceto filho agregado/upsell com status Agregado.
order.label_ready Etiqueta Pronta Ao receber RastreioRecebido. Reemissões do mesmo pedido são deduplicadas por 7 dias.
order.preparing Em Preparação Quando status_code_id muda para Preparação.
order.blocked Parado Quando status_code_id muda para Parado.
order.refunded Estornado Quando status_code_id muda para Estornado.
order.canceled Cancelado Quando status_code_id muda para Cancelado.
Observação

order.label_ready usa o evento interno RastreioRecebido. O recebimento específico de rastreio de marketplace (RastreioMarketplaceRecebido) continua atendendo o webhook v1, mas não dispara esse evento v2.

Rastreamento

Evento Rótulo Status interno que dispara
shipment.posted Objeto Postado POSTED
shipment.in_transit Em Trânsito IN_TRANSIT
shipment.out_for_delivery Saiu para Entrega OUT_FOR_DELIVERY ou ON_ROUTE
shipment.awaiting_pickup Aguardando Retirada AWAITING_PICKUP
shipment.returning Em Devolução RETURNING
shipment.delivered Entregue DELIVERED
shipment.delivery_proof Comprovante de Entrega Quando ProofOfDeliveryReceived é emitido.
shipment.returned Devolvido RETURNED
Importante

Os status de rastreamento WAITING_POSTING, RECIPIENT_ABSENT, DELIVERY_PROBLEM, LOST e CANCELLED não possuem evento v2. O listener os ignora.

Versão do schema

meta.api_version identifica a forma do corpo e só muda quando o schema muda. A versão atual é 2.

Versão Mudanças
2 Adicionados shipment.tracking_url, delivery_proof.delivery_lat e delivery_proof.delivery_lng. Removido shipment.carrier.id — era a chave primária interna da tabela de transportadoras do Paylog, sem significado fora do nosso banco (a API v1 nunca a expôs). Identifique a transportadora por carrier.code.
1 Versão inicial.
Atenção

carrier.id não é mais enviado. Se o seu sistema faz o de‑para por esse número, troque para carrier.code (por exemplo correios, jt-express) antes de atualizar. carrier.code é gerado a partir do nome atual da transportadora e não é um enum fixo — trate valores desconhecidos sem quebrar.

Full-schema e nulabilidade

O corpo possui schema completo: as chaves documentadas existem em todos os eventos. Dados indisponíveis são null, objetos conhecidos mantêm sua forma e items pode ser uma lista vazia. Valores monetários *_cents são inteiros em centavos.

Nesta página:

  • Sempre: a chave existe em todo payload; a coluna de nulabilidade informa se o valor pode ser null.
  • Condicional: a chave existe, mas só recebe objeto ou dado quando a condição é satisfeita; caso contrário vale null.
  • Opcional: a chave ou header pode ser omitido. No corpo v2, nenhuma chave do schema é opcional.

Campos raiz

Campo Tipo Presença Nulabilidade e semântica
event string Sempre Nunca null. Código do evento assinado pelo endpoint.
event_label string Sempre Nunca null. Rótulo em português do evento.
event_timestamp string RFC 3339 Sempre Nunca null. Momento em que o payload foi construído.
idempotency_key string UUID Sempre Nunca null. Identificador para deduplicação; permanece no payload durante o reenvio da mesma entrega.
cancelable booleano Sempre Nunca null. Calculado a partir do status atual do pedido.
order objeto Sempre Nunca null. Dados do pedido.
shipment objeto Sempre Nunca null; quando não há rastreio, mantém o objeto com valores null.
meta objeto Sempre Nunca null; contém webhook_version: 2 e api_version: 2.

order

Campo Tipo Presença Nulabilidade e semântica
pedido_id inteiro Sempre Nunca null. ID interno do pedido.
codigo_transacao string ou null Sempre Pode ser null. Código na plataforma de origem.
referencia_externa string ou null Sempre Pode ser null. Referência fornecida na criação.
plataforma string Sempre Nunca null; usa Desconhecida quando não há nome mapeado.
status.code inteiro ou null Sempre Pode ser null. Código do status atual do pedido.
status.label string ou null Sempre Pode ser null. Rótulo do status atual.
motivo_parado objeto ou null Condicional {id: int, label: string} somente quando o status atual é Parado e existe motivo_parado_id; caso contrário null.
customer.nome string ou null Sempre Pode ser null.
customer.cpf string ou null Sempre Pode ser null; pode conter CPF ou CNPJ conforme o cadastro.
customer.email string ou null Sempre Pode ser null.
customer.telefone string ou null Sempre Celular; quando ausente, usa telefone.
customer.endereco.logradouro string ou null Sempre Pode ser null.
customer.endereco.numero string ou null Sempre Pode ser null.
customer.endereco.complemento string ou null Sempre Pode ser null.
customer.endereco.bairro string ou null Sempre Pode ser null.
customer.endereco.cidade string ou null Sempre Pode ser null.
customer.endereco.uf string ou null Sempre Pode ser null.
customer.endereco.cep string ou null Sempre Pode ser null.
customer.endereco.pais string Sempre Nunca null; usa BR quando não informado.
items array Sempre Pode ser []. Um elemento é emitido por item do pedido.
items[].descricao string ou null Sempre por item Descrição do item; usa o nome do produto como fallback.
items[].quantidade inteiro Sempre por item Conversão inteira da quantidade.
items[].valor_unitario_cents inteiro ou null Sempre por item Valor unitário em centavos.
items[].produto.id inteiro ou null Sempre por item Pode ser null para item sem produto associado.
items[].produto.nome string ou null Sempre por item Pode ser null.
frete_cents inteiro ou null Sempre Frete em centavos.

shipment

Campo Tipo Presença Nulabilidade e semântica
tracking_code string ou null Sempre Pode ser null.
tracking_url string ou null Sempre Página pública de rastreamento da Paylog (/rastreio/{codigo}) — funciona com qualquer transportadora e mostra a linha do tempo junto com as imagens do comprovante. null enquanto não houver código.
carrier.code string ou null Sempre Slug gerado do nome atual da transportadora; não é enum fixo.
carrier.name string ou null Sempre Nome atual da transportadora.
tracking_status.code inteiro ou null Sempre Código do evento de rastreamento usado no payload ou status atual do rastreio.
tracking_status.label string ou null Sempre Rótulo correspondente ao status.
description string ou null Sempre Descrição do evento de rastreamento; pode ser null, inclusive em eventos sem tracking_event.
location string ou null Sempre cidade/UF, apenas cidade ou apenas UF; null sem localização.
occurred_at string RFC 3339 ou null Sempre Horário do evento da transportadora, diferente de event_timestamp.
collection_address objeto ou null Condicional Preenchido somente em AWAITING_PICKUP quando ao menos um campo do ponto de retirada existe.
delivery_proof objeto Sempre Nunca null; todos os seus campos podem ser null.
delivery_proof.image_url string ou null Sempre URL da foto da entrega ou do recibo assinado.
delivery_proof.signature_url string ou null Sempre URL da imagem da assinatura eletrônica.
delivery_proof.received_by string ou null Sempre Identificação textual de quem recebeu.
delivery_proof.signatory string ou null Sempre Nome do signatário.
delivery_proof.delivery_lat string ou null Sempre Latitude da entrega, no formato da transportadora: string com vírgula decimal, por exemplo "-25,4338109".
delivery_proof.delivery_lng string ou null Sempre Longitude da entrega, mesmo formato.

Quando preenchido, collection_address contém sempre estas nove chaves, cada uma podendo ser null:

Campo Formato
unit_type string ou null
logradouro string ou null
numero string ou null
bairro string ou null
cidade string ou null
uf string ou null
cep string ou null
pickup_deadline data YYYY-MM-DD ou null
pickup_list_code string ou null

Exemplo completo: shipment.awaiting_pickup

json
{
  "event": "shipment.awaiting_pickup",
  "event_label": "Aguardando Retirada",
  "event_timestamp": "2026-07-22T10:15:30-03:00",
  "idempotency_key": "d9428888-122b-11e1-b85c-61cd3cbb3210",
  "cancelable": false,
  "order": {
    "pedido_id": 12345,
    "codigo_transacao": "ABC123",
    "referencia_externa": "pedido-987",
    "plataforma": "Braip",
    "status": {
      "code": 18,
      "label": "Aguardando Retirada"
    },
    "motivo_parado": null,
    "customer": {
      "nome": "João da Silva",
      "cpf": "123.456.789-00",
      "email": "[email protected]",
      "telefone": "(11) 99999-9999",
      "endereco": {
        "logradouro": "Rua Exemplo",
        "numero": "123",
        "complemento": null,
        "bairro": "Centro",
        "cidade": "São Paulo",
        "uf": "SP",
        "cep": "01000-000",
        "pais": "BR"
      }
    },
    "items": [
      {
        "descricao": "Produto Exemplo",
        "quantidade": 1,
        "valor_unitario_cents": 16785,
        "produto": {
          "id": 42,
          "nome": "Produto Exemplo"
        }
      }
    ],
    "frete_cents": 1895
  },
  "shipment": {
    "tracking_code": "AA123456789BR",
    "tracking_url": "https://ev.paylog.cash/rastreio/AA123456789BR",
    "carrier": {
      "code": "correios",
      "name": "CORREIOS"
    },
    "tracking_status": {
      "code": 60,
      "label": "Aguardando Retirada"
    },
    "description": "Objeto aguardando retirada no endereço indicado",
    "location": "São Paulo/SP",
    "occurred_at": "2026-07-22T10:10:00-03:00",
    "collection_address": {
      "unit_type": "Agência dos Correios",
      "logradouro": "Avenida Central",
      "numero": "100",
      "bairro": "Centro",
      "cidade": "São Paulo",
      "uf": "SP",
      "cep": "01000-000",
      "pickup_deadline": "2026-07-29",
      "pickup_list_code": "408161154094"
    },
    "delivery_proof": {
      "image_url": null,
      "signature_url": null,
      "received_by": null,
      "signatory": null,
      "delivery_lat": null,
      "delivery_lng": null
    }
  },
  "meta": {
    "webhook_version": 2,
    "api_version": 2
  }
}

Quando ainda não existe rastreio, o objeto shipment mantém esta forma:

json
{
  "tracking_code": null,
  "tracking_url": null,
  "carrier": {
    "code": null,
    "name": null
  },
  "tracking_status": {
    "code": null,
    "label": null
  },
  "description": null,
  "location": null,
  "occurred_at": null,
  "collection_address": null,
  "delivery_proof": {
    "image_url": null,
    "signature_url": null,
    "received_by": null,
    "signatory": null,
    "delivery_lat": null,
    "delivery_lng": null
  }
}

Campo cancelable

cancelable não é inferido pelo nome do evento. Ele é true somente quando o status atual do pedido está nesta lista:

Código Status
1 Pendente
2 Aguardando Faturamento
3 Faturado
4 Preparação
10 Pago - Aguardando NF/DC
8 Parado
11 Correção
13 Falha no Faturamento
17 Etiqueta Pronta
16 Aguardando Transportadora

Em qualquer outro status, o valor é false.

Verificação HMAC-SHA256

O painel gera um segredo de 40 caracteres ao criar o endpoint e o exibe uma única vez. A assinatura é:

text
sha256=<HMAC-SHA256 hexadecimal do corpo JSON bruto usando o segredo>
Atenção

Verifique os bytes exatos do corpo recebido. Fazer parse do JSON e serializá-lo novamente altera espaços, escapes ou ordem das chaves e invalida a assinatura.

PHP

php
$rawBody = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);

$valid = hash_equals($expected, $received);

Node.js

javascript
const crypto = require('node:crypto');

const received = req.header('X-Webhook-Signature') ?? '';
const expected = 'sha256=' + crypto
  .createHmac('sha256', secret)
  .update(rawBody)
  .digest('hex');

const receivedBuffer = Buffer.from(received);
const expectedBuffer = Buffer.from(expected);
const valid = receivedBuffer.length === expectedBuffer.length
  && crypto.timingSafeEqual(receivedBuffer, expectedBuffer);

Python

python
import hashlib
import hmac

received = request.headers.get("X-Webhook-Signature", "")
expected = "sha256=" + hmac.new(
    secret.encode(),
    request.get_data(),
    hashlib.sha256,
).hexdigest()

valid = hmac.compare_digest(expected, received)

Tentativas, logs e pausa automática

Uma resposta não-2xx ou um timeout/erro de conexão falha a tentativa. O job permite quatro tentativas totais:

Tentativa Momento Atraso após a falha anterior
1 Imediata —
2 Reenvio 10 segundos
3 Reenvio 60 segundos
4 Reenvio final 300 segundos
Observação

O array de backoff contém também 900, mas tries = 4; portanto não existe uma quinta tentativa e o atraso de 900 segundos não é alcançado pela configuração atual.

Cada tentativa cria um registro no histórico com evento, payload, número da tentativa, resultado, status HTTP e até 5.000 caracteres do corpo da resposta. Uma resposta 2xx zera consecutive_failures.

Pausa automática conta falhas terminais, não requisições individuais:

  1. Um ciclo de entrega só é terminal depois de esgotar suas até quatro tentativas.
  2. Ao terminar sem sucesso, o endpoint recebe uma falha consecutiva.
  3. Após três ciclos terminais consecutivos, o endpoint é pausado (is_active = false).
  4. Reativar no painel limpa o contador, remove a pausa e ativa o endpoint.

Isso representa até 12 requisições HTTP malsucedidas em três ciclos, se todas as quatro tentativas forem executadas em cada ciclo. Um sucesso entre ciclos reinicia a contagem.

Teste pelo painel

O teste usa o primeiro evento selecionado no endpoint (ou order.created como fallback), gera o mesmo full-schema com dados sintéticos e assina o corpo com o mesmo algoritmo.

Diferenças do teste:

  • envia X-Webhook-Test: true;
  • usa timeout de 10 segundos;
  • faz uma única chamada direta, sem fila ou reenvio;
  • não cria registro no histórico de entregas;
  • não altera o contador de falhas nem pausa o endpoint;
  • considera qualquer resposta 2xx bem-sucedida.

Resposta e idempotência

Retorne qualquer status 2xx em até 30 segundos. Respostas 3xx, 4xx, 5xx, timeout e erros de conexão entram na política de tentativas.

Persista idempotency_key e ignore uma chave já processada. Isso torna seguro receber novamente a mesma entrega: as tentativas automáticas do job, o botão Reenviar de uma entrega do histórico e uma repetição da fila reaproveitam o payload original, portanto repetem a mesma idempotency_key.

Reenvio em lote (por data / produto)

O painel também oferece um reenvio em lote por intervalo de datas e, opcionalmente, por produto. Ele não reenvia entregas antigas: reconstrói o payload a partir do estado atual do pedido. As diferenças abaixo são obrigatórias para quem consome esses eventos.

Aspecto Comportamento no reenvio em lote
idempotency_key Nova a cada reenvio. Não deduplica contra a entrega original.
Seleção de pedidos Todos os pedidos do produtor cujo created_at está no intervalo escolhido — filtrados pelo produto quando informado.
Eventos emitidos Cada evento marcado é emitido para cada pedido selecionado, mesmo que aquele evento nunca tenha ocorrido para o pedido.
order.* e cancelable Refletem o status atual do pedido, não o status do momento original.
shipment.tracking_status Status atual do rastreio do pedido, não o status do evento reenviado.
description, location, occurred_at, collection_address Sempre null — o reenvio não recarrega o evento de rastreamento original.
delivery_proof Preenchido a partir do rastreio atual, quando houver comprovante.
Intervalo permitido Últimos 30 dias pelo painel. Para períodos maiores, abra um chamado no suporte.
Atenção

Como a idempotency_key é nova, deduplicar apenas por ela não evita processar duas vezes um evento vindo de um reenvio em lote. Para esse caso, deduplique também por uma chave semântica sua, por exemplo event + order.pedido_id (+ shipment.tracking_code quando aplicável).

Atualizado em 24/07/2026