# Evolution GO ASAFE — guia da API para agentes de IA

API REST de WhatsApp. Cada **instância** é um número de WhatsApp conectado.
Base URL: https://evoasafe.duckdns.org
Especificação OpenAPI completa: https://evoasafe.duckdns.org/openapi.yaml
Documentação interativa: https://evoasafe.duckdns.org/docs

Este documento é tudo que você precisa para usar a API: criar e conectar instâncias,
enviar mensagens e receber eventos por webhook, seja chamando a API direto, montando
uma automação (n8n, Make, Zapier) ou integrando no código de um projeto.

## Instruções para você (agente de IA)

- **Chaves**: este documento não contém nenhuma chave. Peça ao usuário:
  o **token da instância** para enviar mensagens e configurar webhook, ou a
  **GLOBAL_API_KEY** se precisar criar, listar ou excluir instâncias.
- **Nunca coloque a chave em código de frontend** (navegador, app mobile). Chame a API
  pelo backend ou pela automação e guarde a chave em variável de ambiente
  (ex: `EVOLUTION_API_KEY`), nunca no repositório.
- **Antes de enviar**, confira se a instância está `connected` (`GET /instance/{name}`).
  Se estiver `disconnected` ou `qrcode`, o usuário precisa conectar o número primeiro
  (QR code ou código de pareamento). Mostre o QR (`qrcode` é uma imagem PNG em data URL)
  ou mande o usuário abrir o painel em https://evoasafe.duckdns.org/manager.
- **Ao receber webhooks**, ignore eventos com `data.key.fromMe: true`, senão o bot
  responde às próprias mensagens em loop.
- Respeite limites: nada de disparo em massa ou spam. O número pode ser banido.

## Autenticação

Todas as rotas (exceto /health, /docs, /openapi.yaml e /llms.txt) exigem o header:

    apikey: <CHAVE>

Também aceita `Authorization: Bearer <CHAVE>`. Há dois tipos de chave:

- **GLOBAL_API_KEY**: acessa tudo, inclusive criar, listar e excluir instâncias.
- **Token da instância** (campo `token` devolvido ao criar): acessa só as rotas `/instance/{name}/...`, `/message/{name}/...` e `/chat/{name}/...` daquela instância. Prefira esse token em integrações.

As chaves NÃO estão neste documento. Peça ao usuário a chave adequada.

## Fluxo típico

1. Criar instância (GLOBAL_API_KEY): `POST /instance` com `{"name":"vendas"}` → guarde `token`.
2. Conectar: `POST /instance/vendas/connect` → resposta com `status: "qrcode"` e `qrcode` (imagem PNG em data URL).
3. O usuário lê o QR no celular (WhatsApp → Aparelhos conectados → Conectar aparelho).
   O QR renova a cada ~20s; consulte `GET /instance/vendas` a cada 2–3s até `status: "connected"`.
   Alternativa sem QR: `POST /instance/vendas/pairing-code` com `{"phone":"5511999999999"}` → `pairingCode` para digitar no celular.
4. Enviar mensagens com `POST /message/vendas/text`.
5. Receber mensagens configurando um webhook: `PUT /instance/vendas/webhook`.

Depois de pareada, a instância reconecta sozinha quando o servidor reinicia.

## Status da instância

`disconnected` | `connecting` | `qrcode` (aguardando leitura do QR) | `connected`.
Só é possível enviar mensagens com `connected`.

## Endpoints

| Método | Rota | Chave | Corpo | Retorno |
|---|---|---|---|---|
| GET | /health | — | — | `{status, version, events}` |
| GET | /instance | global | — | lista de Instance |
| POST | /instance | global | `{name, webhookUrl?, webhookEvents?}` | Instance (201) |
| GET | /instance/{name} | ambas | — | Instance |
| DELETE | /instance/{name} | global | — | `{deleted:true}` |
| POST | /instance/{name}/connect | ambas | — | Instance |
| POST | /instance/{name}/pairing-code | ambas | `{phone}` | `{pairingCode}` |
| POST | /instance/{name}/logout | ambas | — | `{loggedOut:true}` |
| PUT | /instance/{name}/webhook | ambas | `{url, enabled, events}` | Instance |
| POST | /message/{name}/text | ambas | `{number, text}` | SendResult (201) |
| POST | /message/{name}/media | ambas | `{number, mediatype, media, caption?, fileName?, mimetype?, ptt?}` | SendResult (201) |
| POST | /chat/{name}/check-numbers | ambas | `{numbers:[...]}` | `[{number, exists, jid}]` |

Instance:

```json
{
  "id": "uuid", "name": "vendas", "token": "A3BB...", "jid": "5511999999999:12@s.whatsapp.net",
  "webhookUrl": "", "webhookEnabled": false, "webhookEvents": [],
  "createdAt": "2026-10-28T18:12:32Z",
  "status": "connected", "phone": "5511999999999", "pushName": "Loja",
  "qrcode": "data:image/png;base64,... (só com status qrcode)", "code": "(só com status qrcode)"
}
```

SendResult: `{"id": "3EB0...", "to": "5511999999999@s.whatsapp.net", "timestamp": 1793199600}`

## Regras importantes

- **Número**: DDI + DDD + número, só dígitos, ex: `5511999999999`. A API consulta o WhatsApp e resolve o JID certo (inclusive o nono dígito do Brasil). Se o número não tiver WhatsApp a resposta é 400. Para grupos use o JID completo (`1203630...@g.us`).
- **Mídia**: `mediatype` = `image` | `video` | `audio` | `document`. `media` = URL `https://...` ou base64. Máx 64 MB. `caption` vale para image, video e document. `fileName` é o nome exibido do documento. Para mensagem de voz use `audio` + `"ptt": true` com arquivo OGG/Opus.
- **Nome da instância**: 1–50 caracteres, letras, números, `-` e `_`.
- Evite disparos em massa: a conexão usa o protocolo do WhatsApp Web e o número pode ser banido.

## Erros

Sempre JSON `{"error": "mensagem"}`.
400 dados inválidos / instância não conectada / número sem WhatsApp · 401 chave inválida · 404 instância não existe · 409 nome já usado · 500 erro interno.

## Webhooks

Eventos: `QRCODE_UPDATED`, `CONNECTION_UPDATE`, `MESSAGES_UPSERT` (mensagem nova, recebida ou enviada), `MESSAGES_UPDATE` (recibo de entrega/leitura). Lista `events` vazia = todos.
Cada evento é um POST JSON para a URL configurada (até 3 tentativas):

```json
{
  "event": "MESSAGES_UPSERT",
  "instance": "vendas",
  "jid": "5511999999999:12@s.whatsapp.net",
  "dateTime": "2026-10-28T15:00:00-03:00",
  "data": {
    "key": { "id": "3EB0...", "remoteJid": "5511888888888@s.whatsapp.net", "fromMe": false, "participant": "5511888888888@s.whatsapp.net" },
    "pushName": "Fulano",
    "isGroup": false,
    "messageType": "text",
    "mediaType": "",
    "text": "Oi!",
    "messageTimestamp": 1793199600,
    "message": { "conversation": "Oi!" }
  }
}
```

- `data.text` traz o texto já extraído (texto simples, texto com link ou legenda de mídia).
- Para responder, use `data.key.remoteJid` como `number` em `/message/{name}/text`.
- Ignore eventos com `data.key.fromMe: true` para não responder às próprias mensagens.
- CONNECTION_UPDATE: `data = {state: "open"|"close", phone?, pushName?, reason?}`.
- MESSAGES_UPDATE: `data = {ids, remoteJid, sender, type, timestamp}`; `type` "" = entregue, "read" = lida.
- QRCODE_UPDATED: `data = {code, base64}`.

## Exemplos

```bash
curl -X POST https://evoasafe.duckdns.org/message/vendas/text \
  -H "apikey: TOKEN_DA_INSTANCIA" -H "Content-Type: application/json" \
  -d '{"number":"5511999999999","text":"Olá!"}'

curl -X POST https://evoasafe.duckdns.org/message/vendas/media \
  -H "apikey: TOKEN_DA_INSTANCIA" -H "Content-Type: application/json" \
  -d '{"number":"5511999999999","mediatype":"document","media":"https://exemplo.com/boleto.pdf","fileName":"boleto.pdf","caption":"Seu boleto"}'
```

## Integração em código

### JavaScript / TypeScript (Node 18+, sem dependências)

```js
const EVOLUTION_URL = "https://evoasafe.duckdns.org";
const INSTANCE = process.env.EVOLUTION_INSTANCE;   // ex: "vendas"
const API_KEY = process.env.EVOLUTION_API_KEY;     // token da instância

async function evolution(method, path, body) {
  const res = await fetch(`${EVOLUTION_URL}${path}`, {
    method,
    headers: { apikey: API_KEY, "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
  });
  const data = await res.json();
  if (!res.ok) throw new Error(`Evolution ${res.status}: ${data.error}`);
  return data;
}

export const sendText = (number, text) =>
  evolution("POST", `/message/${INSTANCE}/text`, { number, text });

export const sendMedia = (number, mediatype, media, caption) =>
  evolution("POST", `/message/${INSTANCE}/media`, { number, mediatype, media, caption });

export const instanceStatus = () => evolution("GET", `/instance/${INSTANCE}`);
```

### Python (requests)

```python
import os, requests

EVOLUTION_URL = "https://evoasafe.duckdns.org"
INSTANCE = os.environ["EVOLUTION_INSTANCE"]
HEADERS = {"apikey": os.environ["EVOLUTION_API_KEY"]}

def send_text(number: str, text: str) -> dict:
    r = requests.post(f"{EVOLUTION_URL}/message/{INSTANCE}/text",
                      json={"number": number, "text": text}, headers=HEADERS, timeout=30)
    r.raise_for_status()
    return r.json()
```

### Receber mensagens (webhook em Node/Express)

```js
app.post("/webhook/whatsapp", express.json({ limit: "10mb" }), async (req, res) => {
  res.sendStatus(200); // responda rápido; processe depois
  const { event, data } = req.body;
  if (event !== "MESSAGES_UPSERT" || data.key.fromMe || data.isGroup) return;
  const from = data.key.remoteJid;   // use como "number" para responder
  const text = data.text;            // texto já extraído
  await sendText(from, `Recebi: ${text}`);
});
```

Depois registre a URL pública do webhook na instância:

```bash
curl -X PUT https://evoasafe.duckdns.org/instance/NOME/webhook \
  -H "apikey: TOKEN_DA_INSTANCIA" -H "Content-Type: application/json" \
  -d '{"url":"https://seu-app.com/webhook/whatsapp","enabled":true,"events":["MESSAGES_UPSERT"]}'
```

### n8n

- **Receber**: nó *Webhook* (método POST) → copie a *Production URL* e registre em
  `PUT /instance/{name}/webhook`. Filtre com um nó *IF*: `event = MESSAGES_UPSERT` e
  `data.key.fromMe = false`. O texto está em `{{ $json.body.data.text }}` e o remetente
  em `{{ $json.body.data.key.remoteJid }}`.
- **Enviar**: nó *HTTP Request*, método POST, URL `https://evoasafe.duckdns.org/message/NOME/text`,
  header `apikey` com o token, body JSON `{"number": "...", "text": "..."}`.
- Também dá para importar `https://evoasafe.duckdns.org/openapi.yaml` em ferramentas que aceitam OpenAPI.
