Get in Touch

Have a question about the platform, need help with your integration, or want to discuss partnership opportunities and enterprise pricing? Drop us an email — we’ll do our best to get back to you within 3–4 hours.

contact@fiwano.com
Menu da documentação

Trabalhando com um agente de IA? Baixe a documentação completa como arquivo Markdown para usar como contexto.

Baixar .md completo

Recebendo Mensagens

Quando 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.

Setup e payload de webhook do WhatsApp

Para WhatsApp, habilite message.received no canal e aponte webhook_url para seu endpoint HTTPS público. O envelope do webhook tem o mesmo formato nos três canais:

  • Remetentes do WhatsApp chegam como números de telefone em data.from.
  • Remetentes do Instagram chegam como valores IGSID em data.from.
  • Remetentes do Facebook Messenger chegam como valores PSID em data.from.

Os campos de nível superior (event, channel_id, channel_type, timestamp, data) permanecem estáveis, então um único receiver consegue lidar com webhooks do WhatsApp, webhooks do Instagram e webhooks do Messenger sem três parsers separados da Meta.

Verificando assinaturas

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)

Tipos de evento

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.echo Cópia de uma mensagem que sua empresa enviou fora da Fiwano (app WhatsApp Business, caixa de entrada do Instagram, Caixa de Entrada da Página do Facebook, Meta Business Suite, outra integração) Todos
conversation.referral Um usuário que retorna clicou em um anúncio ou link m.me / ig.me em uma conversa existente sem escrever; traz o mesmo bloco referral de message.received e reabre a janela de 24 horas (beta) Instagram, Facebook
message.sent Sua mensagem foi aceita pela Meta WhatsApp
message.delivered Mensagem entregue no dispositivo do destinatário WhatsApp, Instagram, Facebook
message.read Mensagem lida pelo destinatário * WhatsApp, Instagram, Facebook
message.failed Sua mensagem não pôde ser entregue WhatsApp, Instagram, Facebook

* message.read depende das configurações de privacidade do destinatário no WhatsApp e no Instagram — se ele tiver desativado as confirmações de leitura, o status read nunca chegará. Trate delivered como um estado terminal de sucesso.

Novos tipos de evento podem ser adicionados com o tempo; eles nunca são ativados em um canal existente até você incluí-los em webhook_events. Ignore campos de payload que você não conhece — veja Compatibilidade.

Rastreamento de status de entrega

Quando você envia texto, mídia ou um modelo do WhatsApp por qualquer endpoint de envio, recebe um message_id (um UUID do Fiwano). Todo webhook de status dessa mensagem traz o mesmo message_id, no mesmo formato em todos os canais.

  • Progressão de status: 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).
  • Cascata de leitura: quando um usuário lê uma conversa, o Fiwano envia um webhook message.read separado para cada mensagem não lida que você enviou a esse usuário — não apenas a mais recente.
  • Os status podem chegar fora de ordem — no Instagram um read pode chegar antes do delivered da mesma mensagem. Trate o status mais alto já visto como o atual e faça upsert por message_id.

Formato do payload

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": { ... }
}

message.received (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": "text",
    "text": "Hello!"
  }
}

data.from — número de telefone do remetente sem +. Use diretamente como recipient ao responder.

message.received (Instagram)

{
  "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).

message.received (Facebook Messenger)

{
  "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).

message.received — mídia (Pro)

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 entregam o mesmo bloco data.media; apenas data.from muda (IGSID ou PSID em vez de um número de telefone).

data.type é o tipo da mensagem em todos os canais: text, image, audio, video, document, share ou unsupported. Faça o roteamento por ele. Se algum dia aparecer um tipo que sua integração não reconhece, trate-o como unsupported (veja Compatibilidade). Os quatro tipos de mídia são exatamente os valores aceitos como media_type de saída, então não é preciso mapear tipos para encaminhar uma mensagem de mídia — apenas o arquivo precisa ser re-hospedado, porque a download_url é autenticada (veja Baixando mídia recebida).

Figurinhas (stickers) chegam como image em todos os canais. No WhatsApp e no Messenger elas trazem media.sticker: true para diferenciá-las de fotos. Uma figurinha do WhatsApp é um arquivo WebP (mime_type: "image/webp"); uma figurinha do Messenger também traz o media.sticker_id persistente da Meta (369239263222822 é o joinha). No Instagram, uma figurinha ou um GIF escolhido no app chega como imagem comum — type: "image", mime_type: "image/gif", sem media.sticker — porque a Meta não envia nenhum marcador de figurinha ali, então não dá para distinguir uma figurinha de um GIF. Para devolver uma figurinha use media_type: "sticker" — encaminhar um WebP como image é rejeitado pelo WhatsApp.

Instagram e Facebook Messenger também usam anexos para coisas que não são arquivo. Um post, reel ou story compartilhado, ou uma menção em story, chega como type: "share"; um pin de localização ou um cartão de produto chega como type: "unsupported", sem bloco data.media — veja tipo unsupported abaixo.

Quando a Meta inclui texto junto com mídia, o Fiwano expõe esse texto como data.caption no evento de mídia. Mensagens somente de texto continuam usando data.text. Essa regra é igual para WhatsApp, Instagram e Facebook Messenger.

Vários anexos: cada anexo é entregue em seu próprio webhook message.received e em seu próprio POST HTTP; o Fiwano nunca envia um array de eventos de webhook. Os eventos são enviados na ordem dos anexos da Meta. O primeiro evento mantém o ID da mensagem da Meta e recebe a legenda, quando houver. Os eventos seguintes usam IDs determinísticos com .2, .3 e assim por diante, sem repetir a legenda:

mid.xxx       imagem + legenda
mid.xxx.2     imagem
mid.xxx.3     vídeo

Trate o message_id recebido como uma chave opaca de idempotência; não interprete o sufixo nem envie esse ID para a Meta. As tentativas de entrega continuam independentes por evento, então uma falha no endpoint do cliente ainda pode fazer uma parte posterior chegar antes da nova tentativa de uma parte anterior. Uma falha no download de mídia não elimina os demais anexos: seu evento contém media.download_url: null e media.error.

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".

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.
sticker bool Presente apenas quando a imagem é uma figurinha (true) — WhatsApp (WebP) e Facebook Messenger. Nunca aparece no Instagram: as figurinhas do Instagram chegam como imagens GIF comuns.
sticker_id string Apenas figurinhas do Facebook Messenger — id persistente da figurinha na Meta. Devolva-a com media_type: "sticker".
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). No Facebook Messenger é o nome como a Meta o entrega — a Meta troca espaços por - e remove parte da pontuação. O app do Instagram não envia 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 arquivo não está disponível: o download da Meta falhou ou o arquivo passa do limite de 10 MB (file_size ainda mostra o tamanho real).
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.

Baixando mídia recebida

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

Qualquer tipo de arquivo entregue pela Meta fica disponível; o único limite é o tamanho. A resposta são os bytes brutos do arquivo com o Content-Type original e, quando o nome é conhecido, Content-Disposition: attachment conforme a RFC 6266 — filename (alternativa em ASCII, _ para os demais caracteres) seguido de filename* com o nome exato em UTF-8 quando os dois diferem. Prefira filename*, ou simplesmente use media.filename. Os arquivos expiram cerca de 60 minutos após o Fiwano buscá-los da Meta; media.expires_at é a referência definitiva. 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.

message.received — toques em botões e escolhas de menu (todos os canais)

Quando um usuário escolhe uma das opções que você ofereceu, a escolha chega como uma mensagem text comum cujo text é o rótulo que ele viu, e button_id é o id de máquina da opção — o id que você definiu em send-interactive. Isso cobre os botões de resposta interativos e as linhas de lista do WhatsApp, as respostas rápidas e os botões de template do Instagram e do Messenger, os botões de resposta rápida de modelos do WhatsApp e os postbacks do Messenger / Instagram configurados fora do Fiwano (Get Started, ice breakers, menu persistente):

{
  "event": "message.received",
  "channel_id": "a1b2c3d4e5f67890",
  "channel_type": "whatsapp",
  "timestamp": "2025-01-15T10:31:00Z",
  "data": {
    "message_id": "wamid.yyy",
    "from": "1234567890",
    "from_name": "John Doe",
    "type": "text",
    "text": "Adicionar",
    "button_id": "add_to_cart:SKU-1",
    "reply_to": {"message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}
  }
}

Não há evento separado: um único handler de texto cobre respostas digitadas e toques, e button_id os distingue — ele só aparece em um toque. É o id que você definiu — no send-interactive, ou no send-template para um botão de resposta rápida (sem id, o texto do botão). Para botões configurados fora do Fiwano, como um menu ou ice breakers do Messenger, é o payload definido lá. No WhatsApp, a qual mensagem o botão pertencia está em reply_to: reply_to.message_id é o UUID que você recebeu de send-interactive ou send-template. O Instagram e o Messenger não informam a qual mensagem um toque responde; lá, button_id é como você o reconhece.

message.received — posts, reels, stories e menções em story compartilhados

No Instagram e no Facebook Messenger um usuário pode compartilhar um post ou um reel na conversa; no Instagram ele também pode enviar um story pelo botão de compartilhar do story, ou mencionar sua conta no story dele. Esses chegam como type: "share":

{
  "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": "share",
    "share_type": "reel",
    "caption": "Tem esse em azul?",
    "share": {"url": "https://www.instagram.com/reel/ABC123/", "expires_at": null}
  }
}
Campo Descrição
share_type post (um post compartilhado), reel (um reel compartilhado), story (Instagram: o usuário enviou um story — dele ou de outra pessoa), story_mention (Instagram: o usuário mencionou você no story dele)
share.url Link da Meta para o conteúdo compartilhado. Um post do Facebook ou um reel: a página, que abre no Facebook / Instagram. Um post do Instagram: o arquivo de imagem do post (uma imagem, também em um carrossel) — a Meta não envia link para a página do post e não informa por quanto tempo este continua válido, então não o armazene. Um story ou uma menção em story: a mídia do próprio story
share.expires_at Somente story e story_mention: o story desaparece em até 24 horas (a Meta não informa quando ele foi publicado, então o valor é 24 horas após a mensagem — o máximo que ele ainda pode existir). null para posts e reels
caption Texto que o usuário escreveu junto com o compartilhamento, quando houver — o mesmo caption da mídia. A legenda do próprio post compartilhado não é incluída

O conteúdo compartilhado não é baixado e não há bloco data.media: um post pertence ao seu autor, e a Meta não permite que aplicativos armazenem a mídia de stories. Uma foto ou vídeo do próprio usuário enviado como mensagem continua sendo um evento image / video comum.

Facebook Messenger. A Meta envia um post compartilhado como share somente quando o post tem foto própria (share_type: "post") ou vídeo ("reel"). Os demais posts — só texto ou de um grupo — itens do Marketplace e stories do Facebook (que só podem ser compartilhados como link) chegam como type: "text" com o link, igual a um link digitado pelo usuário. O texto que o usuário escreve junto com o compartilhamento chega como uma mensagem text separada.

Respostas e mensagens citadas — reply_to

Quando um usuário responde a uma mensagem específica ("Responder" no WhatsApp, deslizar para responder no Instagram / Messenger, toque em botão de template do WhatsApp), o evento traz reply_to ao lado de type:

"reply_to": {"message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}

reply_to.message_id é o id que você já tem para a mensagem citada: o UUID do Fiwano se for uma mensagem que você enviou pelo Fiwano (ou recebeu como message.echo), ou o id do provedor de data.message_id se for uma mensagem anterior do usuário. Compare com os ids que você armazenou e saberá qual mensagem foi citada; não há um sinalizador de origem separado. Para uma mensagem com vários anexos, é o id da primeira parte. No caso raro de uma resposta chegar ao Fiwano antes de o envio citado ser registrado, o id do provedor é repassado como está.

Uma resposta do Instagram ao seu story traz o story em vez de um id de mensagem:

"reply_to": {
  "story": {
    "id": "17900000000000009",
    "url": "https://lookaside.fbsbx.com/…/story.jpg",
    "expires_at": "2025-01-16T10:30:00Z",
    "link_url": "https://shop.example/promo"
  }
}

story.id é o id de mídia do seu story, story.url um link temporário para a mídia dele, story.expires_at o fim das suas 24 horas de vida, e story.link_url a figurinha de link que o usuário tocou, quando houve uma (caso contrário null).

reply_to está presente em message.received e em message.echo (um operador respondendo a uma mensagem específica), nos três canais e para todos os tipos de mensagem. Está ausente quando a mensagem não é uma resposta.

Contexto de referral — anúncios e links (beta)

Quando uma conversa começa a partir de um anúncio Click-to-WhatsApp, Click-to-Instagram ou Click-to-Messenger, ou de um link m.me / ig.me com parâmetro ref, a Meta anexa a atribuição ao primeiro evento recebido. A Fiwano a repassa como data.referral no message.received que segue o clique — normalmente a primeira mensagem da conversa; um toque em ice breaker ou Get Started no Instagram / Messenger chega como type: "text" e a traz da mesma forma. Em uma mensagem com vários anexos, ela está apenas na primeira parte.

Beta até novembro de 2026. As quatro chaves normalizadas (source, text, image_url, ref) e o evento conversation.referral podem ser ajustados; raw tem garantia de permanecer exatamente como está, então tudo o que for construído sobre raw está seguro. Se você pretende depender das chaves normalizadas ou de conversation.referral, avise-nos em support@fiwano.com: se algo mudar, informaremos antes da mudança.

"referral": {
  "source": "ad",
  "text": "Chat with us\nSummer Succulents are here!",
  "image_url": "https://scontent.xx.fbcdn.net/v/t45.1/...",
  "ref": null,
  "raw": {
    "source_url": "https://fb.me/3cr4Wqqkv",
    "source_id": "120226305854810726",
    "source_type": "ad",
    "headline": "Chat with us",
    "body": "Summer Succulents are here!",
    "media_type": "image",
    "image_url": "https://scontent.xx.fbcdn.net/v/t45.1/...",
    "ctwa_clid": "Aff-n8ZTODiE79d22KtAwQKj9e_mIEOOj27vDVwFjN80dp4...",
    "welcome_message": {"text": "Hi there! Let us know how we can help!"}
  }
}
Campo Significado
source De onde o usuário veio. ad — anúncio pago (inclusive posicionamentos em Story); link — link m.me / ig.me com ref; product — página de produto da Loja do Instagram. O conjunto é aberto: outra origem informada pela Meta é repassada em minúsculas, unknown significa que a Meta não enviou origem.
text O texto do anúncio que o usuário viu. WhatsApp: título e texto principal unidos por quebra de linha. Instagram / Messenger: o título do anúncio fornecido pela Meta. null para links.
image_url O criativo como imagem: a foto de um anúncio de imagem ou a miniatura de um anúncio de vídeo. null quando a Meta não envia.
ref O seu próprio marcador do parâmetro ref de um link m.me / ig.me ou de um anúncio do Instagram / Messenger. Sempre null no WhatsApp.
raw O objeto de referral da Meta exatamente como recebido: source_id / ad_id, source_url, post_id, ctwa_clid, headline / body / ad_title, welcome_message, flow_id, product. Os nomes dos campos variam por canal; consulte a referência da Meta do seu canal.

text e image_url foram pensados para ir direto ao seu modelo: o usuário está respondendo a um anúncio que dizia isto e tinha esta aparência.

Um usuário que retorna — conversation.referral (Instagram, Messenger). Quando um usuário que já conversou com você clica em um anúncio ou em um link m.me / ig.me sem escrever, a Meta envia a atribuição como um evento separado, sem mensagem. O clique reabre a janela de 24 horas, então você pode responder. Ative conversation.referral em webhook_events para recebê-lo; o payload é data.from, data.from_name e o mesmo bloco referral. O WhatsApp não tem equivalente: lá a atribuição sempre chega junto com uma mensagem.

{
  "event": "conversation.referral",
  "channel_id": "a1b2c3d4e5f67890",
  "channel_type": "instagram",
  "timestamp": "2026-09-21T10:30:00Z",
  "data": {
    "from": "17841400000000777",
    "from_name": null,
    "referral": {"source": "link", "text": null, "image_url": null, "ref": "spring_promo", "raw": {"ref": "spring_promo", "source": "SHORTLINKS", "type": "OPEN_THREAD"}}
  }
}

O que esperar da Meta:

  • Uma única vez. O bloco vem no evento que segue o clique e não se repete nas mensagens seguintes. Guarde-o na conversa quando chegar; a Fiwano não mantém histórico de mensagens.
  • Os links do criativo são temporários. image_url e as URLs em raw são links públicos assinados da CDN da Meta, sem token. A Meta não documenta a validade: baixe a imagem quando o evento chegar se quiser mantê-la.
  • A atribuição pode ser incompleta. A Meta omite raw.ctwa_clid em anúncios posicionados no Status do WhatsApp e pode omiti-lo em cliques vindos de um navegador, após o anúncio ser excluído ou quando o usuário fechou o contexto do anúncio antes de escrever. A ausência do id de clique não significa conversa orgânica.
  • Só anúncios e links com ref trazem atribuição. Uma mensagem pelo botão do perfil, pelo link na bio do Instagram, por um link wa.me ou por QR code é uma mensagem comum, sem referral.
  • raw.welcome_message.text (WhatsApp) é a saudação configurada no anúncio. O WhatsApp a mostra no chat antes de o usuário escrever; ela não é enviada pela API, então não há mensagem de saída nem eco dela.

A Fiwano não chama a Conversions API da Meta. Para atribuir uma venda a um anúncio Click-to-WhatsApp, guarde raw.ctwa_clid e raw.source_id no início da conversa e envie o evento de conversão você mesmo dentro da janela de 7 dias da Meta (action_source: business_messaging, messaging_channel: whatsapp, user_data.ctwa_clid sem hash, user_data.whatsapp_business_account_id). No Instagram e no Messenger use raw.ad_id nos seus próprios relatórios; a Meta não tem id de clique nesses canais.

message.received — tipo não suportado (todos os canais)

Uma mensagem chega como type: "unsupported" quando o Fiwano não pode entregar o conteúdo como arquivo. unsupported_type diz o que era, e não há bloco data.media. Existem dois motivos, e upgrade_required os distingue.

Mídia em uma licença Starter. O arquivo existe, mas seu plano não o inclui. unsupported_type é o tipo de mídia que o Pro teria entregue, e upgrade_required indica o plano que o libera:

{
  "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"
  }
}

Faça upgrade pela página Billing no portal para receber o conteúdo completo de mídia.

Conteúdo que não é um arquivo. Nenhum plano entrega esses casos, então upgrade_required está ausente. unsupported_type carrega o nome que a própria Meta usa para o conteúdo:

Canal Valores de unsupported_type
WhatsApp location, contacts, order (pedido de catálogo), system (por exemplo, troca de número), edit e revoke (mensagem editada ou apagada no app WhatsApp Business), nfm_reply (resposta de um WhatsApp Flow), poll_creation, poll_update, gif, group_invite e qualquer outro tipo que o WhatsApp informe
Instagram template (um cartão que a Meta envia no lugar do conteúdo, por exemplo uma localização compartilhada), ephemeral (foto ou vídeo de visualização única — a Meta não entrega o conteúdo), unsupported (o próprio Instagram não conseguiu entregar o conteúdo)
Facebook Messenger template, location, appointment_booking, fallback (um item compartilhado que a Meta entregou sem link), unsupported

Qualquer tipo não listado aqui chega da mesma forma, então um unsupported_type desconhecido continua sendo apenas conteúdo não suportado. Reações a mensagens, edições e exclusões de mensagens, e mensagens em grupos do WhatsApp não são entregues como eventos de webhook. Uma prévia de link do Messenger chega como text: com o texto da mensagem quando o usuário escreveu algo, caso contrário como text contendo a URL. Só uma prévia sem link vira unsupported / fallback.

message.echo — mensagens enviadas fora da Fiwano

Quando alguém do seu lado responde a um cliente sem passar pela Fiwano, a Meta ecoa essa mensagem de volta — e a Fiwano pode entregar uma cópia para você, para que seu sistema veja a conversa inteira, não apenas a metade dele. Origens por canal:

Canal De onde a mensagem foi enviada
WhatsApp App WhatsApp Business ou dispositivo vinculado, em um número Coexistence
Instagram Caixa de entrada do app Instagram, Meta Business Suite ou outra integração
Facebook Messenger Caixa de Entrada da Página, Meta Business Suite ou outra integração

Habilite por canal adicionando message.echo a webhook_events (desativado por padrão, disponível em todos os planos). Mensagens enviadas pela Fiwano nunca chegam como eco — você já as tem.

{
  "event": "message.echo",
  "channel_id": "b2c3d4e5f6789012",
  "channel_type": "instagram",
  "timestamp": "2026-09-01T10:30:00Z",
  "data": {
    "message_id": "550e8400-e29b-41d4-a716-446655440000",
    "recipient": "6543217890123456",
    "status": "sent",
    "type": "text",
    "text": "Resposta do operador"
  }
}
  • message_id — um UUID da Fiwano, exatamente como o que você recebe ao enviar pela API. Ele é estável: se a Meta reenviar o mesmo eco, você recebe o mesmo UUID — deduplique por ele.
  • recipient — o usuário que recebeu a mensagem, no mesmo formato aceito pelos endpoints de envio (telefone no WhatsApp, IGSID no Instagram, PSID no Facebook). Você pode responder diretamente para recipient.
  • status: "sent" — o estado inicial do ciclo de vida. Um eco confirma que a mensagem existe na conversa, não que chegou ao dispositivo do destinatário. Não é emitido message.sent separado para ecos.
  • Quem exatamente enviou a mensagem (operador, dispositivo ou app) não é exposto — a Meta não fornece uma identidade confiável para isso.

Rastreamento de status para ecos. Por padrão um eco é uma cópia única: sem delivered/read posteriores. Defina o campo booleano echo_statuses do canal como true (via PATCH /api/v1/channels/{id} ou no Portal) e as mensagens ecoadas ganham o mesmo ciclo de status das mensagens enviadas pela Fiwano: os webhooks message.delivered / message.read / message.failed seguintes referenciam o mesmo message_id do eco e são filtrados pelo seu webhook_events exatamente como os status comuns.

Os status delivered e read de ecos do WhatsApp são entregues da mesma forma que para mensagens enviadas pela Fiwano. A Meta não garante formalmente a entrega de status para mensagens enviadas do app WhatsApp Business, então trate um status ausente como normal, não como erro.

O Instagram não tem recibo de entrega; o próprio eco equivale ao delivered sintético que a Fiwano emite para os seus envios no Instagram, então nenhum message.delivered separado segue um eco do Instagram. Uma confirmação de leitura do Instagram cobre a conversa inteira: segue um message.read para cada mensagem ecoada que o usuário ainda não tinha lido, da mesma forma que para as mensagens enviadas pela Fiwano.

Status e ecos são entregues de forma independente e at-least-once: um status pode ocasionalmente chegar antes do eco ao qual pertence. Correlacione por message_id e faça upsert em vez de depender da ordem de chegada.

Mídia em ecos não é entregue. Uma mensagem de mídia ecoada mantém o data.type real (image, audio, video, document; uma figurinha é image, com media.sticker: true no WhatsApp e no Messenger) e a legenda quando presente, mas o arquivo em si é pulado — data.media chega sem download:

{
  "data": {
    "message_id": "550e8400-e29b-41d4-a716-446655440000",
    "recipient": "6543217890123456",
    "status": "sent",
    "type": "image",
    "caption": "Foto da fatura",
    "media": {"media_id": null, "download_url": null, "unavailable": "echo_media_not_supported"}
  }
}

A regra que você já aplica à mídia recebida — verifique media.download_url antes de baixar — cobre este caso sem código extra e mantém seu handler compatível caso a mídia de ecos fique disponível no futuro. Mensagens do Instagram/Messenger com múltiplos anexos são divididas em eventos message.echo separados por anexo (cada um com seu próprio message_id); um post ou reel compartilhado chega como type: "share" e os demais anexos que não são arquivos como type: "unsupported" com unsupported_type — igual ao message.received. O eco de uma resposta traz reply_to: quando um operador responde a uma mensagem específica do cliente, reply_to.message_id é o id do provedor dessa mensagem.

Não são entregues como ecos: reações, edições e exclusões de mensagens (unsend). São alterações de uma mensagem existente, não mensagens novas, e são puladas silenciosamente. No WhatsApp, ecos existem apenas para números Coexistence — um canal conectado somente pela Cloud API não tem origem de mensagens externas, então message.echo nunca dispara nele.

Atenção: nunca espelhe um eco de volta na mesma conversa automaticamente. Sua resposta não geraria eco (envios da Fiwano são filtrados), mas um bot do outro lado — ou uma segunda integração que também espelha ecos — pode criar um loop. Sempre deduplique por message_id antes de agir sobre um eco.

message.delivered / message.read (todos os canais)

{
  "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.

message.failed (todos os canais)

{
  "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"}]
  }
}

Enviado quando uma mensagem que você enviou não é entregue depois que a chamada de envio retornou:

  • o envio retornou queued e a Fiwano não conseguiu concluí-lo — a Meta continuou rejeitando até as novas tentativas se esgotarem, rejeitou de forma permanente em uma nova tentativa, nunca o confirmou, ou o canal foi desconectado enquanto a mensagem aguardava;
  • no WhatsApp, também quando a Meta aceitou a mensagem e informa depois que não conseguiu entregá-la.

Um envio que retorna failed imediatamente não gera webhook — a resposta já informa isso. error é um motivo curto em texto, sempre presente. errors é um array com os detalhes completos do erro da Meta — code, title, message e error_subcode / error_data.details quando a Meta os fornece — presente só quando a falha foi causada pela Meta. message.failed é definitivo: nenhum outro status vem depois dele para o mesmo message_id.

Política de novas tentativas

Se sua webhook URL retornar um status não-2xx ou estiver inacessível, o sistema tenta novamente automaticamente:

  • 7 tentativas com backoff exponencial: 30s, 1m, 2m, 2m, 2m, 2m, 2m (~12 minutos no total)
  • Prazo rígido de 20 minutos — após o qual a entrega é marcada como permanentemente falha
  • E-mail de aviso enviado após a 3ª tentativa falha (com as novas tentativas ainda em andamento)
  • Alerta por e-mail enviado quando todas as tentativas se esgotam (falha permanente)
  • Os payloads ficam armazenados, criptografados, apenas enquanto uma entrega está em nova tentativa, e são apagados quando ela é concluída ou expira
  • No portal, a página Delivery problems do canal (aba Webhooks) lista as entregas dos últimos 7 dias que estão em nova tentativa, falharam ou se recuperaram após 3+ tentativas, com o erro do seu endpoint (os payloads nunca são exibidos)

Importante: Seu endpoint deve responder com HTTP 2xx em até 5 segundos. Respostas não-2xx ou timeouts disparam as novas tentativas acima.

Responda primeiro, processe depois. Faça o trabalho lento, como gerar uma resposta de IA, depois de retornar 2xx (em plataformas serverless, com o mecanismo de tarefas em segundo plano da plataforma). Uma resposta lenta é reenviada e entrega o mesmo evento de novo — deduplique message.received por message_id.

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.

Perfil do remetente

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:

  • Instagram — username, name, profile_pic, follower_count, is_verified_user
  • Facebook — nome de exibição em first_name; last_name e profile_pic quando disponíveis na Meta

WhatsApp não é suportado (o nome já está no webhook). Os resultados são cacheados por até 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.from pela primeira vez, depois cacheie o resultado do seu lado — não há necessidade de chamar a cada mensagem.

Perguntas frequentes

Como recebo mensagens do WhatsApp com um webhook?

Defina uma webhook_url no canal de WhatsApp conectado e habilite message.received em webhook_events. A Fiwano então envia cada mensagem recebida do WhatsApp para seu endpoint como um POST assinado com payload JSON normalizado.

Como é o payload de um webhook do WhatsApp?

O nível superior sempre inclui event, channel_id, channel_type, timestamp e data. Para mensagens de texto do WhatsApp, data inclui message_id, from, from_name, type e text. Mensagens de mídia incluem uma download_url autenticada e temporária no Pro.

O mesmo formato de webhook funciona para Instagram e Messenger?

Sim. A Fiwano normaliza WhatsApp, Instagram DM e Facebook Messenger no mesmo envelope de evento. O identificador do remetente muda por canal — número de telefone, IGSID ou PSID — mas o formato do webhook permanece consistente.

Como verifico a assinatura de um webhook da Fiwano?

Defina um webhook_secret no canal, compute HMAC-SHA256 sobre o corpo bruto da requisição com esse segredo e compare com o header X-Webhook-Signature.