Trabalhando com um agente de IA? Baixe a documentação completa como arquivo Markdown para usar como contexto.
Baixar .md completoQuando um usuário envia mensagem ao seu canal conectado, o Fiwano entrega a mensagem — e depois seus status de entrega — à webhook_url do seu canal como uma requisição POST. Esta página cobre a verificação de webhooks, os formatos de payload por canal, o download de mídia recebida e a consulta ao perfil de um remetente.
Defina a webhook_url e escolha quais webhook_events receber ao conectar um canal (veja Canais); por padrão nenhum evento está habilitado. Se o canal tiver um webhook_secret, cada entrega é assinada para você verificar que veio do Fiwano — fortemente recomendado. Enquanto você não definir um segredo, as entregas são enviadas sem assinatura.
Quando o canal tem um webhook_secret, toda requisição de webhook inclui um header X-Webhook-Signature:
X-Webhook-Signature: sha256=<hmac_hex>
Para verificar: compute o HMAC-SHA256 do corpo bruto (raw) da requisição usando seu webhook_secret como chave, depois compare o digest em hex. (Se nenhum segredo estiver configurado, esse header não é enviado — defina um para habilitar a verificação.)
import hmac, hashlib
def verify_signature(body: bytes, secret: str, header: str) -> bool:
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
received = header.replace("sha256=", "")
return hmac.compare_digest(expected, received)
Cada tipo de canal suporta um conjunto específico de eventos de webhook. Somente os eventos que você habilitar explicitamente via webhook_events são entregues. Por padrão, nenhum evento está habilitado — você precisa configurá-los após conectar um canal.
| Evento | Descrição | Canais |
|---|---|---|
message.received |
Mensagem recebida de um usuário | Todos |
message.sent |
Sua mensagem foi aceita pela Meta | |
message.delivered |
Mensagem entregue no dispositivo do destinatário | WhatsApp, Instagram, Facebook |
message.read |
Mensagem lida pelo destinatário * | WhatsApp, Instagram, Facebook |
message.failed |
A entrega da mensagem falhou |
* message.read no WhatsApp depende das configurações de privacidade do destinatário — se as confirmações de leitura estiverem desativadas, o status read nunca chegará. Trate delivered como um estado terminal de sucesso.
Quando você envia uma mensagem via /api/v1/messages/send, recebe um message_id (UUID). Todos os webhooks de status subsequentes referenciam esse mesmo UUID.
message_id está sempre presente em todos os eventos de status — é um UUID gerado pelo Fiwano, não um ID interno da Meta.sent → delivered → read. Cada status implica todos os anteriores.data.recipient é o identificador do usuário: número de telefone (WhatsApp), IGSID (Instagram) ou PSID (Facebook).message.read separado para cada mensagem não lida — não apenas a mais recente.Todos os payloads compartilham a mesma estrutura de nível superior:
{
"event": "message.received",
"channel_id": "a1b2c3d4e5f67890",
"channel_type": "whatsapp",
"timestamp": "2025-01-15T10:30:00Z",
"data": { ... }
}
{
"event": "message.received",
"channel_id": "a1b2c3d4e5f67890",
"channel_type": "whatsapp",
"timestamp": "2025-01-15T10:30:00Z",
"data": {
"message_id": "wamid.xxx",
"from": "1234567890",
"from_name": "John Doe",
"type": "text",
"text": "Hello!"
}
}
data.from — número de telefone do remetente sem +. Use diretamente como recipient ao responder.
{
"event": "message.received",
"channel_id": "b2c3d4e5f6789012",
"channel_type": "instagram",
"timestamp": "2025-01-15T10:30:00Z",
"data": {
"message_id": "mid.xxx",
"from": "6543217890123456",
"from_name": null,
"type": "text",
"text": "Hi there!"
}
}
data.from — IGSID. Use como recipient ao responder. from_name é sempre null (a Meta não inclui o nome do remetente nos webhooks do IG).
{
"event": "message.received",
"channel_id": "c3f8a1b2e4d56789",
"channel_type": "facebook",
"timestamp": "2025-01-15T10:30:00Z",
"data": {
"message_id": "mid.xxx",
"from": "7890123456789012",
"from_name": null,
"type": "text",
"text": "Hello from Messenger!"
}
}
data.from — PSID. Use como recipient ao responder. from_name é sempre null (a Meta não inclui o nome do remetente nos webhooks do FB).
Com uma licença Pro, mensagens de mídia incluem o conteúdo do arquivo. O arquivo de mídia é baixado da Meta e armazenado temporariamente. Use o download_url para buscar o arquivo antes que ele expire.
Exemplo de imagem no WhatsApp:
{
"event": "message.received",
"channel_id": "a1b2c3d4e5f67890",
"channel_type": "whatsapp",
"timestamp": "2025-01-15T10:30:00Z",
"data": {
"message_id": "wamid.xxx",
"from": "1234567890",
"from_name": "John Doe",
"type": "image",
"caption": "Check this photo",
"media": {
"media_id": "m1b2c3d4e5f67890",
"mime_type": "image/jpeg",
"file_size": 245760,
"filename": null,
"sha256": "abc123...",
"duration_ms": null,
"download_url": "https://fiwano.com/api/v1/media/m1b2c3d4e5f67890",
"expires_at": "2025-01-15T11:30:00Z"
}
}
}
Exemplo de mensagem de voz no WhatsApp:
{
"event": "message.received",
"channel_id": "a1b2c3d4e5f67890",
"channel_type": "whatsapp",
"timestamp": "2025-01-15T10:30:00Z",
"data": {
"message_id": "wamid.xxx",
"from": "1234567890",
"from_name": "John Doe",
"type": "audio",
"media": {
"media_id": "m2b3c4d5e6f78901",
"voice": true,
"mime_type": "audio/ogg; codecs=opus",
"file_size": 12345,
"filename": null,
"sha256": "def456...",
"duration_ms": 5200,
"download_url": "https://fiwano.com/api/v1/media/m2b3c4d5e6f78901",
"expires_at": "2025-01-15T11:30:00Z"
}
}
}
Instagram e Facebook Messenger usam o mesmo bloco normalizado data.media. O remetente continua sendo data.from (IGSID para Instagram, PSID para Facebook), e data.type segue o tipo de anexo da Meta, como "image" ou "audio". Use data.type como o tipo primário da mensagem e como o media_type de saída ao ecoar ou encaminhar mídia.
O Fiwano preserva o formato de arquivo original da Meta e não faz transcodificação de mídia. media.mime_type descreve os bytes do arquivo baixado, não a semântica da mensagem. Por exemplo, clipes em estilo de voz do Facebook Messenger costumam baixar como OGG/Opus (audio/ogg), enquanto mensagens de áudio do Instagram podem baixar como MP4 somente-áudio servido com video/mp4. Em ambos os casos o tipo da mensagem ainda é data.type: "audio".
Apenas para WhatsApp recebido, a Meta fornece um indicador confiável de mensagem de voz. O Fiwano o expõe como media.voice: true quando presente. Instagram e Facebook Messenger não expõem um indicador de voz confiável equivalente pelo payload do webhook, então media.voice é omitido para esses canais.
O download_url é autenticado; busque-o com sua X-API-Key. Não o passe diretamente como media_url de saída, porque a Meta não enviará o header da sua API key — re-hospede os bytes atrás de uma URL HTTPS pública ou assinada primeiro.
Campos do payload de mídia:
| Campo | Tipo | Descrição |
|---|---|---|
media_id |
string | ID do arquivo de mídia — use em GET /api/v1/media/{media_id} para baixar |
voice |
bool | Presente apenas para mensagens de voz do WhatsApp (true). Omitido para IG/FB porque a Meta não fornece um indicador de voz confiável ali. |
mime_type |
string | Tipo MIME (ex.: image/jpeg, audio/ogg; codecs=opus) |
file_size |
int | Tamanho do arquivo em bytes |
filename |
string|null | Nome de arquivo original (apenas documentos) |
sha256 |
string|null | Hash SHA-256 da Meta (apenas WhatsApp) |
duration_ms |
int|null | Duração em milissegundos (apenas áudio/vídeo) |
download_url |
string|null | URL de download autenticada. null se o download da Meta falhou. |
error |
string | Presente apenas quando o download falhou — descreve o erro |
expires_at |
string | Timestamp ISO 8601 — o arquivo é excluído após esse horário |
Observação: Trate mensagens de voz como mensagens de áudio.
data.type: "audio"é o valor estável entre canais para roteamento e encaminhamento.media.voiceé uma dica opcional, exclusiva do WhatsApp, para UI/UX.
Busque o arquivo em data.media.download_url (que é GET /api/v1/media/{media_id}) com sua X-API-Key:
curl https://fiwano.com/api/v1/media/m1b2c3d4e5f67890 \
-H "X-API-Key: YOUR_API_KEY" \
--output photo.jpg
A resposta são os bytes brutos do arquivo com o Content-Type original (e um nome de arquivo em Content-Disposition quando conhecido). Os arquivos expiram 60 minutos após o webhook — baixe prontamente e re-hospede o que precisar manter; após a expiração a URL retorna 410 Gone. Os tamanhos estão em Capacidades; os códigos de status na Referência da API.
Com uma licença Starter, mensagens de mídia são entregues com type: "unsupported" e um campo upgrade_required indicando qual licença é necessária:
{
"event": "message.received",
"channel_id": "a1b2c3d4e5f67890",
"channel_type": "whatsapp",
"timestamp": "2025-01-15T10:30:00Z",
"data": {
"message_id": "wamid.xxx",
"from": "1234567890",
"from_name": "John Doe",
"type": "unsupported",
"unsupported_type": "image",
"upgrade_required": "pro"
}
}
O campo upgrade_required informa qual plano de licença é necessário. Faça upgrade pela página Billing no portal para receber o conteúdo completo de mídia.
Observação: Mensagens com tipos realmente não suportados (ex.:
location,contacts,reaction) ainda chegam comotype: "unsupported"semupgrade_required, independentemente do seu plano de licença.
{
"event": "message.read",
"channel_id": "b2c3d4e5f6789012",
"channel_type": "instagram",
"timestamp": "2025-01-15T10:30:10Z",
"data": {
"message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "read",
"recipient": "6543217890123456"
}
}
Mesmo formato para todos os canais e todos os status (sent, delivered, read). message_id é o UUID da resposta de envio.
{
"event": "message.failed",
"channel_id": "a1b2c3d4e5f67890",
"channel_type": "whatsapp",
"timestamp": "2025-01-15T10:30:05Z",
"data": {
"message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipient": "1234567890",
"status": "failed",
"error": "Message undeliverable",
"errors": [{"code": 131047, "title": "Message undeliverable"}]
}
}
Se sua webhook URL retornar um status não-2xx ou estiver inacessível, o sistema tenta novamente automaticamente:
Importante: Seu endpoint deve responder com HTTP 2xx em até 5 segundos. Respostas não-2xx ou timeouts disparam a fila de novas tentativas. Mensagens recebidas da Meta são sempre salvas do nosso lado, mesmo que o repasse falhe.
Dica: Habilite apenas os eventos de webhook que você de fato trata. Eventos não tratados que recebem respostas não-2xx vão encher sua fila de novas tentativas desnecessariamente.
O WhatsApp inclui o nome do remetente em cada webhook (data.from_name) — nenhuma chamada extra necessária. Instagram e Facebook não (data.from_name é sempre null); para obter um nome ou avatar, chame o endpoint de perfil:
GET /api/v1/channels/{channel_id}/profile/{user_id}
Passe o valor de data.from (IGSID para Instagram, PSID para Facebook) como user_id. Ele retorna:
username, name, profile_pic, follower_count, is_verifiedfirst_name, last_name, profile_picWhatsApp não é suportado (o nome já está no webhook). Os resultados são cacheados por 5 minutos — a flag cached da resposta indica se foi um acerto de cache. Request/response completos e códigos de status estão na Referência da API.
Dica: chame isto uma vez quando você vir um novo
data.frompela primeira vez, depois cacheie o resultado do seu lado — não há necessidade de chamar a cada mensagem.
Documentação da API Fiwano