Um Webhook é uma forma de a Zouti enviar notificações automáticas em tempo real para um sistema externo sempre que algo acontece na sua conta — uma venda, um pagamento, uma assinatura, um carrinho abandonado, etc. Em vez de você consultar a Zouti manualmente, a Zouti faz uma requisição HTTP POST para a URL que você configurar, com os dados do evento no corpo. Serve para integrar a Zouti com CRMs, ERPs, áreas de membros, ferramentas de automação e qualquer sistema próprio
1. No painel, acesse o menu Integrações

2. Localize o card Webhooks (categoria "Plataforma externa") e clique em Acessar

3. Clique em Criar Webhook
4. Preencha os campos:
- Nome do Webhook — um nome para você identificar a integração
- URL — o endereço de destino. O prefixo https:// é adicionado automaticamente
(somente HTTPS é aceito)
- Seleção de produtos (opcional) — se nenhum produto for selecionado, o webhook
dispara para todos os produtos da conta; se selecionar produtos específicos, só
dispara para eles
- Seleção de eventos — marque quais eventos devem disparar o webhook
5. Clique em Criar Webhook para finalizar
6. Gerencie os webhooks pela listagem (nome, URL, status Ativo/Inativo, data de criação,
editar, testar e excluir)
Os eventos abaixo são exatamente os que aparecem para o cliente na tela de criação/edição
do webhook, na ordem em que são exibidos. O "Evento técnico" é o valor enviado no
cabeçalho x-zouti-event-type e que identifica o evento na integração.
Pedidos:
Evento (como aparece no painel) | Evento técnico | Objeto enviado | Cliente iniciou o checkout mas não concluiu o pagamento |
Carrinho abandonado |
| AbandonedCart | Cliente iniciou o checkout mas não concluiu o pagamento |
Pedido criado | |
| Um novo pedido é registrado |
Pedido atualizado |
|
| Um pedido existente sofre alteração |
Pedido pago | |
| O pagamento do pedido é confirmado |
Pedido estornado |
|
| O pedido é reembolsado (status |
Pedido em chargeback | |
| Há uma contestação/chargeback (status |
Pedido cancelado |
|
| O pedido é cancelado |
Pedido não pago | | | O pedido não foi pago (ex.: PIX/boleto expirado) |
Pagamentos (PIX e Boleto):
Evento (como aparece no painel) | Evento técnico | Objeto enviado | Cliente iniciou o checkout mas não concluiu o pagamento |
Pix criado |
|
| Um pagamento via PIX é gerado (aguardando pagamento) — inclui |
Boleto criado | |
| Um boleto é gerado (aguardando pagamento) — inclui |
Assinaturas:
Evento (como aparece no painel) | Evento técnico | Objeto enviado | Cliente iniciou o checkout mas não concluiu o pagamento |
Assinatura criada |
|
| Nova assinatura registrada (status |
Assinatura ativada | |
| Assinatura ativada (status |
Assinatura renovada |
|
| Assinatura renovada com sucesso |
Assinatura cancelada |
|
| Assinatura cancelada (status |
Assinatura atrasada | |
| A renovação da assinatura está atrasada |
Parcelamento Inteligente:
Evento (como aparece no painel) | Evento técnico | Objeto enviado | Cliente iniciou o checkout mas não concluiu o pagamento |
Parcelamento inteligente pago | |
| Uma parcela do parcelamento inteligente é paga |
Parcelamento inteligente atrasado |
|
| Uma parcela está atrasada (cobrança falhou) |
Cobranças:
Evento (como aparece no painel) | Evento técnico | Objeto enviado | Cliente iniciou o checkout mas não concluiu o pagamento |
Cobrança Pendente |
|
| Uma cobrança está aguardando pagamento |
Cobrança Aprovada |
|
| Uma cobrança é confirmada |
Cobrança Reembolsada |
|
| Uma cobrança é reembolsada |
Cobrança Contestada |
|
| Uma cobrança é contestada pelo cliente |
Cobrança Expirada |
|
| Uma cobrança expira sem pagamento |
Cobrança Recusada |
|
| Uma cobrança é recusada |
Solicitações de reembolso?
Evento (como aparece no painel) | Evento técnico | Objeto enviado | Cliente iniciou o checkout mas não concluiu o pagamento |
Solicitação de reembolso criada |
|
| O cliente solicita um reembolso |
Solicitação de reembolso aprovada | |
| A solicitação de reembolso é aprovada |
Solicitação de reembolso recusada | |
| A solicitação de reembolso é recusada |
Observação de status
> - Order.status: AWAITING_PAYMENT, PAID, UNPAID, REFUNDED, DISPUTED, CANCELED
> - Subscription.status: INCOMPLETE, ACTIVE, CANCELED, TRIAL
> - Métodos de pagamento: Cartão de crédito CREDIT_CARD), PIX PIX), Boleto BOLETO)
> - RefundRequest.status: PENDING, APPROVED, REFUSED
Um carrinho abandonado é gerado quando um cliente inicia o checkout, não conclui o
pagamento e a sessão de checkout expira. Nesse momento a Zouti dispara o evento
ABANDONED_CART_CREATED (objeto AbandonedCart).
Quando o evento é (e não é) disparado:
O carrinho abandonado só é criado — e o webhook só dispara — se:
A sessão de checkout expirou sem o pagamento ter sido concluído; e
O cliente informou um e-mail durante o checkout.
O evento NÃO é disparado quando:
O checkout não capturou o e-mail do cliente (sem e-mail não há como criar o carrinho/remarketing);
Já existe um pedido pago para o mesmo e-mail + oferta nas últimas 24 horas (o cliente já comprou);
Já existe um carrinho abandonado ativo para o mesmo e-mail + oferta (evita duplicatas).
Etapas do carrinho( Step):
Step | Significado |
| Cliente preencheu apenas os dados pessoais |
| Cliente avançou até a etapa de frete/entrega (produto físico) |
| Cliente chegou à etapa de pagamento |
| Cliente gerou um PIX e não pagou (aguardando pagamento) |
| O pagamento do cliente foi recusado |
Observação técnica: na busca/filtragem, PAYMENT e AWAITING_PAYMENT são tratados como o mesmo grupo de "etapa de pagamento".
O payload completo de exemplo do objeto AbandonedCart está no apêndice técnico.
A plataforma tem a função Testar Webhook, que envia um disparo de exemplo sem
criar dados reais:
1. Na listagem de webhooks, clique em Testar Webhook
2. Escolha qual webhook deseja testar
3. Escolha o evento a simular (só aparecem os eventos configurados naquele webhook)
4. Clique em Enviar webhook teste. O disparo de teste é assinado com o mesmo segredo do webhook, ou seja, seu servidor valida exatamente como uma entrega real.
Inativação automática e reativação
Um webhook é inativado automaticamente após 10 falhas consecutivas de conexão.
O motivo fica registrado como CONSECUTIVE_FAILURES.
Quando isso acontece, um ícone de alerta aparece na listagem com a explicação.
O cliente pode reativar manualmente o webhook depois de corrigir o destino.
Webhooks também podem ser ativados/inativados manualmente pelo switch da listagem.
Webhook inativado não recebe disparos.
Política de retentativa (retry)
Apenas respostas HTTP 2xx são consideradas sucesso.
O servidor de destino deve responder em até 30 segundos (timeout).
Em caso de falha, a Zouti retenta automaticamente com backoff exponencial(intervalo crescente entre tentativas, com no máximo ~12h entre uma e outra).
As retentativas continuam por até 4 dias após o evento original. Depois disso a entrega é descartada.
Cabeçalhos enviados em cada disparo:
content-type: application/json
user-agent: Zouti-Webhook/1.0
x-zouti-webhook-id: web_xxx
x-zouti-webhook-delivery-id: webd_xxx
x-zouti-object-id: ord_xxx (ID do objeto — pedido, assinatura, etc.)
x-zouti-object-name: Order (tipo do objeto: Order | Subscription | AbandonedCart | Payment | SmartInstallment | RefundRequest)
x-zouti-event-id: evt_xxx
x-zouti-event-type: ORDER_PAID (o evento técnico da tabela acima)
x-zouti-signature: t=1717000000000,v1=<hmac_sha256>Verificação de assinatura:
Cada disparo vem assinado por HMAC-SHA256 usando o segredo do webhook. O formato é:
[
t=<timestamp_em_ms>,v1=<hash_hex>
Onde `v1` é o HMAC-SHA256 do **corpo bruto** (raw body) da requisição usando o segredo
como chave. Exemplo de verificação em Node.js:
javascript
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signatureHeader, secret) {
const [tPart, v1Part] = signatureHeader.split(',');
const receivedHash = v1Part.split('=')[1];
const expectedHash = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(receivedHash),
Buffer.from(expectedHash),
);
}> Dica: valide sempre o corpo bruto (antes de qualquer parse/JSON),caso contrário o hash não confere. O segredo pode ser rotacionado pelo painel; após arotação há um período de carência em que a assinatura antiga ainda é aceita.
Valores monetários:
Todos os valores vêm em centavos (ex.: amount: 9790) e também com a versão em reais
no campo correspondente *_in_brl (ex.: amount_in_brl: 97.90).
Exemplos de payload (corpo enviado):
Estes são exatamente os payloads usados na função "Testar Webhook" — representam o formato real de cada tipo de objeto. Campos podem variar conforme o método de pagamento e o tipo de produto.
Pedido Order)** — eventos ORDER_*, PIX_CREATED, BOLETO_CREATED:
```json
{
"id": "ord_eloz1kotrd7jtghi5oa3vl",
"account_id": "acc_...",
"status": "PAID",
"payment_type": "UNIQUE",
"customer_id": "cus_...",
"provider": "ZOUTI",
"order_session_id": "os_...",
"amount_subtotal": 9790,
"amount_subtotal_in_brl": 97.9,
"amount_total": 9790,
"amount_total_in_brl": 97.9,
"currency": "BRL",
"items": [
{
"amount": 9700,
"amount_in_brl": 97.0,
"description": "Curso completo de Desenvolvimento Web Full Stack",
"image_url": "https://.../icon.jpg",
"name": "Curso de Desenvolvimento Web",
"product_id": "prod_...",
"quantity": 1,
"type": "SKU"
}
],
"customer": {
"document": "12345678909",
"email": "[email protected]",
"name": "Maria Silva",
"phone": "5511987654321",
"instagram": "maria.silva"
},
"payment": {
"method": "PIX",
"amount": 9790,
"amount_in_brl": 97.9,
"fee": 290,
"fee_in_brl": 2.9,
"net_amount": 9500,
"net_amount_in_brl": 95.0,
"interest_amount": 0,
"installments": 1,
"pix": { "code": "00020126..." }
},
"utm_data": {
"utm_campaign": "black_friday_2024",
"utm_source": "google",
"utm_medium": "cpc",
"utm_content": "banner_promocional",
"utm_term": "curso programacao"
},
"shipping_address": {
"line1": "Av. Paulista, 1578",
"neighborhood": "Bela Vista",
"city": "São Paulo",
"state": "SP",
"country": "Brasil",
"postal_code": "01310200"
},
"created_at": "2024-12-16T19:50:17.044Z",
"updated_at": "2024-12-16T19:50:17.044Z"
}Assinatura Subscription)** — eventos SUBSCRIPTION_*:
```json
{
"id": "sub_...",
"account_id": "acc_...",
"order_id": "ord_...",
"status": "ACTIVE",
"customer": {
"id": "cus_...",
"name": "João Silva",
"email": "[email protected]",
"document": "11122233344",
"phone": "5511988776655"
},
"plan": {
"id": "prod_...",
"name": "Anual",
"amount": 119700,
"amount_in_brl": 1197.0,
"currency": "BRL",
"interval": "YEARLY"
},
"payment_method": "CREDIT_CARD",
"current_period_start": "2025-05-01T00:00:00.000Z",
"installments": 12,
"cancel_at_period_end": false,
"billing_cycle_count": 1,
"created_at": "2025-05-01T00:00:00.000Z",
"updated_at": "2025-05-01T00:00:00.000Z"
}Carrinho abandonado AbandonedCart)** — evento ABANDONED_CART_CREATED:
```json
{
"id": "ab_d_...",
"step": "PAYMENT",
"total_amount": 14990,
"total_amount_in_brl": 149.9,
"order_session_id": "os_...",
"url": "https://pay.zouti.com.br/infoproduct_cart_token/ab_d_...",
"currency": "BRL",
"customer": {
"name": "Lucas Oliveira",
"email": "[email protected]",
"document": "98765432100",
"phone": "5511999887766"
},
"metadata": {
"theme": "ZOUTI",
"payment_method": "CREDIT_CARD",
"checkout_type": "INFOPRODUCT"
},
"items": [
{
"type": "SKU",
"product_id": "prod_...",
"name": "Curso de Desenvolvimento Web",
"amount": 14990,
"amount_in_brl": 149.9,
"quantity": 1
}
],
"utm_data": { "utm_source": "facebook", "utm_medium": "social" },
"account_id": "acc_...",
"created_at": "2025-05-12T19:30:41.459Z",
"updated_at": "2025-05-12T19:30:41.459Z"
}>O step indica em que etapa o cliente parou: PERSONAL_DATA, PAYMENT, AWAITING_PAYMENT ou REFUSED.
Parcelamento inteligente SmartInstallment)** — eventos SMART_INSTALLMENT_*:
```json
{
"id": "smi_...",
"status": "PAID",
"method": "CREDIT_CARD",
"order_id": "ord_...",
"account_id": "acc_...",
"amount": 9975,
"amount_in_brl": 99.75,
"installment_number": 1,
"total_installments": 12,
"payment_id": "pay_...",
"due_date": "2025-01-15T00:00:00.000Z",
"currency": "BRL",
"paid_at": "2025-01-10T10:30:00.000Z",
"card": { "brand": "VISA", "last_digits": "1111", "holder_name": "João Silva" },
"customer": { "id": "cus_...", "name": "João Silva", "email": "[email protected]" }
}Solicitação de reembolso RefundRequest)** — eventos REFUND_REQUEST_*:
```json
{
"id": "rrq_...",
"account_id": "acc_...",
"order_id": "ord_...",
"payment_id": "pay_...",
"status": "APPROVED",
"reason": "Não recebi o produto",
"rejection_reason": null,
"accepted_at": "2025-05-01T01:00:00.000Z",
"created_at": "2025-05-01T00:00:00.000Z",
"updated_at": "2025-05-01T00:00:00.000Z",
"customer": { "id": "cus_...", "name": "Maria Silva", "email": "[email protected]" },
"order": { "id": "ord_...", "status": "PAID", "amount_total": 9790, "currency": "BRL" },
"payment": { "id": "pay_...", "status": "PAID", "method": "CREDIT_CARD", "amount": 9790 }
}A URL do webhook deve usar HTTPS (o https:// é adicionado automaticamente).
Se nenhum produto for selecionado, o webhook vale para todos os produtos da conta.
Cada webhook pode ter vários eventos associados ao mesmo tempo.
É possível ter mais de um webhook para a mesma URL, cada um com eventos diferentes.-
Apenas respostas HTTP 2xx são consideradas sucesso; o destino deve responder em até 30 segundos.
Em caso de falha, há retentativa automática com backoff exponencial por até 4 dias.
Após 10 falhas consecutivas de conexão, o webhook é inativado automaticamente (motivo CONSECUTIVE_FAILURES) e precisa ser reativado manualmente.
O cliente pode ativar/inativar manualmente pelo switch da listagem; webhook inativado não recebe disparos.
Webhooks cuja URL contém "utmify" não aparecem na listagem principal (ficam só na área da integração Utmify).
Limitações conhecidas:
Somente URLs com protocolo HTTPS são suportadas.
Não há reenvio manual de disparos que falharam (apenas a política de retentativa interna).
Webhooks inativados por falhas consecutivas precisam ser reativados manualmente.
Não é possível visualizar o histórico completo de disparos de um webhook pela interface.
Posso criar mais de um webhook para a mesma URL? Sim, cada um com eventos diferentes.
O que acontece se meu servidor estiver fora do ar? A Zouti retenta por até 4 dias. Após 10 falhas consecutivas, o webhook é inativado automaticamente e precisa ser reativado manualmente.
Como sei se está funcionando? Use a função **Testar Webhook** para enviar um disparo de teste.
O https:// é obrigatório? Sim — e é adicionado automaticamente, não precisa digitar.