---
title: "Socket.IO"
description: "Mensagem recebida, sessão que caiu, QR Code lido, saldo creditado, fatura paga: tudo chega pela mesma conexão aberta, no instante em que acontece. E pela mesma conexão você envia — sem abrir uma requisição HTTP por disparo."
lang: pt-BR
canonical: https://doc.apibrasil.io/socket-io
markdown: https://doc.apibrasil.io/socket-io.md
source: apibrasil-documentation
---

# Socket.IO

> Mensagem recebida, sessão que caiu, QR Code lido, saldo creditado, fatura paga: tudo chega pela mesma conexão aberta, no instante em que acontece. E pela mesma conexão você envia — sem abrir uma requisição HTTP por disparo.

- **Endereço:** `https://socket.apibrasil.com.br`

## A conexão

Endereço: https://socket.apibrasil.com.br
Os valores vão na QUERY da conexão, e não em cabeçalho HTTP. O handshake do Socket.IO aqui não lê `Authorization`.

**Parâmetros da query**

| Parâmetro | Tipo |  | Descrição | Onde achar |
| --- | --- | --- | --- | --- |
| `bearer` | string (JWT) | obrigatório | O mesmo Bearer Token que você usa na API. É o único parâmetro conferido na entrada: sem ele a conexão é recusada com “Token de autenticação não fornecido”, e com um token que não valida, com “Token de autenticação inválido”. | Painel da APIBrasil, ou a resposta de POST /api/v2/login. |
| `channelName` | string | obrigatório | A sala em que você entra. É por ela que o serviço decide o que te entregar, e é o seu Profile ID — o mesmo valor que o gateway usa ao publicar. Sem ele a conexão SOBE mas você não entra em sala nenhuma: nenhum evento chega, e nenhum erro é mostrado. | Profile ID, no painel da APIBrasil. |
| `deviceToken` | string | opcional | Não filtra nada e não afeta o que você recebe. Ele fica guardado e só é lido quando você emite `send`, para virar o cabeçalho `DeviceToken` da chamada ao gateway. Obrigatório apenas se você for enviar por uma API que exige dispositivo. | Token do dispositivo, na tela de dispositivos do painel. |
| `action` | string | opcional | O caminho da API que o `send` vai chamar — por exemplo `whatsapp/sendText`. Fica fixo na conexão: para enviar por duas APIs diferentes, abra duas conexões. Sem ele, o `send` devolve `{ error: “URL não fornecida” }` em `receive`. | O caminho depois de /api/v2/ na documentação da API. |

**Quando a conexão é recusada**

| Mensagem em connect_error | Causa | Correção |
| --- | --- | --- |
| `Token de autenticação não fornecido` | A query subiu sem `bearer`. | Passe o token em `query.bearer`. Ele vai na query da conexão, não no cabeçalho `Authorization` — o handshake do Socket.IO não lê cabeçalho aqui. |
| `Token de autenticação inválido` | O `bearer` chegou, mas não validou: expirado, truncado ou de outro ambiente. | Renove o token. Reconectar com o MESMO token rejeitado só repete a recusa — o painel do cliente chega a marcar o token como inválido para não entrar em laço de reconexão. |
| `Erro de configuração` | O token chegou, mas o serviço subiu sem segredo para conferi-lo. | Não é problema seu: é do lado do servidor. Abra um chamado. |

## O que chega (7)

Registre os ouvintes pelos nomes EXATOS abaixo. Não invente `message`, `status`, `qr` ou `connection`: o serviço não emite nenhum desses quatro, e quem os registrou esperou para sempre.

### `events` — Tudo que acontece nos seus dispositivos (só no seu canal)

⚠ **Ressalva:** O status de servidor chega AQUI, com `data.wook` igual a SERVER_STATUS — e não no evento `server`, apesar do nome.

**Quando dispara:** A cada webhook de dispositivo do seu Profile ID: mensagem recebida, mudança de conexão, leitura de QR Code, chamada entrando, alerta de servidor, fatura paga. É o quadro mais movimentado do canal.

**Vem de:** POST /api/webhook/{canal}, chamada pelo apibrasil-webhook (ProcessWebhookJob, alertas de uptime, retorno de pagamento).

**Campos**

| Nome | Tipo | Descrição |
| --- | --- | --- |
| `session` | string | O token do dispositivo que originou o evento — é por ele que se sabe de QUAL aparelho veio. Nem todo produtor preenche: os avisos de cobrança chegam sem `session`. |
| `channelName` | string | O seu Profile ID, repetido dentro do corpo. Útil para conferência, não para roteio. |
| `data` | object | O conteúdo do evento. A forma muda conforme o produtor — o que dá a pista é `data.wook`. |
| `data.wook` | string | O tipo do evento, em maiúsculas. É por ele que você roteia. Valores observados no código: STATUS_CONNECT, PARTICIPANTS_CHANGED, INCOMING_CALL, SERVER_STATUS, INVOICES, RECHARGE. |

**Exemplo**

```json
{
  "session": "d4f1c2e0-8b3a-4f7e-9c11-2a6d5e0b1f43",
  "channelName": "9f8e7d6c-5b4a-3210-fedc-ba9876543210",
  "data": {
    "wook": "SERVER_STATUS",
    "heartbeat": "down",
    "name": "cluster-01",
    "maxretries": "3",
    "interval": "60",
    "accepted_statuscodes": "200-299",
    "dns_resolve_server": "1.1.1.1"
  }
}
```

### `platform` — Notificações da sua conta (só no seu canal)

⚠ **Ressalva:** Roteie por `data.type`, não por `event`: `data.type` é preenchido pelo gateway em toda notificação, enquanto `event` falta em alguns produtores.

**Quando dispara:** Quando algo acontece com a conta: saldo creditado, plano ativado, fatura emitida, dispositivo criado ou removido, login, ticket respondido. É o mesmo aviso que aparece como sininho no painel.

**Vem de:** POST /api/platform/{canal}, chamada pelo gateway (SocketClient::send).

**Campos**

| Nome | Tipo | Descrição |
| --- | --- | --- |
| `event` | string | O tipo, em minúsculas e separado por ponto (`user.balance.added`). Nem sempre vem: os avisos de WhatsApp oficial publicam sem este campo. |
| `title` | string | Título pronto para exibir, já em português. |
| `message` | string | A frase completa, com os valores já formatados. |
| `data` | object | Os dados do evento, mais quatro chaves que o gateway acrescenta sempre: `type`, `action_url`, `url` e `icon`. |

**Exemplo**

```json
{
  "event": "user.balance.added",
  "title": "Saldo adicionado",
  "message": "Foi adicionado R$ 50,00 ao seu saldo. Saldo atual: R$ 173,40",
  "data": {
    "type": "user.balance.added",
    "action_url": "/financeiro",
    "url": "/financeiro",
    "icon": "/icons/wallet.png",
    "amount": 50,
    "new_balance": 173.4
  }
}
```

### `receive` — A resposta do que você enviou (só no seu canal)

⚠ **Ressalva:** Vai para a SALA, não para quem enviou. Duas abas na mesma conta recebem a resposta uma da outra — inclua um identificador seu no corpo se precisar distinguir.

**Quando dispara:** Depois de cada `send`. Traz o corpo da resposta do gateway — a mesma que você receberia chamando a API por HTTP.

**Vem de:** O próprio serviço, ao concluir a chamada disparada pelo seu `send`.

**Campos**

| Nome | Tipo | Descrição |
| --- | --- | --- |
| `(corpo da API)` | object | O corpo devolvido pelo gateway, sem envelope. Em caso de erro HTTP, o corpo do erro chega neste mesmo evento — não há um `error` separado. |

**Exemplo**

```json
{
  "error": false,
  "message": "Mensagem enviada com sucesso!",
  "response": {
    "id": "true_5531999999999@c.us_3EB0F1C2D3",
    "status": "PENDING"
  }
}
```

### `wppserver` — Eventos crus do servidor WPP (só no seu canal)

**Quando dispara:** Quando o servidor WPPConnect repassa um evento da sessão do WhatsApp.

**Vem de:** POST /api/wppconnect/{canal}.

**Campos**

| Nome | Tipo | Descrição |
| --- | --- | --- |
| `(corpo inteiro)` | object | O envelope chega completo, sem desembrulhar — ao contrário de `evolution`, que perde o envelope no caminho. |

**Exemplo**

```json
{
  "session": "d4f1c2e0-8b3a-4f7e-9c11-2a6d5e0b1f43",
  "data": {
    "wook": "STATUS_CONNECT",
    "status": "CONNECTED"
  }
}
```

### `evolution` — Eventos crus do servidor Evolution (só no seu canal)

⚠ **Ressalva:** Este evento chega SEM identificação de dispositivo. A rota repassa apenas `body.data`, e o `session` do produtor não sobrevive — com dois dispositivos Evolution no mesmo canal, não há como saber de qual veio pelo payload. Quando isso importa, escute `events`.

**Quando dispara:** Quando o servidor Evolution repassa um evento da instância.

**Vem de:** POST /api/evolution/{canal}, chamada pelo apibrasil-webhook (ProcessEvolutionWebhookJob).

**Campos**

| Nome | Tipo | Descrição |
| --- | --- | --- |
| `(conteúdo de data)` | object | Só o `data` do envelope. O `session` e o `channelName` que o produtor enviou ficam pelo caminho. |

**Exemplo**

```json
{
  "event": "messages.upsert",
  "instance": "minha-instancia",
  "data": {
    "key": {
      "remoteJid": "5531999999999@s.whatsapp.net"
    },
    "message": {
      "conversation": "oi"
    }
  }
}
```

### `server` — Difusão global (difusão global: chega a todos os conectados, de qualquer conta)

⚠ **Ressalva:** Não é o status dos servidores, apesar do nome — esse chega em `events` com `data.wook` igual a SERVER_STATUS. E como a difusão é global, nunca trate o que chegar aqui como dado da sua conta.

**Quando dispara:** Quando alguém publica em POST /api/server. Nenhum dos quatro repositórios analisados chama essa rota — na prática, este evento não dispara hoje.

**Vem de:** POST /api/server. Único caminho que NÃO usa sala: o serviço faz `io.emit`, para todos os conectados.

**Campos**

| Nome | Tipo | Descrição |
| --- | --- | --- |
| `(corpo inteiro)` | object | O que for publicado na rota, sem transformação. |

**Exemplo**

```json
{
  "status": "up",
  "ts": 1756339200
}
```

### `chat` — Alguém desconectou (difusão global: chega a todos os conectados, de qualquer conta)

⚠ **Ressalva:** Difundido para todos os conectados, não só para o seu canal — então ele dispara por desconexões de outras contas. Sem utilidade prática; está documentado para que ninguém o confunda com um evento seu.

**Quando dispara:** Sempre que QUALQUER cliente conectado ao serviço se desconecta.

**Vem de:** O próprio serviço, no `disconnect` de cada socket.

**Campos**

| Nome | Tipo | Descrição |
| --- | --- | --- |
| `(texto)` | string | A frase fixa “Cliente desconectado”. Não diz quem, nem de qual canal. |

**Exemplo**

```json
{
  "(payload)": "Cliente desconectado"
}
```

## O que você envia

O socket NÃO implementa envio: ele repassa o que você emite para https://gateway.apibrasil.io/api/v2, pondo o seu Bearer e o seu DeviceToken nos cabeçalhos. A resposta volta no evento `receive`.

Qualquer caminho de /api/v2/ que aceite POST serve como action — whatsmeow, waba e as APIs de consulta inclusive. A lista ao lado é a das mais usadas, não a das permitidas.

| Ação |  | Motor |  |
| --- | --- | --- | --- |
| `whatsapp/sendText` | Texto pelo WhatsApp (WPP) | WPPConnect | exige DeviceToken |
| `whatsapp/sendFile` | Arquivo pelo WhatsApp (WPP) | WPPConnect | exige DeviceToken |
| `evolution/message/sendText` | Texto pelo Baileys (Evolution) | Evolution API | exige DeviceToken |
| `evolution/message/sendMedia` | Arquivo em Base64 (Evolution) | Evolution API | exige DeviceToken |

**Corpo do send — `whatsapp/sendText`**: Mensagem de texto simples pelo motor WPP.

```json
{
  "number": "5531999999999",
  "text": "Sua entrega saiu para o endereço cadastrado.",
  "time_typing": 0,
  "options": {
    "createChat": true
  }
}
```

**Corpo do send — `whatsapp/sendFile`**: Mídia ou documento por URL pública, pelo motor WPP.

```json
{
  "number": "5531999999999",
  "path": "https://exemplo.com.br/nota-fiscal.pdf"
}
```

**Corpo do send — `evolution/message/sendText`**: Mensagem de texto pelo motor Baileys, com controle de digitação.

```json
{
  "number": "5531999999999",
  "options": {
    "delay": 1200,
    "presence": "composing"
  },
  "textMessage": {
    "text": "Sua entrega saiu para o endereço cadastrado."
  }
}
```

**Corpo do send — `evolution/message/sendMedia`**: Mídia enviada como Base64, para quando o arquivo não tem URL pública. O conteúdo trafega inteiro pela conexão.

```json
{
  "number": "5531999999999",
  "options": {
    "delay": 1200,
    "presence": "composing"
  },
  "mediaMessage": {
    "mediatype": "image",
    "caption": "Comprovante da entrega",
    "media": "/9j/4AAQSkZJRgABAQAAAQABAAD…"
  }
}
```

## Armadilhas: o que quebra sem avisar

Estes comportamentos foram apurados no código do serviço. Em todos eles o sintoma aponta para o lugar errado, e é por isso que estão aqui:

1. **Falha de rede no envio não devolve resposta nenhuma** (gravidade alta)
   Quando a chamada ao gateway falha por REDE — recusa de conexão, tempo esgotado —, o tratamento de erro do serviço tenta ler um corpo de resposta que não existe e estoura antes de emitir. Nenhum `receive` chega, nem de erro. O sintoma é um envio que fica pendurado para sempre. Sempre coloque um tempo-limite do seu lado; não espere `receive` indefinidamente.
2. **Conectar sem channelName parece funcionar** (gravidade alta)
   A conexão é aceita, o cliente marca “conectado”, e você não entra em sala nenhuma — nada chega, e nenhum erro aparece. Se o socket conectou e o silêncio dura, o primeiro suspeito é o `channelName`.
3. **As respostas vão para a sala inteira** (gravidade alta)
   O `receive` é entregue ao canal, não a quem enviou. Duas abas abertas na mesma conta recebem a resposta uma da outra. Se você casa pedido com resposta, ponha um identificador seu no corpo do `send` e confira na volta.
4. **Uma conexão serve a uma ação só** (gravidade média)
   O `action` é lido do handshake, não do `send`. Trocar de API significa abrir outra conexão — mandar um corpo de `sendFile` numa conexão aberta com `sendText` chama `sendText` com o corpo errado.
5. **O quadro evolution não diz de qual dispositivo veio** (gravidade média)
   A rota repassa apenas o `data` do envelope, e a identificação do dispositivo fica pelo caminho. Com mais de um dispositivo Evolution no mesmo canal, use `events` — lá o `session` chega.
6. **subscribe troca de sala, não acrescenta uma** (gravidade média)
   Emitir `subscribe` faz o serviço SAIR do canal atual antes de entrar no novo. Não dá para ouvir dois canais na mesma conexão; para isso, abra duas.

## Destinos com página própria

- [Servidor](https://doc.apibrasil.io/socket-io/server.md): O evento `server` existe no serviço, mas nenhum produtor foi encontrado nos repositórios — ele não dispara hoje. O status de servidor que você procura chega em `events`.
- [Plataforma](https://doc.apibrasil.io/socket-io/platform.md): As notificações da sua conta em tempo real: saldo, planos, faturas, dispositivos, login e tickets. É o que o painel usa para acender o sininho.
- [Enviar texto (WPP)](https://doc.apibrasil.io/socket-io/send-text-wpp.md): Disparar mensagem de texto pelo motor WPP sem abrir uma requisição HTTP por envio.
- [Enviar arquivos (WPP)](https://doc.apibrasil.io/socket-io/send-files-wpp.md): Enviar mídia ou documento por URL pública, pelo motor WPP.
- [Enviar texto (Baileys)](https://doc.apibrasil.io/socket-io/send-text-baileys.md): Disparar mensagem de texto pelo motor Baileys, com controle de presença e atraso.
- [Enviar arquivos (Base64)](https://doc.apibrasil.io/socket-io/send-files-base64.md): Enviar arquivo sem URL pública, embutido em Base64 no próprio corpo — útil para conteúdo gerado na hora.
- [Todos os dispositivos](https://doc.apibrasil.io/socket-io/all-devices.md): Acompanhar TODOS os dispositivos do seu Profile ID numa conexão só. Cada quadro traz `session` com o token do dispositivo de origem.
- [Dispositivo único](https://doc.apibrasil.io/socket-io/unique-device.md): Receber apenas os eventos de UM dispositivo. O filtro não está na conexão: o serviço emite um quadro cujo NOME é o token do dispositivo, então você escuta esse nome.

## Ver também

- Índice da documentação: https://doc.apibrasil.io/llms.txt
- Versão HTML desta página: https://doc.apibrasil.io/socket-io
