Formato do envelope
A requisição que o Vipter faz ao seu endpoint, campo a campo, com os cabeçalhos, o prazo de resposta, como tratar eventos repetidos e o que o Vipter garante sobre a ordem.
Cada evento chega como um POST para a URL do endpoint. O corpo é um objeto JSON, o envelope, que é igual para todos os tipos. O que muda é o objeto dentro de data.object, descrito no catálogo de eventos.
A requisição
POST /webhooks/vipter HTTP/1.1
Host: erp.example.com
Content-Type: application/json
User-Agent: Vipter-Webhooks/1.0
Vipter-Signature: t=1790604192,v1=5d41402abc4b2a76b9719d911017c592ae2e6b7c0f1d3e5a7b9c2d4f6a8b0c1e
Vipter-Event-Id: evt_3f9a1c7e5b2d4f6a8c0e1b3d
Vipter-Event-Type: order.paid
{"id":"evt_3f9a1c7e5b2d4f6a8c0e1b3d","object":"event","type":"order.paid","created":1790604191,"livemode":true,"api_version":"2026-09-01","data":{"object":{"object":"order","id":"ord_5c1e8a2b9d4f4e7a8b3c6d1e2f7a9b0c", "…": "…"}}}O corpo vem em JSON compacto, sem espaços nem quebras de linha, em UTF-8. Não reformate o corpo antes de verificar a assinatura.
Cabeçalhos
| Cabeçalho | Conteúdo |
|---|---|
Content-Type | application/json |
User-Agent | Vipter-Webhooks/1.0 |
Vipter-Signature | t=<segundos Unix>,v1=<HMAC em hexadecimal>. Durante a troca de segredo, vem um segundo v1=. Veja Verificar a assinatura. |
Vipter-Event-Id | O mesmo valor do campo id do corpo. Dá para descartar uma repetição antes de ler o corpo. |
Vipter-Event-Type | O mesmo valor do campo type do corpo. Dá para rotear o evento sem ler o corpo. |
Na maioria dos frameworks, os nomes de cabeçalho não diferenciam maiúsculas: vipter-signature no Node.js, $_SERVER['HTTP_VIPTER_SIGNATURE'] no PHP.
Campos do envelope
| Campo | Tipo | Conteúdo |
|---|---|---|
id | texto | ID único do evento: evt_ seguido de 24 caracteres hexadecimais. É o mesmo em todas as tentativas e reenvios. |
object | texto | Sempre "event". |
type | texto | Tipo do evento, como order.paid. Veja o catálogo. |
created | inteiro | Quando o evento foi criado, em segundos Unix. Não muda entre tentativas. |
livemode | booleano | O ambiente do Vipter que gerou o evento. Não indica se o provedor de pagamento estava em modo de teste: uma compra de teste com um provedor em sandbox chega com o mesmo valor das vendas reais. |
api_version | texto | Versão do formato. Hoje todos os eventos saem com "2026-09-01". |
data.object | objeto | O pedido, a assinatura, o cliente ou o checkout abandonado, no estado em que estava quando o evento foi criado. |
created e o t da assinatura são coisas diferentes. created é o momento do evento e fica fixo. t é o momento do envio daquela tentativa e muda a cada tentativa.
A resposta
A entrega dá certo quando o seu servidor responde com qualquer código 2xx em até 10 segundos. O corpo da resposta é ignorado.
Conta como falha, e gera nova tentativa:
- Qualquer código fora de 2xx, inclusive 3xx. O Vipter não segue redirecionamentos: se a URL mudou, atualize o endpoint.
- Nenhuma resposta em 10 segundos. A entrega fica com o erro
timeout. - Erro de rede, DNS ou TLS. A URL precisa ser
https://, com certificado válido.
Responda 2xx também para os tipos de evento que o seu sistema ignora. Uma resposta de erro não descarta o evento: ele volta nas tentativas seguintes e conta para a desativação automática.
Responda antes de processar
Verifique a assinatura, grave o evento numa fila ou tabela e responda 200. Faça o trabalho pesado, como chamar outros sistemas, depois de responder.
Idempotência
A entrega é "pelo menos uma vez". O mesmo evento, com o mesmo id, pode chegar mais de uma vez quando:
- o seu servidor processou o evento mas a resposta não chegou ao Vipter em 10 segundos, e a tentativa seguinte repete o envio;
- alguém clicou em Reenviar numa entrega;
- o envio foi interrompido do lado do Vipter antes de registrar o resultado. A entrega volta para a fila depois de 5 minutos e é enviada de novo.
Para isso, guarde o id (ou o cabeçalho Vipter-Event-Id) numa coluna com restrição de unicidade e ignore o evento se ele já existe:
create table vipter_events (
id text primary key, -- evt_…
type text not null,
received_at timestamptz not null default now()
);
-- Ao receber: se a linha já existe, o evento é uma repetição.
insert into vipter_events (id, type) values ($1, $2) on conflict (id) do nothing;Existe um segundo caso, com id diferente: o mesmo fato em dois eventos. Uma mudança feita no painel ou na área do cliente, como cancelar uma assinatura, gera o evento na hora, e a confirmação do provedor sobre a mesma mudança pode gerar outro evento do mesmo tipo. Por isso, além de descartar id repetido, faça o processamento depender do estado e não do evento: "marcar a assinatura como cancelada" pode rodar duas vezes sem problema, "mandar o e-mail de cancelamento" precisa conferir se já foi mandado.
Ordem dos eventos
O Vipter não garante a ordem de chegada. Entregas são feitas em paralelo, e uma entrega que falhou volta horas depois, quando eventos mais novos já chegaram. Um subscription.renewed pode chegar antes do order.paid da renovação, e um order.refunded pode chegar antes do order.paid do mesmo pedido, se o order.paid falhou na primeira tentativa.
Para não sobrescrever um estado novo com um antigo:
- Compare
data.object.updated_atcom o que você já tem gravado e ignore o evento se ele for mais antigo. - Ou, ao receber qualquer evento de um pedido ou assinatura, trate o
statusdo objeto como a verdade naquele momento, e não o tipo do evento.
O que fazer a seguir
- Verifique a assinatura com o código pronto em Node.js, PHP e Python.
- Veja o calendário de tentativas e o que acontece quando o endpoint fica fora do ar.
Catálogo de eventos
Os 20 tipos de evento que o Vipter envia por webhook, quando cada um dispara e o que vem em data.object, com um exemplo completo de pedido, assinatura, cliente e checkout abandonado.
Verificar a assinatura
Como o Vipter assina cada webhook, código testado em Node.js, PHP e Python para conferir a assinatura, como trocar o segredo sem perder eventos e os erros que mais fazem a verificação falhar.