Webhooks

Webhooks são notificações automatizadas para URLs definidas, que informam em tempo real sobre eventos. Eles são úteis porque permitem uma resposta imediata a eventos específicos, sem a necessidade de consultas contínuas. Webhooks ajudam a manter os sistemas eficientes e responsivos.

Seus Webhooks são chamados imediatamente quando um evento ocorre. Se não conseguirmos alcançar sua URL ou se ela responder com um status de erro, repetimos o envio segundo um plano fixo e, além disso, verificamos automaticamente ao longo de vários dias se o destinatário está falhando de forma permanente. Os detalhes estão descritos abaixo em Entrega e retries.


Entrega e retries

Quando reenviamos um Webhook

Um Webhook só é reentregue quando a resposta do seu servidor indica um erro temporário:

  • Name
    HTTP 429
    Type
    rate-limit
    Description

    Seu endpoint sinaliza que está recebendo muitas solicitações no momento.

  • Name
    HTTP 5xx
    Type
    erro de servidor
    Description

    Concretamente 500, 502, 503 e 504, além dos códigos de erro de origem da Cloudflare 520 a 524 (por exemplo, 521 "Web Server Is Down", que a Cloudflare retorna em vez de 502 para domínios com proxy).

  • Name
    Erros de conexão
    Type
    rede
    Description

    Tempos esgotados, falhas de resolução DNS, erros de TLS/handshake ou uma conexão interrompida durante a solicitação.

Todas as demais respostas são consideradas finais e não desencadeiam outra tentativa de entrega. Isso inclui códigos 4xx como 400, 401, 403 e 404, pois geralmente indicam um erro de configuração no lado do destinatário que não pode ser resolvido apenas tentando novamente. Respostas na faixa 2xx e 3xx são consideradas bem-sucedidas.

Timeout de requisição

Cada tentativa de entrega tem um timeout de 5 segundos. Se o seu servidor não responder dentro dessa janela, tratamos a tentativa como falha e agendamos um retry conforme o plano abaixo.

Plano de retry

Se uma tentativa falhar, a próxima é agendada após um intervalo fixo e crescente. Realizamos um total de até 14 tentativas; depois disso, o job é descartado. A janela total de entrega abrange, portanto, cerca de 3,5 dias.

TentativaEspera desde a tentativa anterior
1 (inicial)imediatamente
21 minuto
35 minutos
430 minutos
560 minutos
62 horas
7 a 1410 horas cada

Cada tentativa é assinada separadamente e traz cabeçalhos X-Timestamp, X-Nonce e X-Signature novos, veja abaixo em Verificar assinatura.

Desativação automática em caso de falhas persistentes

Além dos retries individuais, monitoramos continuamente a taxa de erro diária por Webhook. Um Webhook é marcado como "problemático" assim que, em um dado dia,

  • a taxa de erro diária estiver acima de 80 % e
  • ocorrerem pelo menos 10 falhas naquele dia.

A partir desse momento, o Webhook é observado por vários dias. Se a situação não melhorar, o sistema escala em etapas:

DiaLimite (taxa de erro rolling)Ação
1acima de 80 %E-mail de aviso nível 1
3acima de 85 %E-mail de aviso nível 2
5acima de 90 %E-mail de aviso nível 3
7acima de 95 %O Webhook é desativado automaticamente, entrada no audit-log e e-mail de desativação

Os contadores diários são contados a partir do dia em que o problema é detectado.

Após uma desativação automática

Um Webhook desativado automaticamente permanece inativo até que você o reative manualmente no Dashboard. Após a reativação, as estatísticas começam do zero.

O que conta como "bem-sucedido"?

Para a avaliação da taxa de erro, toda resposta que não seja um disparador de retry conta como "ok". Concretamente, respostas 2xx, 3xx e até mesmo 4xx (com exceção de 429) são tratadas como bem-sucedidas, pois, do ponto de vista do sistema de envio, representam uma resposta final e inequívoca.


Segurança

Verificar IP de origem

Todos os Webhooks vêm do nosso IPv4 195.201.160.143 ou do IPv6 2a01:4f8:13a:8e7::2. Se isso mudar, você encontrará todos os detalhes no nosso Changelog.

Verificar assinatura

Todos os Webhooks para o seu servidor são assinados com a chave de assinatura da sua conta. Com base nesses dados, você pode validar a autenticidade das solicitações recebidas de nós e evitar duplicações ou solicitações fraudulentas. Verifique a assinatura conforme descrito na página Assinatura.

Enviamos os seguintes cabeçalhos em cada Webhook:

  • Name
    X-Signature
    Type
    string
    Description

    A assinatura gerada por nós

  • Name
    X-Timestamp
    Type
    integer
    Description

    Timestamp no qual a assinatura foi criada por nós

  • Name
    X-Nonce
    Type
    string
    Description

    String gerada aleatoriamente com 32 caracteres

Cada tentativa de entrega é assinada separadamente. O envio inicial, cada retry automático do plano de retry e um reenvio manual pelo depurador de Webhooks têm seu próprio X-Timestamp, seu próprio X-Nonce e uma X-Signature correspondente. Um retry que chega horas após o evento continua, portanto, com um timestamp atual e um nonce que nunca foi usado antes.

Assim, a janela de 30 segundos para o timestamp e a exclusividade do nonce descritas na página Assinatura valem sem alteração também para os retries. Mantenha a sua janela de validação em 30 segundos e não a amplie para os retries.


Exemplos como JSON Payload

SMS

SMS recebida (sms_mo)

{
  "data": {
    "id": "681590",
    "sender": "SMS",
    "system": "491771783130",
    "text": "Hello World",
    "time": "1605878104",
    "message_type": "SMS"
  },
  "webhook_event": "sms_mo",
  "webhook_timestamp": "2020-12-02 11:55:44"
}

Mudança de status do SMS (dlr)

{
  "data": {
    "msg_id": "77149843739",
    "status": "TRANSMITTED",
    "timestamp": "2021-08-24 08:08:00.000000"
  },
  "webhook_event": "dlr",
  "webhook_timestamp": "2021-08-24T08:08:00+02:00"
}

Rastreamento de Desempenho (tracking)

{
    "webhook_event": "tracking",
    "webhook_timestamp": "2022-07-27T07:38:18+02:00",
    "data": {
        "sms_id": "77182424125",
        "sms_label": null,
        "tracking_url": "https://svn.me/7oz",
        "final_url": "https://www.google.com",
        "type": "click",
        "total_clicks": 5,
        "total_views": 3
    }
}

Voz

Alteração de Status da Mensagem de Voz (voice_status)

{
  "data": {
    "callerId": "49176123456789",
    "duration": "4",
    "id": "284195",
    "pricePerMinute": 0.075,
    "recipient": "4943160049851",
    "status": "completed",
    "timestamp": 1629786769
  },
  "webhook_event": "voice_status",
  "webhook_timestamp": "2021-08-24T08:32:50+02:00"
}

Chamada Recebida (voice_call)

{
  "webhook_event": "voice_call",
  "webhook_timestamp": "2024-08-02T07:28:59+02:00",
  "data": {
    "id": 0,
    "caller": "4943160049851",
    "time": 1722576539,
    "system": "4915170517246"
  }
}

Sinal DTMF (voice_dtmf)

{
  "webhook_event": "voice_dtmf",
  "webhook_timestamp": "2024-08-02T07:28:59+02:00",
  "data": {
    "id": 0,
    "callerId": "4943160049851",
    "recipient": "4943160049851",
    "status": "completed",
    "system": "4915170517246",
    "timestamp": 1722576539,
    "duration": 2.76,
    "pricePerMinute": 0.045,
    "dtmf_digit": 9,
    "total_price": 0.045
  }
}

RCS

Mensagem foi entregue

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T10:01:07+01:00",
  "data":
  {
    "msg_id": "77233699836",
    "status": "DELIVERED",
    "timestamp": "1709888466.254410",
    "foreign_id": null,
    "agent_id": "myfancyagent"
  }
}

Mensagem foi lida

{
    "webhook_event": "rcs",
    "webhook_timestamp": "2024-03-08T10:01:09+01:00",
    "data":
    {
      "msg_id": "77233699836",
      "status": "READ",
      "timestamp": "1709888468.065783",
      "foreign_id": null,
      "agent_id": "myfancyagent"
    }
  }

Relatório de status de uma mensagem enviada

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T10:00:18+01:00",
  "data":
  {
    "sender": "4915153952979",
    "status": "IS_TYPING",
    "agent_id": "myfancyagent"
  }
}

Uma mensagem de texto foi enviada

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T09:47:12+01:00",
  "data": {
    "id": 1871353,
    "sender": "4915153952979",
    "time": 1709870553,
    "message_type": "RCS",
    "content_type": "text",
    "text": "Hallo",
    "agent_id": "myfancyagent"
  }
}

Uma resposta sugerida foi selecionada

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T05:59:33+01:00",
  "data": {
    "id": 1870983,
    "sender": "4915153952979",
    "time": 1709870129,
    "message_type": "RCS",
    "content_type": "suggestion_response",
    "suggestion_response": {
      "postbackData": "suggestion_2",
      "text": "Suggestion #2",
      "type": "REPLY"
    },
    "agent_id": "myfancyagent"
  }
}

Uma imagem foi enviada

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T08:58:20+01:00",
  "data": {
    "id": 1871195,
    "sender": "4915153952979",
    "time": 1709870556,
    "message_type": "RCS",
    "content_type": "user_file",
    "user_file": {
      "thumbnail": {
        "mimeType": "image/jpeg",
        "fileSizeBytes": 10166,
        "fileUri": "https://static.seven.io/uploads/rbm/61513d3d/6176746a5177553d/4f36357145416a773452537737696e495a366a68454a42344639574937716e62704156765358664d486e42776730337334675a76522b6d4f574b6e32626632546842493d.jpeg"
      },
      "payload": {
        "mimeType": "image/jpeg",
        "fileSizeBytes": 611314,
        "fileName": "IMG_20240308_050231_01.jpg",
        "fileUri": "https://static.seven.io/uploads/rbm/61513d3d/6176746a5177553d/4f76746e45515768746866717653444e6166336a513852375249614d753637656f414a6f4758624d5358417269306e767331493645372b47446654325a66764b3168453d.jpeg"
      }
    },
    "agent_id": "myfancyagent"
  }
}

Um arquivo foi enviado

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T11:07:51+01:00",
  "data": {
    "id": 1871485,
    "sender": "4915153952979",
    "time": 1709892233,
    "message_type": "RCS",
    "content_type": "user_file",
    "user_file": {
      "payload": {
        "mimeType": "application/pdf",
        "fileSizeBytes": 18810,
        "fileName": "sample (1).pdf",
        "fileUri": "https://static.seven.io/uploads/rbm/61513d3d/617678715267303d/4f616c675256696b73524c6c75337a4b5a66336a51705570464e665a752f6d4e70464d37546e504b5458423730556a7337314e70514c3241436650304d616e4b3078493d.pdf"
      }
    },
    "agent_id": "myfancyagent"
  }
}

Uma localização foi compartilhada

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T11:07:51+01:00",
  "data": {
    "id": 1871485,
    "sender": "4915153952979",
    "time": 1709892233,
    "message_type": "RCS",
    "content_type": "location",
    "location": {
        "latitude": 54.3216562,
        "longitude": 10.1350767
    },
    "agent_id": "myfancyagent"
  }
}

WhatsApp

As mensagens de WhatsApp recebidas são entregues com o evento wa_mo. O objeto data contém sempre os mesmos campos base, independentemente do que o cliente enviou:

  • Name
    id
    Type
    integer
    Description

    ID interno da mensagem recebida

  • Name
    sender
    Type
    string
    Description

    Número de telefone do remetente em formato internacional sem o sinal de mais inicial

  • Name
    time
    Type
    integer
    Description

    Timestamp Unix do recebimento

  • Name
    text
    Type
    string
    Description

    Conteúdo da mensagem. Texto simples em mensagens de texto, uma string codificada em JSON para todos os outros tipos de conteúdo

  • Name
    message_type
    Type
    string
    Description

    Sempre WA

  • Name
    system
    Type
    string
    Description

    O seu número do WhatsApp Business para o qual a mensagem foi enviada

Se definir um filtro de evento no Webhook, ele é comparado com system. Assim pode distribuir as mensagens por número do WhatsApp Business.

Mensagem de texto recebida (wa_mo)

{
  "webhook_event": "wa_mo",
  "webhook_timestamp": "2026-08-21T14:50:12+02:00",
  "data": {
    "id": 4521987,
    "sender": "353861234567",
    "time": 1755780612,
    "text": "Hello World",
    "message_type": "WA",
    "system": "4915170517246"
  }
}

Tipos de conteúdo além de texto simples

Mídias, localizações, contatos, botões de resposta rápida e respostas interativas são entregues no mesmo envelope. O campo text contém então uma string codificada em JSON cuja chave type indica o que foi recebido:

typeEnviado pelo cliente
user_fileImagem, vídeo, áudio, documento ou sticker
locationUma localização compartilhada
contactUm cartão de contato compartilhado
buttonBotão de resposta rápida de uma mensagem de modelo
interactiveResposta a uma mensagem interativa (botão ou lista)

Conteúdo decodificado do campo text:

Mídia (user_file)

{
  "type": "user_file",
  "user_file": [
    {
      "type": "image",
      "mediaId": "1234567890123456",
      "mimeType": "image/jpeg",
      "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "fileName": "IMG_20260821_145012.jpg",
      "fileUri": "https://static.seven.io/uploads/whatsapp/61513d3d/6176746a/4f36357145416a773452537737/IMG_20260821_145012.jpg",
      "caption": "Olha isto"
    }
  ]
}

type é derivado do tipo MIME e é um de image, video, audio, document ou sticker. Se não conseguirmos baixar o arquivo da Meta, fileUri está ausente e o objeto contém "downloadFailed": true. Nesse caso, use mediaId para buscar o arquivo você mesmo.

Localização

{
  "type": "location",
  "location": {
    "latitude": 54.3216562,
    "longitude": 10.1350767,
    "name": "seven communications GmbH",
    "address": "Alte Weide 7, 24116 Kiel"
  }
}

Contato

{
  "type": "contact",
  "contact": {
    "formattedName": "John Doe",
    "firstName": "John",
    "lastName": "Doe",
    "middleName": null,
    "phones": [{"phone": "+353861234567", "type": "CELL"}],
    "emails": [{"email": "john@example.com", "type": "WORK"}],
    "addresses": [],
    "company": "Example Ltd",
    "birthday": "1985-04-12"
  }
}

Botão de resposta rápida

{
  "type": "button",
  "button": {
    "text": "Confirmar agendamento",
    "payload": "confirm_2026_08_21"
  }
}

Resposta interativa

{
  "type": "interactive",
  "interactive": {
    "item_id": "option_2",
    "title": "Opção #2",
    "description": "Entrega na sexta-feira"
  }
}
Última atualização: Há 7 horas