Eventos

Os eventos avisam o que acontece na inbox, na hora: mensagem nova, mudança de status, edição, reação. Eles chegam por GET /events, uma conexão aberta (SSE), e pelo webhook, com os mesmos eventos e o mesmo formato.

Conectar

curl -N "https://<subdomínio>.orkesio.com/events?events=message.received,message.status" \
  -H "token: <token da inbox>"
  • events: os eventos que você quer, separados por vírgula. Sem events, chegam todos, menos status_post.received (os Status de todos os contatos), newsletter.updated e newsletter.message (os canais) e history.batch (o histórico em lotes), que só vêm pedidos pelo nome.
  • Só os nomes da Orkesio (com ponto). Um nome da uazapi (messages) responde 400: para os eventos da uazapi, use o /sse, que continua igual.
  • exclude: o mesmo filtro do webhook, com os mesmos valores. exclude=sent_by_api tira as mensagens enviadas pela api. Os valores: sent_by_api, not_sent_by_api, out, in, groups e not_groups. Valor que não existe, ou in junto com out, responde 400.
  • Ao conectar, chega a linha : connected (um comentário do SSE, que o EventSource do navegador ignora).

Com o EventSource (navegador), cada evento chega no onmessage, e o tipo está dentro do JSON. O EventSource não manda headers; o token pode ir na URL (?token=), mas aí ele fica no histórico e nos logs. Prefira conectar pelo seu servidor e repassar ao navegador.

const source = new EventSource("https://<subdomínio>.orkesio.com/events?events=message.received&token=<token>");
source.onmessage = (e) => {
  const event = JSON.parse(e.data);
  if (event.type === "message.received") mostrar(event.data);
};

O formato

Cada evento chega como duas linhas do SSE: o id do evento e o data, um JSON com o envelope:

id: evt_01JAB3K9ZQ7V0M4T8R2X6WYD5H
data: {"id":"evt_01JAB3K9ZQ7V0M4T8R2X6WYD5H","type":"message.received","created_at":"2026-10-17T12:00:00.000Z","inbox":{...},"data":{...}}
CampoO que é
idId do evento: evt_ + 26 letras, em ordem de criação. Use para descartar repetidos.
typeO evento (message.received, message.status...).
created_atQuando o evento aconteceu (ISO 8601, UTC).
inboxA inbox: id, name, phone (o número, só no WhatsApp), account_id (a Página, a conta ou o bot, nos outros canais) e channel (whatsapp_web, whatsapp_cloud, messenger, instagram ou telegram).
dataO conteúdo, com formato fixo por type (tabela abaixo).

O envelope é o mesmo do webhook: quem trata um, trata o outro.

Os eventos de mensagem

EventoQuando chegadata
message.receivedO contato mandou uma mensagem.message (a mensagem), chat (a conversa, com o lead_id) e lead (só em conversa direta)
message.sentUma mensagem da inbox saiu: pela API, por automação, pelo celular ou por outro aparelho (sent_by diz qual).message, chat e lead, como em message.received
message.statusUma mensagem da inbox foi entregue, lida, ouvida ou falhou.chat, status, at e messages (os ids); error na falha
message.failedSó as falhas: uma mensagem da inbox não saiu (recusada, fora do prazo, cancelada ou da campanha). Para quem só quer saber dos problemas.message (com status: failed e o error) e chat (id, lid, type, name)
group.receiptsParticipantes de um grupo receberam ou leram uma mensagem da inbox. Em lote, a cada 60 segundos.chat, message e receipts (quem, o quê, quando)
message.updatedUma mensagem mudou: editada, apagada, fixada, desafixada, transcrita ou com a mídia pronta.change (o que mudou), message (já com a mudança) e chat
message.reactionAlguém reagiu a uma mensagem, ou tirou a reação.message (a reação, como mensagem com type: reaction) e chat

Os demais eventos

EventoQuando chegadata
chat.updatedUma conversa mudou: arquivada, fixada, silenciada, etiquetas, não lidas, automação, efêmeras, bloqueio, nome. Também quando a conversa aparece pela primeira vez (changes vazio). Para quem recebe o message.received e o message.sent, a mensagem nova não gera este evento só pelas não lidas e pela vez: elas vêm no data.chat deles.chat (o estado novo) e changes (o que mudou)
chat.presenceO contato ficou online ou offline, ou está digitando, gravando áudio ou parou.chat, state; no grupo, participant; last_seen_at no offline, quando o contato mostra
inbox.statusA conexão da inbox mudou: conectando, QR code ou código de pareamento pronto, conectada, desconectada, deslogada ou banida.status; qr_code ou pair_code para mostrar; reason e banned_until na queda
label.updatedUma etiqueta do WhatsApp Business foi criada, editada ou apagada.action, id, name, color (na exclusão, só action e id)
call.updatedLigação tocando, atendida, recusada, encerrada ou perdida.a ligação: id, chat, author, direction, status, at; video quando se sabe e duration_seconds na encerrada
status_post.receivedUm contato postou um Status. Só chega pedido pelo nome.message (a mensagem do Status)
newsletter.updatedAs visualizações ou reações dos posts de um canal mudaram, depois de POST /newsletters/{newsletter_id}/live-updates e enquanto a assinatura vale. Só chega pedido pelo nome.newsletter_id e posts (server_id, reactions e views só quando muda)

O que não chega aqui:

  • Registros do grupo e do sistema (alguém entrou, o nome mudou) não chegam como message.received, para não disparar automação que espera uma mensagem de verdade. Eles ficam na busca (category=event).
  • Publicações de Status (status@broadcast) não chegam nos eventos de mensagem: chegam em status_post.received, pedido pelo nome.
  • O recibo de leitura das mensagens recebidas (quando você as lê pelo celular) não chega em message.status, que é só das mensagens da inbox.

Ordem, repetição e quedas

  • Ordem: os eventos de uma inbox saem na ordem em que acontecem.
  • Repetição: use o id do evento para descartar um repetido.
  • Sem reenvio: se a conexão cair, os eventos desse intervalo não são reenviados. Ao reconectar:
    • mensagens novas: busque com GET /messages usando after com a hora do último evento recebido;
    • mudanças em mensagens antigas (status, edição, reação, apagada): o after filtra pela hora em que a mensagem foi enviada, não pela hora da mudança. Uma mensagem enviada às 10h e lida às 12h, durante a queda, não volta em after=12h. Para elas, consulte de novo as que ainda importam (por exemplo, as suas enviadas ainda sem read, com id ou com direction=out no período).
  • Cliente lento: cada conexão tem uma fila curta. Se o cliente não lê, os eventos que não cabem são descartados; leia sem bloquear e processe à parte.

Webhook

O webhook entrega os mesmos eventos, com o mesmo envelope, por POST no seu endereço. As rotas ficam em /webhooks; os webhooks da uazapi (/webhook) continuam iguais e separados.

Criar

POST /webhooks
{
  "name": "CRM",
  "url": "https://crm.exemplo.com/whatsapp",
  "events": ["message.received", "message.status"],
  "retries": 3,
  "headers": { "Authorization": "Bearer seu-token" }
}
  • Até 5 webhooks por inbox.
  • events: os nomes da Orkesio (com ponto). O formato da entrega é o envelope desta página.
  • url: https:// (recomendado) ou http://, sempre em endereço público. Mandar de novo uma url que já existe atualiza aquele webhook (200), sem criar outro.
  • headers: até 5, enviados em toda entrega, para o seu destino autenticar. Nas respostas, o valor volta como ***.
  • A resposta da criação (201) traz o secret inteiro, só nela (e quando você o troca com rotate_secret): guarde-o. Na atualização pela mesma url (200) ele vem mascarado e o que você guardou continua valendo.

Webhook global

O dono do servidor (header admintoken) tem um webhook global, em /server/webhook, com os mesmos campos e a mesma entrega. Ele recebe os eventos de todas as inboxes do servidor; inbox (no envelope) diz de qual veio cada evento, e as falhas guardadas trazem a inbox em inbox_id.

O que chega

Cada evento é um POST com o envelope no corpo (Content-Type: application/json) e três headers do padrão Standard Webhooks:

HeaderO que é
Webhook-IdO id do evento (o mesmo id do envelope). Igual nas novas tentativas: use para descartar repetidos.
Webhook-TimestampQuando a entrega saiu (segundos Unix). Recuse entregas muito antigas (por exemplo, mais de 5 minutos).
Webhook-Signaturev1, e o HMAC-SHA256, em base64, de <Webhook-Id>.<Webhook-Timestamp>.<corpo>, com a chave do secret (a parte depois de whsec_, decodificada de base64).

Conferir a assinatura prova que a entrega saiu da Orkesio e que o corpo não mudou. O passo a passo, com exemplos em Node, Python e PHP e um caso de teste, está em Conferir a assinatura (HMAC).

Responder

Responda 2xx rápido, depois de registrar o evento, e processe à parte. O corpo da sua resposta é ignorado.

Quando a entrega falha

  • Novas tentativas: com retries (até 3), a entrega é repetida depois de 10 s, 1 min e 5 min, só quando não há risco de chegar em dobro: a conexão nem abriu, ou o destino respondeu 429, 502 ou 503. Tempo esgotado, 500 e 504 não repetem, porque o destino pode ter processado o evento.
  • Falhas guardadas: a entrega de message.received ou message.sent que falhou de vez fica 24 horas em GET /webhooks/{webhook_id}/failures. A falha dos outros eventos conta só em health.
  • Reenvio: POST /webhooks/resends reenvia as de mensagem, montadas de novo a partir da mensagem salva, com o mesmo Webhook-Id.
  • Desligamento: depois de 24 horas sem nenhuma resposta boa (com pelo menos 10 falhas), a API desliga o webhook e marca health.disabled_at. Salvar de novo (PATCH) religa.
  • Saúde: cada webhook traz health, com o tempo de resposta do destino, as falhas em seguida e o último erro.
  • Teto de espera: cada webhook guarda até 200 novas tentativas aguardando o intervalo (10 s, 1 min, 5 min). Com o destino fora do ar e muitos eventos, o que passa disso falha de vez na hora.
  • Reinício do servidor: as novas tentativas que estavam esperando se perdem e não ficam nas falhas guardadas.

Repetidos

A entrega é best effort: um evento pode se perder (resposta 500, tempo esgotado, teto de espera ou reinício, como acima) e pode chegar duas vezes (um reenvio das falhas guardadas, ou um 502/503 devolvido por um proxy depois de o destino já ter processado). Guarde o Webhook-Id dos últimos eventos e ignore o que já viu; para não perder dado, confira pelas rotas de leitura o que importa.

Vindo do webhook da uazapi

O GET /webhooks também lista os webhooks da uazapi (format: "v1", eventos em v1_events). Por aqui, eles só ligam e desligam (PATCH com enabled) e se apagam; o resto se altera no /webhook. Para passar a receber o formato da Orkesio:

  1. Crie um webhook com POST /webhooks e os eventos da Orkesio. Pode ser o mesmo link do webhook da uazapi, com um caminho ou header que diga o formato.
  2. Confira que as entregas chegam e que a assinatura bate.
  3. Desligue o da uazapi (PATCH com enabled: false) ou apague. Enquanto os dois estiverem ligados, cada evento chega duas vezes, uma em cada formato.