Webhooks

Webhooks permitem que sua aplicacao receba eventos em tempo real do WhatsApp. Quando algo acontece (mensagem recebida, conexao alterada, etc.), a NeoGo envia um POST HTTP para a URL configurada.

Configuracao

A URL de webhook e definida na criacao da instancia:

curl -X POST http://localhost:4000/instance/create \
  -H "apikey: SUA_CHAVE_GLOBAL" \
  -H "Content-Type: application/json" \
  -d '{
    "instanceName": "producao",
    "webhookUrl": "https://meusite.com/webhook"
  }'

Tipos de Evento

A NeoGo emite 13 tipos de eventos. Voce pode filtrar eventos especificos ou receber todos (ALL).

EventoDescricaoQuando dispara
MESSAGEMensagem recebidaQuando a instancia recebe uma mensagem de texto, midia, etc.
SEND_MESSAGEMensagem enviadaConfirmacao de que uma mensagem foi enviada com sucesso
READ_RECEIPTConfirmacao de leituraQuando o destinatario leu a mensagem (ticks azuis)
PRESENCEPresenca onlineQuando um contato fica online/offline
HISTORY_SYNCSincronizacao de historicoDurante a sincronizacao inicial de mensagens
CHAT_PRESENCEPresenca no chatQuando alguem esta digitando ou gravando audio
CALLChamadaChamada recebida (aceita, rejeitada, perdida)
CONNECTIONStatus de conexaoQuando a instancia conecta, desconecta ou perde conexao
LABELLabelsQuando uma label e adicionada ou removida
CONTACTContatosAtualizacoes de contatos
GROUPGruposMudancas em grupos (participantes, config, etc.)
NEWSLETTERNewslettersAtualizacoes de canais/newsletters
QRCODEQR CodeQuando um novo QR code e gerado para conexao

Formato do Payload

Todos os eventos seguem o mesmo formato base:

{
  "event": "MESSAGE",
  "instanceId": "abc-123",
  "instanceName": "producao",
  "data": {
    // dados especificos do evento
  }
}

Exemplo: Mensagem Recebida

{
  "event": "MESSAGE",
  "instanceId": "abc-123",
  "instanceName": "producao",
  "data": {
    "key": {
      "remoteJid": "5511999999999@s.whatsapp.net",
      "fromMe": false,
      "id": "3EB0A1B2C3D4E5F6"
    },
    "pushName": "Joao Silva",
    "message": {
      "conversation": "Ola, tudo bem?"
    },
    "messageTimestamp": 1714924800
  }
}

Exemplo: Status de Conexao

{
  "event": "CONNECTION",
  "instanceId": "abc-123",
  "instanceName": "producao",
  "data": {
    "status": "CONNECTED",
    "number": "5511999999999"
  }
}

Exemplo: QR Code

{
  "event": "QRCODE",
  "instanceId": "abc-123",
  "instanceName": "producao",
  "data": {
    "qrcode": "data:image/png;base64,iVBORw0KGgo..."
  }
}

Verificacao HMAC

Para garantir que os webhooks vem realmente da NeoGo (e nao de um atacante), cada requisicao inclui uma assinatura HMAC-SHA256 no header X-Webhook-Signature.

Como verificar (Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(JSON.stringify(payload));
  const expected = hmac.digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// No handler do webhook:
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const isValid = verifyWebhookSignature(
    req.body,
    signature,
    'SEU_WEBHOOK_SECRET'
  );

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  // Processar evento...
  console.log('Evento:', req.body.event);
  res.sendStatus(200);
});

Como verificar (Python)

import hmac
import hashlib
import json

def verify_webhook(payload, signature, secret):
    expected = hmac.new(
        secret.encode(),
        json.dumps(payload).encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

# No handler:
@app.post("/webhook")
def webhook(request):
    signature = request.headers.get("X-Webhook-Signature")
    if not verify_webhook(request.json, signature, "SEU_WEBHOOK_SECRET"):
        return {"error": "Invalid signature"}, 401

    event = request.json
    print(f"Evento: {event['event']}")
    return {"ok": True}

Como verificar (Go)

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "io"
    "net/http"
)

func verifySignature(body []byte, signature, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(body)
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(signature), []byte(expected))
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    signature := r.Header.Get("X-Webhook-Signature")

    if !verifySignature(body, signature, "SEU_WEBHOOK_SECRET") {
        http.Error(w, "Invalid signature", http.StatusUnauthorized)
        return
    }

    var event map[string]interface{}
    json.Unmarshal(body, &event)
    // Processar evento...
    w.WriteHeader(http.StatusOK)
}

Sistema de Retry (Dead-Letter)

Se o seu endpoint retornar erro (status 4xx/5xx) ou nao responder, a NeoGo armazena o evento no banco de dados como "dead letter" para reenvio posterior.

Gerenciamento de Dead Letters

# Listar dead letters de uma instancia
curl http://localhost:4000/instance/webhook-dead-letters/INSTANCE_ID \
  -H "apikey: SUA_CHAVE_GLOBAL"

# Reenviar um dead letter especifico
curl -X POST http://localhost:4000/instance/webhook-dead-letters/retry/DEAD_LETTER_ID \
  -H "apikey: SUA_CHAVE_GLOBAL"
Dica: Retorne 200 OK o mais rapido possivel no endpoint de webhook. Processe o evento de forma assincrona (fila, worker, etc.) para evitar timeouts.

Canais Alternativos de Eventos

Alem de webhooks HTTP, a NeoGo suporta outros canais para receber eventos:

CanalConfiguracaoUso
RabbitMQRABBITMQ_URLFilas por instancia + fila global. Auto-reconexao.
Redis Pub/SubREDIS_URL + REDIS_GLOBAL_EVENTS=truePub/Sub distribuido. Substitui NATS.
NATSNATS_URLMensageria leve. Alternativa ao Redis.
WebSocketConectar em /wsTempo real no navegador.

WebSocket

Conecte via WebSocket para receber eventos em tempo real no navegador ou em clientes persistentes:

// JavaScript
const ws = new WebSocket(
  'ws://localhost:4000/ws?token=TOKEN_DA_INSTANCIA&instanceId=INSTANCE_ID'
);

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Evento:', data.event);
  console.log('Dados:', data.data);
};

ws.onclose = () => {
  console.log('Conexao encerrada, reconectando...');
  // implementar reconexao
};

Boas Praticas