Eventos em Tempo Real (SSE/WebSocket)

16. Eventos em Tempo Real (SSE/WebSocket)#

Receba eventos em tempo real sem polling. Ideal para dashboards e chatbots.

GET/v1/instances/{instanceId}/events (SSE)#

Server-Sent Events stream.

Auth: Header Authorization: Bearer <jwt> ou X-API-Key: <key>

Empresa ativa (mesma regra da seção 1 - Autenticação): com JWT, a cada conexão o servidor consulta o master DB: empresa inexistente → 403 (company not found), suspensa → 403 (company suspended), falha de banco nessa consulta → 503 (service unavailable). Com API key, empresa suspensa → 403; token inválido → 401.

Query Parameters:

Parametro Tipo Descricao
events string Filtro de eventos (separados por virgula). Ex: message.received,connection.update
last_event_id string Cursor opcional para replay curto. Se informado, o servidor reenviara eventos buffered posteriores a esse ID

Resume/Replays: o SSE também aceita o header Last-Event-ID. O hub mantem um buffer em memória com os 100 eventos mais recentes por instância para retomada após desconexoes curtas.

Limite por instância: máximo de 20 conexões SSE/WebSocket simultaneas por instância. Excesso retorna 429 Too Many Requests.

Formato dos eventos:

text
event: connected
data: {"instance_id": "84c2e480-..."}

event: message.received
id: evt_1234
data: {"event_id":"550e8400-...","type":"message.received","instance_id":"84c2e480-...","timestamp":"2026-03-07T15:30:45.123Z","data":{...}}

: heartbeat

Evento live-only do Audit Timeline: quando o worker grava uma linha em stealth_audit_events, ele pública um evento SSE/WS stealth.audit_event no mesmo canal Redis por instância. Esse evento existe para refresh imediato da UI e não entra em webhooks nem em GET /events/history.

Payload stealth.audit_event:

json
{
  "id": 42,
  "instance_id": "84c2e480-...",
  "event_type": "connection.opened",
  "event_data": {
    "proxy_city": "sao-paulo",
    "spec": "hello_chrome_120"
  },
  "occurred_at": "2026-04-20T22:30:00Z",
  "created_at": "2026-04-20T22:30:00Z"
}

Vocabulario controlado de event_type (v1):

  • fingerprint.assigned
  • fingerprint.rotated
  • warmup.phase_changed
  • warmup.bypass_granted
  • warmup.bypass_expired
  • connection.opened
  • connection.closed
  • connection.banned
  • hygiene.applied
  • humanize.applied
  • stealth.inbound_first.blocked
  • stealth.app_version_canary_started
  • stealth.app_version_fleet_applied
  • stealth_transport.debug_enabled
  • stealth_transport.debug_renewed
  • stealth_transport.debug_expired

Notas warmup:

  • warmup.phase_changed e gravado de forma transacional junto com o UPDATE tenant_instances.warmup_phase.
  • warmup.bypass_granted e emitido por POST /v1/admin/instances/{instanceId}/warmup/bypass e carrega reason, ticket_id, token e expires_at dentro de event_data.
  • hygiene.applied e emitido uma vez por alteração bem-sucedida (push_name, photo, status) e carrega type + value dentro de event_data.

Exemplo com curl:

bash
curl -N \
  -H "Authorization: Bearer eyJ..." \
  "https://api.catcher.one/v1/instances/84c2e480/events?events=message.received,connection.update"

Exemplo com JavaScript (fetch streaming):

javascript
const response = await fetch(
  'https://api.catcher.one/v1/instances/84c2e480/events?events=message.received',
  {
    headers: { Authorization: `Bearer ${token}` }
  }
);
// Leia response.body como ReadableStream e parseie o protocolo SSE.

GET/v1/instances/{instanceId}/events/history#

Histórico de eventos persistidos com paginação baseada em cursor.

Auth: Bearer JWT ou API Key

Query Parameters:

Parametro Tipo Descricao
limit int Máximo de eventos (1-200, padrão 50)
before string Cursor: event_id para buscar eventos anteriores (DESC)
after string Cursor: event_id para buscar eventos posteriores (ASC, catch-up)
since string Timestamp RFC3339 para buscar eventos a partir de (ASC)
types string Filtro por tipo(s) de evento, separados por virgula. Ex: message.received,message.sent
contains string Busca por substring em qualquer campo principal do evento: event_id, type, instance_id, timestamp e no JSON data (chaves e valores). Ex: contains=ABC123 encontra todos eventos que referenciam o WhatsApp message ID ABC123; contains=message.sent filtra por tipo; contains=2026-04-23 encontra eventos daquela data

Resposta 200:

json
{
  "events": [
    {
      "event_id": "ca5e3b45-055a-4ccc-be37-cb9d76d25887",
      "type": "message.received",
      "instance_id": "be23db3e-900d-4194-98ca-7a1deb2157a5",
      "timestamp": "2026-04-04T17:34:55.965Z",
      "data": {
        "message_ids": ["ABC123"],
        "chat": "5511999999999@s.whatsapp.net",
        "from": "5511999999999@s.whatsapp.net",
        "type": "text",
        "content": "Ola!"
      }
    }
  ],
  "has_more": true
}

Correlação de eventos com mensagens: Use contains com o whatsapp_id da mensagem para encontrar todos os eventos relacionados (received, sent, delivered, read, deleted, edited).

Busca paginada: quando contains estiver presente, a paginação (before / after) continua sendo aplicada sobre o conjunto filtrado. Exemplo: limit=50&contains=oi retorna os 50 eventos mais recentes que contenham oi; o próximo before=<ultimo_event_id> retorna os 50 matches seguintes, não apenas os próximos 50 eventos brutos.


GET/v1/instances/{instanceId}/events/stats#

Estatísticas agregadas dos eventos de uma instância.

Auth: Bearer JWT ou API Key

Resposta 200:

json
{
  "total_count": 1523,
  "count_by_type": {
    "message.received": 450,
    "message.sent": 320,
    "message.delivered": 310,
    "message.read": 280
  },
  "latest_timestamp": "2026-04-04T17:34:55.965Z"
}

GET/v1/instances/{instanceId}/ws (WebSocket)#

Conexão WebSocket para eventos em tempo real.

Auth: Header Authorization ou X-API-Key (mesma regra do SSE).

Query Parameters: events e last_event_id. A autenticação não aceita mais token em query string.

Comportamento:

  • Upgrade para WebSocket (101 Switching Protocols)
  • Somente leitura (servidor envia, cliente recebe)
  • Ping a cada 30s para manter a conexão
  • Mensagens em formato JSON
  • Replay inicial opcional a partir de last_event_id
  • Se o limite de 20 subscribers por instância for excedido, a API responde 429 antes do upgrade

Exemplo com Node.js (ws com headers):

javascript
const WebSocket = require('ws');

const ws = new WebSocket(
  'wss://api.catcher.one/v1/instances/84c2e480/ws?events=message.received',
  {
    headers: { Authorization: `Bearer ${token}` }
  }
);

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