# wapp-api — referência dos endpoints Base: `https://wapp-api.renatopavan.com.br` Todo acesso é por HTTPS. Chamadas em `http://` são redirecionadas. ## Autenticação Toda rota `/api/*` exige o header `X-API-Key`. Cada projeto tem a sua chave, gerada no servidor com `python gerar_chave.py criar `. Sem header válido: **401**. O `` do cliente vira a **`origem`** das mensagens. Não dá para sobrescrever pelo body — se você mandar `origem` no JSON, ele é ignorado. Cada cliente só enxerga nas consultas as mensagens que ele mesmo enfileirou. `/health` é a única rota sem autenticação. ## Mensagens ### `POST /api/mensagens` — enfileira ```json {"instancia":"principal","numero":"(11) 99999-8888","tipo":"texto", "conteudo":"Seu boleto venceu.","agendado_para":null} ``` | Campo | Obrigatório | Observação | |---|---|---| | `instancia` | não | nome de uma instância sua, ou `"gateway"`; omitido, vale a sua conectada — e o gateway, se você não tiver nenhuma | | `numero` | sim | aceita `(11) 99999-8888`, `11999998888`, `5511999998888`; ou JID de grupo autorizado (`docs/api-grupos.md`) | | `tipo` | não | `texto` (padrão); anexos em `docs/api-midia.md` | | `conteudo` | sim | texto da mensagem | | `agendado_para` | não | ISO 8601; nulo = assim que for a vez | **201** ```json {"id":123,"status":"na_fila","numero_destino":"5511999998888", "origem":"erp-financeiro","instancia":"principal","agendado_para":null} ``` A resposta é o **enfileiramento**, não o envio. Quem envia é o worker, com intervalo aleatório entre mensagens. Acompanhe pelo `GET /api/mensagens/`. ### Sem aparelho próprio: envio pelo gateway Projeto que só manda link de senha e aviso técnico (svn-monitor, bkp-app) não precisa de celular pareado: recebe chave e manda **pelo número do gateway**. | | Cliente com aparelho | Cliente sem aparelho | |---|---|---| | `instancia` no body | nome da sua | omitir, ou `"gateway"` | | Número que aparece para o destinatário | o seu | o do gateway | | Janela de horário | a sua (`GET /api/janela`) | a do **gateway**, 7h–22h | | Teto diário do número | o do seu aparelho | o do aparelho do gateway, dividido com os demais | | `origem`, teto por minuto, quota, opt-out, log, callback | seus | **seus também** — muda só o número que sai | `"instancia": "gateway"` vale mesmo para quem tem aparelho: é o jeito de mandar um aviso técnico pelo número do sistema. Se você tiver uma instância sua chamada `gateway`, ela ganha — a palavra reservada não toma o seu aparelho. Quando a mensagem sai pelo gateway, a resposta **201** traz `pelo_gateway` e um `aviso`: ```json {"id":123,"status":"na_fila","instancia":"gateway","pelo_gateway":true, "aviso":"sai pelo número do gateway: ... A janela de horário é a do gateway, 7h–22h."} ``` **409** quando não há por onde mandar — você sem instância conectada e o gateway sem aparelho pareado. Não é falha do serviço: é aparelho faltando, e o corpo diz onde resolver (Painel › Instâncias). Detalhes em `docs/instancias.md`. ### `GET /api/mensagens/` **200** com os campos da mensagem, incluindo `status`, `tentativas`, `erro`, `id_mensagem_wa`, `enviado_em`. **404** se o id não for seu. ### `GET /api/mensagens` — lista Filtros: `status`, `numero`, `data_inicio`, `data_fim` (ISO 8601), `limite` (padrão 100, teto 500). **200** → `{"total":2,"limite":100,"mensagens":[...]}` ### Mensagens recebidas `GET /api/mensagens/entrada` lista o que chegou e `GET /api/mensagens/entrada//arquivo` baixa o anexo. Formato, cursor e o callback `mensagem_recebida` em `docs/api-entrada.md`. ### Ciclo do `status` `na_fila` → `enviando` → `enviado` → `entregue` → `lido`, ou `falhou`. ## Instâncias | Método | Path | Devolve | |---|---|---| | `POST` | `/api/instancias` | **201** `{id, nome, status}` — body: `{"nome":"principal","descricao":"..."}` | | `GET` | `/api/instancias` | **200** lista com status | | `GET` | `/api/instancias//qrcode` | **200** `{qrcode_base64, pairing_code}` | | `GET` | `/api/instancias//status` | **200** `{status, estado_evolution}` | | `DELETE` | `/api/instancias/` | **200**; **409** se houver mensagem na fila | Passo a passo de conexão (QR, código de pareamento, erros) em `docs/instancias.md`. ## `GET /health` Sem autenticação. **200** `{"status":"ok","banco":true,"evolution":true}` ou **503** com `"status":"degradado"`. ## Códigos de erro | Código | Quando | |---|---| | **400** | body inválido, número que não normaliza, data fora do ISO 8601, `instancia` que não é sua nem `gateway` (a resposta traz `instancias_validas`) | | **401** | `X-API-Key` ausente, inválida ou revogada | | **403** | número está no opt-out | | **404** | mensagem inexistente (ou de outro cliente) | | **405** | método errado para a rota | | **409** | nome de instância repetido, remoção com fila pendente, ou nenhum aparelho conectado para enviar (nem seu, nem do gateway) | | **413** | corpo acima do limite de 20 MB (ver `docs/api-midia.md`) | | **429** | limite de requisições por minuto excedido; respeite o `Retry-After` | | **507** | sua quota de disco para anexos acabou | | **500** | erro interno — já registrado em `log_eventos` | | **502** | a Evolution recusou ou não respondeu | Todo erro vem como `{"erro":"...","detalhe":"..."}` — `detalhe` é opcional. ## Opt-out Quem responde `SAIR`, `PARAR` ou `DESCADASTRAR` entra na tabela `optout` automaticamente. A partir daí, enfileirar para esse número devolve **403**, e o worker também confere de novo antes de cada envio. ## Anexos Imagem, documento, vídeo e áudio vão por `multipart/form-data` na mesma rota. Ver `docs/api-midia.md`. ## Exemplos de código curl, Python, PHP e Delphi: ver `docs/api-exemplos.md`. ## Limites | Limite | Padrão | Estouro | |---|---|---| | Requisições por minuto | 60 | **429** com `Retry-After` | | Espaço em disco para anexos | 1 GB | **507** | | Tamanho de um anexo | 16 MB | **400** | | Corpo da requisição | 20 MB | **413** | Os dois primeiros são por cliente e podem ser ajustados — fale comigo se o seu volume pedir mais. ## Janela de horário Disparo sai só dentro da sua janela. **Resposta não**: se o destino escreveu para a instância nos últimos 60 min, a mensagem sai a qualquer hora. Quem confere isso é o gateway, olhando as mensagens recebidas — não há campo para declarar "isto é resposta". | Rota | O que faz | |---|---| | `GET /api/janela` | Devolve `inicio`, `fim`, o `teto` e `resposta_minutos` | | `PUT /api/janela` | Grava `{"inicio": 8, "fim": 20}` | A janela é sua; o **teto** é do gateway e só o administrador muda (padrão 07–22h). Janela fora do teto → **400** dizendo qual é o teto. Motivo: quem a Meta bane é o número, e o número é nosso. Pelo mesmo motivo, a janela que vale é sempre a do **dono do número que sai**: mensagem sua que sai pelo gateway obedece à janela do gateway (7h–22h), não à sua. ## Callback Em vez de consultar, você pode receber um POST a cada mudança de status e a cada mensagem recebida. Ver `docs/api-callbacks.md`.