# RyzeAPI > Gateway REST que transforma o WhatsApp em uma API moderna. Documentação completa em PT-BR com todas as páginas inlinadas para consumo por LLMs. Cada página aparece em um bloco ``. Frontmatter YAML foi removido; tabelas, blocos de código, componentes MDX (CardGroup, Steps, Note, etc.) e snippets em ``/`` permanecem intactos. ## Guia ### Primeiros passos ## O que é a RyzeAPI A **RyzeAPI** é um gateway REST que transforma o WhatsApp em uma API moderna, permitindo que sistemas, CRMs, bots, automações e dashboards **enviem e recebam mensagens, gerenciem grupos, publiquem em canais e reajam a eventos em tempo real**, sem lidar com a complexidade do protocolo interno do WhatsApp Web. Você envia uma requisição HTTP, a RyzeAPI cuida do resto. Texto, mídia, stickers, localização, contatos, reações, enquetes, carrosséis, listas, botões, formulários e até PIX. Gerencie vários números de WhatsApp simultaneamente, cada um com token próprio e configurações isoladas. Receba mensagens, status de entrega e mudanças de grupo via webhook ou WebSocket. Até 3 webhooks simultâneos por instância. Carrosséis, botões interativos, listas, formulários e botões PIX, recursos normalmente indisponíveis em APIs genéricas do WhatsApp. ## O que você pode fazer A RyzeAPI cobre o ciclo completo de integração com o WhatsApp: - **Conversas 1-a-1 e em grupo**, envie e receba qualquer tipo de conteúdo - **Gestão de contatos**, liste, organize em etiquetas, bloqueie, favorite - **Grupos**, crie, convide, modere, atualize nome/descrição/foto, gerencie participantes - **Comunidades**, crie comunidades e vincule seus grupos - **Canais (newsletters)**, publique em broadcast para seguidores - **Perfil da conta**, nome, foto, privacidade - **Stories (status)**, publique status que somem em 24h - **Eventos ao vivo**, webhooks e WebSockets para reagir em tempo real ## Arquitetura da sua integração Entender estes 3 níveis ajuda a desenhar sua aplicação corretamente: Seu espaço na RyzeAPI. Tem um **TokenAccount** único e um limite de quantas instâncias você pode criar. Cada instância é **uma conexão ativa com um número de WhatsApp**. Uma conta pode ter várias, por exemplo, uma para vendas, outra para suporte, outra para marketing. Cada instância recebe um **TokenInstance** próprio ao ser criada. Usam o TokenInstance. Enviar mensagens, ler contatos, criar grupos, configurar webhooks, tudo passa pelo token da instância específica. ## Primeiros passos Conecte seu WhatsApp e envie sua primeira mensagem em menos de 5 minutos. Entenda os conceitos centrais antes de mergulhar nos endpoints. Como usar TokenAccount e TokenInstance. Formatos de resposta e como tratar cada código HTTP. ## Explore os módulos A API é organizada em **módulos**. Cada um agrupa endpoints que cuidam de um aspecto específico do WhatsApp. Criar, conectar, desconectar, ajustar configurações. Enviar todos os tipos: texto, mídia, botões, carrosséis, listas. Contatos, etiquetas, arquivar, fixar, editar, apagar mensagens. Criar grupos, gerenciar participantes, links de convite. Criar comunidades e vincular seus grupos. Criar e gerenciar canais de broadcast. Nome, foto e privacidade da conta WhatsApp. Configurar webhooks e WebSockets por instância. Conexão persistente para eventos em tempo real. Integração nativa com o Chatwoot. Este guia cobre o caminho mínimo para ter uma instância conectada e trocar sua primeira mensagem. ## Pré-requisitos Já possuir o **TokenAccount** da RyzeAPI. Um celular com **WhatsApp Business** (ou o normal) instalado. ## 1. Defina seu token ```bash export Token_Account="seu-account-token" ``` Em todos os exemplos, a Base URL é sempre `https://ryzeapi.cloud`. ## 2. Crie uma instância Use o seu TokenAccount para provisionar uma nova instância de WhatsApp. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/new" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{"name": "$Instance_Name"}' ``` ```javascript Node.js const res = await fetch(`https://ryzeapi.cloud/api/instance/new`, { method: "POST", headers: { "token": Token_Account, "Content-Type": "application/json", }, body: JSON.stringify({ name: "$Instance_Name" }), }); const { data } = await res.json(); console.log(data.token); // este é o TokenInstance ``` ```python Python import os, requests r = requests.post( "https://ryzeapi.cloud/api/instance/new", headers={"token": os.environ["Token_Account"]}, json={"name": "minhaInstancia"}, ) print(r.json()["data"]["token"]) # este é o TokenInstance ``` Resposta esperada: ```json { "success": true, "message": "Instance created successfully", "status": "created", "data": { "id": "01953abc-...", "name": "$Instance_Name", "token": "a1b2c3d4-...", "status": "disconnected", "createdAt": "2026-04-21T12:00:00Z" } } ``` **Guarde o `data.token`**, este é o seu **TokenInstance**. A partir de agora, use ele (não o TokenAccount) para operar esta instância. ```bash export Token_Instance="a1b2c3d4-..." ``` ## 3. Conecte ao WhatsApp Use seu **TokenInstance** daqui em diante. ```bash curl -X GET "https://ryzeapi.cloud/api/instance/connect/$Instance_Name" \ -H "token: $Token_Instance" ``` A resposta traz: - `data.qrCodes`, strings que podem ser convertidas em QR - `data.qrImages`, PNGs em base64 prontos para exibir como imagem Escaneie no seu celular em **WhatsApp → Dispositivos vinculados → Vincular um dispositivo**. Ideal para ambientes sem câmera. Passe seu número no parâmetro `number`: ```bash curl -X GET "https://ryzeapi.cloud/api/instance/connect/$Instance_Name?number=5511999999999" \ -H "token: $Token_Instance" ``` A resposta traz um código de 8 caracteres para digitar em **Dispositivos vinculados → Vincular com código**. ## 4. Confirme que está conectado ```bash curl -X GET "https://ryzeapi.cloud/api/instance/list?instanceName=$Instance_Name" \ -H "token: $Token_Instance" ``` Aguarde o campo `status` da instância ficar igual a `"connected"`. ## 5. Envie sua primeira mensagem ```bash curl -X POST "https://ryzeapi.cloud/api/message/text/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "text": "Olá do RyzeAPI 👋" }' ``` ## 6. Configure um webhook (opcional) Para receber eventos em tempo real (mensagens chegando, status de entrega, etc.): ```bash curl -X POST "https://ryzeapi.cloud/api/events/webhook/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "label": "default", "enabled": true, "url": "https://seu-servidor.com/webhook", "events": ["message.exchange", "group.flow", "instance.state"], "mediaBase64": false }' ``` Cada instância aceita até **3 webhooks simultâneos** (por exemplo: produção, staging e um de log). Veja [Eventos](/pt/api/events/overview) para os 6 tipos disponíveis. ## Próximos passos Imagens, vídeos, áudios, documentos, botões, carrosséis, listas e formulários. Organize conversas, crie etiquetas, arquive, bloqueie e fixe chats. Entenda TokenAccount vs TokenInstance em detalhe. Como interpretar e tratar cada código HTTP. Entender estes conceitos centrais evita horas de debug. Leia antes de abrir qualquer módulo. ## Conta (Account) A sua **conta** na RyzeAPI é o que agrupa todas as suas instâncias de WhatsApp. Ela tem: - Um **TokenAccount** único (sua credencial principal) - Um **limite de instâncias** que você pode criar - Acesso a todas as instâncias dela Pense nela como sua "área de cliente": dentro dela ficam suas instâncias, cada uma representando um número de WhatsApp conectado. ## Instância Uma **instância** é uma conexão ativa com um número de WhatsApp. Cada instância tem: - **Nome único na sua conta** (ex.: `minhaInstancia`, `suporte`, `atendimento`) - **Token próprio** (TokenInstance) gerado automaticamente quando você cria a instância - **Sessão persistente**, depois de conectar ao WhatsApp, ela se mantém ligada mesmo após reiniciar - **Configurações individuais** (webhook, proxy, etc.) O nome da instância aparece em toda URL com `:instance` no path. Exemplo: `POST /api/message/text/minhaInstancia` opera na instância chamada `minhaInstancia`. ## Tokens: Account vs Instance Entregue quando sua conta foi criada. Usado para **administrar** sua conta. **Casos de uso**: criar, listar ou deletar instâncias. Gerado pela API ao criar uma instância. Usado nas **operações do dia a dia**. **Casos de uso**: enviar mensagem, criar grupo, configurar webhook, etc. Veja [Autenticação](/pt/guide/authentication) para detalhes sobre quando usar cada um. ## JID (Jabber ID) Todo contato, grupo ou canal no WhatsApp tem um **JID**, um identificador único no estilo email. A API aceita e retorna JIDs em todos os endpoints que referenciam destinatários. | Tipo | Formato | Exemplo | | ---- | ------- | ------- | | Usuário | `@s.whatsapp.net` | `5511999999999@s.whatsapp.net` | | Grupo | `@g.us` | `120363024567890123@g.us` | | Canal (newsletter) | `@newsletter` | `123456789@newsletter` | Em endpoints de envio para contatos, **você pode passar só o número** (ex.: `5511999999999`) no campo `number`, a API monta o JID completo automaticamente. Para grupos, passe sempre o JID completo (o que termina em `@g.us`). ## Webhook vs WebSocket A RyzeAPI oferece **dois canais complementares** para receber eventos em tempo real (novas mensagens, status de entrega, mudanças de grupo, etc.). Você pode usar os dois ao mesmo tempo. Quando um evento acontece na sua instância, a RyzeAPI faz uma requisição `POST` para a URL que você configurou. **Ideal para:** integrações servidor-a-servidor, CRMs, automações, bots. **Características:** - A RyzeAPI tenta novamente se seu endpoint estiver fora do ar (retry com backoff) - Você pode configurar até 3 webhooks simultâneos por instância (produção, staging, logging) - Pode incluir a mídia em base64 no corpo do webhook, ou apenas a URL Seu cliente mantém uma conexão aberta com a RyzeAPI e recebe os eventos em tempo real pelo mesmo canal. **Ideal para:** dashboards ao vivo, aplicações browser, qualquer interface onde você quer ver as mensagens chegarem sem polling. **Características:** - Reconexão automática recomendada do lado do cliente - Mesmo shape de evento que o webhook - Autenticado via query string (`?token=`) quando em browser ### Os 6 tipos de evento | Evento | Quando acontece | | ------ | --------------- | | `message.exchange` | Mensagem enviada ou recebida | | `message.status` | Mudança de status (enviada → entregue → lida) | | `group.flow` | Criação, saída ou alteração em grupo | | `instance.state` | Conectou, desconectou, QR novo, logout | | `call.update` | Chamada recebida, rejeitada, aceita | | `label.update` | Etiqueta criada, renomeada, atribuída | ## Formato das respostas A API usa dois formatos de envelope, que podem variar entre os endpoints. Ambos começam com o campo `success` que indica se a operação deu certo. ```json { "success": true, "message": "Text message sent successfully", "status": "sent", "data": { "...": "..." } } ``` **Campos importantes:** - `success`: sempre presente - `message`: descrição humana do resultado - `status`: **código estável de negócio** (ex.: `sent`, `connected`, `qr_ready`). É o melhor campo para tratar respostas programaticamente - `data`: payload útil (varia por endpoint) ```json { "success": false, "error": { "message": "Instance is not connected to WhatsApp" } } ``` **Ordem de prioridade para tratamento:** 1. O código HTTP (401, 403, 429, etc.), fonte de verdade 2. O texto de `error.message`, quando precisar diferenciar dois erros do mesmo HTTP Veja [Tipos de erro](/pt/guide/errors) para o catálogo completo de mensagens literais. ## Glossário rápido | Termo | Significado | | ----- | ----------- | | **Conta** | Sua área principal na RyzeAPI. Agrupa todas as suas instâncias, com uma cota de quantas você pode criar. | | **Instância** | Conexão ativa com um número de WhatsApp, gerenciada pela API. | | **TokenAccount** | Seu token principal, usado para criar/listar/deletar instâncias. | | **TokenInstance** | Token da instância específica, usado nas operações do dia a dia. | | **JID** | Identificador único no WhatsApp (contato, grupo ou canal) no formato email. | | **Webhook** | Requisição `POST` que a RyzeAPI faz para a sua URL quando um evento acontece. | | **WebSocket** | Conexão persistente que entrega eventos em tempo real para um cliente. | | **Pairing code** | Código de 8 caracteres para conectar uma instância sem precisar escanear QR. | ## Variáveis usadas em exemplos A Base URL é sempre `https://ryzeapi.cloud`. Os exemplos usam estas variáveis: | Variável | Significado | | -------- | ----------- | | `$Token_Account` | Seu token de conta | | `$Token_Instance` | Token de uma instância específica | | `$Instance_Name` | Nome da instância (ex.: `minhaInstancia`) | ### Fundamentos A RyzeAPI usa dois tipos de token para autenticar cada requisição. Todos são enviados no header `token`. Entenda quando usar cada um e como tratar os erros mais comuns. ## Dois tipos de token É o seu **token principal**. Com ele você cria e administra suas instâncias de WhatsApp. **Escopo:** sua conta inteira, todas as instâncias que ela possui. **Quando usar:** criar uma instância, listar todas as suas instâncias, deletar uma instância, ou qualquer operação em que você precise agir como "dono da conta". Gerado automaticamente quando você cria uma instância nova. Cada instância tem o seu. **Escopo:** **apenas aquela instância específica**. **Quando usar:** operações do dia a dia, enviar mensagem, criar grupo, configurar webhook, ler contatos, etc. É o token que você vai usar em **99% das chamadas**. Como regra prática: **use o TokenInstance sempre que o endpoint tem `:instance` no path**. Use o TokenAccount apenas para criar/listar/deletar instâncias. ## Ciclo de vida dos tokens É entregue quando sua conta é criada. Guarde em local seguro, ele não pode ser recuperado depois. Chame [`POST /api/instance/new`](/pt/api/instance/create) enviando seu TokenAccount. A resposta traz o campo `data.token`, que é o **TokenInstance** daquela instância. A partir daí, toda operação sobre aquela instância (mandar mensagem, ler contatos, configurar webhook) usa o TokenInstance. Volte a usar seu TokenAccount para provisionar mais uma. ## Três formas de enviar o token Recomendamos **sempre** usar o header `token`. As outras formas existem apenas para casos específicos. ### Header `token` (padrão recomendado) ```http POST /api/message/text/$Instance_Name HTTP/1.1 Host: ryzeapi.cloud Content-Type: application/json token: seu-token-aqui {"number": "5511999999999", "message": "Olá!"} ``` ### Header `Authorization: Bearer` (compatibilidade) Caso sua ferramenta só permita o padrão Bearer. ```http Authorization: Bearer seu-token-aqui ``` ### Query string `?token=` (apenas WebSocket em navegador) Usado quando não é possível enviar headers, o caso típico é WebSocket em JavaScript do browser, já que `new WebSocket(...)` não aceita headers customizados. ```javascript const ws = new WebSocket( `wss://ryzeapi.cloud/ws/$Instance_Name?token=${instanceToken}` ); ``` Query strings aparecem em logs de proxy e CDN. **Não use em produção fora do caso WebSocket/browser.** ## Quem pode fazer o quê - Criar novas instâncias ([`POST /api/instance/new`](/pt/api/instance/create)) - Listar todas as suas instâncias ([`GET /api/instance/list`](/pt/api/instance/list)) - Operar qualquer endpoint `:instance` das suas próprias instâncias (enviar mensagem, gerenciar grupos, etc.) - Deletar uma instância sua ([`DELETE /api/instance/delete/:instance`](/pt/api/instance/delete)) - Qualquer endpoint `:instance` da **própria instância**: enviar mensagem, ler contatos, configurar webhook, criar grupo, etc. - Inspecionar o estado atual da própria instância ([`GET /api/instance/list`](/pt/api/instance/list)) - Abrir conexão WebSocket para receber eventos em tempo real - TokenInstance **não cria instâncias novas**, use TokenAccount para isso - TokenInstance de uma instância **não opera outra instância**, cada token só vale para a sua própria - TokenAccount/TokenInstance de uma conta **não operam instâncias de outra conta** ## Proteção de acesso (ownership) A RyzeAPI verifica automaticamente, antes de cada operação, se o token enviado tem permissão para aquele recurso específico. Isso impede que um token vaze acesso a instâncias de terceiros. Exemplos do que é bloqueado automaticamente: - Tentar enviar mensagem na instância `B` usando o token da instância `A` → `403` - Tentar deletar uma instância que pertence a outra conta → `403` - TokenAccount tentando criar mais instâncias do que sua cota permite → `403` ## Erros de autenticação Todas respostas de erro de auth vêm com HTTP `401` (token inválido) ou `403` (sem permissão) e seguem o formato: ```json { "success": false, "error": { "message": "descrição legível do problema" } } ``` ### Mensagens que você pode encontrar | HTTP | Mensagem | Causa mais provável | | :--: | -------- | ------------------- | | 401 | `Missing token in header` | Você não enviou o header `token` | | 401 | `Missing token in header, Authorization header, or query parameter` | Nenhuma das 3 formas de token foi usada | | 401 | `Invalid token` | Token não existe, está errado, ou foi revogado | | 401 | `Invalid instance token` | O token não corresponde à instância no path | | 403 | `Instance token does not match requested instance` | Está usando o TokenInstance de A no path `:instance=B` | | 403 | `Instance does not belong to your account` | TokenAccount tentando operar instância de outra conta | | 403 | `Account instance quota exceeded` | Atingiu o limite de instâncias da sua conta | Se receber `Invalid token` sem saber por quê, verifique: (1) se o token está completo e sem espaços; (2) se está usando o tipo certo de token para aquele endpoint; (3) se a instância existe e está ativa. ## Segurança: boas práticas Trate **todo token** como credencial, armazene em variáveis de ambiente ou cofre de segredos, nunca no frontend ou em repositórios públicos. Use um TokenInstance por instância, se um vazar, você revoga só aquele. Use HTTPS em produção, a RyzeAPI aceita apenas conexões seguras. Use o header `token` (não a query string) na maioria dos casos, headers não vão parar em logs de proxy. Monitore o header `X-RateLimit-Remaining` para evitar ser bloqueado por excesso de requisições. ## Próximo Os formatos de resposta e como interpretar cada código HTTP. Quanto você pode chamar por minuto e como reagir ao `429`. Toda resposta da RyzeAPI tem um formato previsível. Entender esse formato é o primeiro passo para tratar erros com robustez no seu código. ## Formato geral das respostas Todas começam com o campo `success`, que indica se a operação deu certo. ```json { "success": true, "message": "Message sent successfully", "status": "sent", "data": { "...": "..." } } ``` **Campos comuns em respostas de sucesso:** | Campo | Significado | | ----- | ----------- | | `success` | Sempre `true` em sucesso | | `message` | Descrição legível do que aconteceu (use só para log/UI) | | `status` | Código de negócio quando aplicável (ex.: `sent`, `connected`) | | `data` | Payload útil do endpoint (varia conforme o caso) | ```json { "success": false, "error": { "message": "Instance is not connected to WhatsApp" } } ``` **Campos da resposta de erro:** | Campo | Significado | | ----- | ----------- | | `success` | Sempre `false` em erro | | `error.message` | Texto descritivo do problema | O envelope de erro tem **apenas** `success` + `error.message`. Não existe campo `code` numérico nem `errorType`. A diferenciação programática deve ser feita pelo **status HTTP** combinado com o conteúdo de `error.message`. ## Códigos HTTP | HTTP | Significado | Quando aparece | | :--: | ----------- | -------------- | | `200` | OK | Sucesso de leitura/operação síncrona | | `201` | Created | Criação | | `202` | Accepted | Job assíncrono criado | | `400` | Bad Request | Validação de body/query falhou; instância desconectada; identificador inválido | | `401` | Unauthorized | Token ausente ou inválido | | `403` | Forbidden | Token válido mas sem permissão (ownership) | | `404` | Not Found | Instância/grupo/recurso não existe | | `409` | Conflict | Nome duplicado de instância; webhook duplicado | | `429` | Too Many Requests | Rate limit; throttling do WhatsApp | | `500` | Internal Server Error | Erro interno (DB, encryption, whatsmeow) | | `501` | Not Implemented | Operação não suportada pelo whatsmeow embarcado | | `503` | Service Unavailable | Módulo Chatwoot não habilitado no servidor; instância não conectada | ## Mensagens literais por categoria A diferenciação fina entre erros é feita pelo texto de `error.message`. Abaixo, as mensagens que você pode encontrar. | Mensagem | | -------- | | `Missing token in header` | | `Missing token in header, Authorization header, or query parameter` | | `Missing token` | | `Invalid token` | | `Invalid instance token` | | `Invalid account token` | | `Unauthenticated` | | Mensagem | | -------- | | `Instance token does not match requested instance` | | `Instance does not belong to this account` | | `Not authorized to view group requests (must be admin)` | | `Not authorized to perform this action (must be admin)` | | `Not authorized to update this group (must be admin)` | | `Not authorized to reset group invite link (must be admin)` | | `Not allowed to join this group` | | `Not allowed to leave this group` | | Mensagem | | -------- | | `Instance name is required` | | `Invalid request body` | | `The 'name' field is required` | | `The 'identifier' query parameter is required (...)` | | `At least one participant is required` | | `At least one field must be provided ...` | | `At least one privacy setting must be provided` | | `group name must be 25 characters or less` | | `Community name must be 25 characters or less` | | `invalid participant : ` | | `invalid group JID : ` | | `invalid community JID : ` | | `Invalid action. Must be one of: add, remove, promote, demote, approve, reject` | | `Invalid value: . Valid values: ...` | | `Invalid duration format` | | `Invalid invite link or code` | | `Invite link has been revoked or expired` | | `Number not found or not registered on WhatsApp` | | `invalid LID format` | | HTTP | Mensagem | | :--: | -------- | | 400 | `Instance is not connected to WhatsApp` | | 503 | `Instance not connected` (quando crítica para a operação) | | Mensagem | | -------- | | `Rate limit exceeded` | | `rate limit exceeded (429): wait before creating again` (throttling do WhatsApp) | | Mensagem | | -------- | | `Instance with this name already exists` | | `webhook limit reached (max 3 enabled per instance)` | | HTTP | Mensagem | Causa | | :--: | -------- | ----- | | 401 | `Chatwoot rejected the API token - verify chatwootApiToken. Detail: ...` | Token Chatwoot inválido | | 403 | `Chatwoot denied the request - verify the API token has admin scope on account . Detail: ...` | Token sem permissão de admin | | 400 | `Chatwoot account or endpoint not found - verify chatwootBaseUrl (...) and chatwootAccountId (...). Detail: ...` | `account_id` ou URL incorretos | | 400 | `Chatwoot rejected the request as invalid ... Detail: ...` | Erro 422 do Chatwoot | | 502 | `Chatwoot is unreachable at - verify chatwootBaseUrl and that the host is reachable from the server. Detail: ...` | DNS, refused, timeout, ou 5xx | | 503 | `integration gateway not configured` | Módulo Chatwoot não habilitado no servidor | A causa-raiz vinda do Chatwoot é incluída sempre após `Detail:` para diagnóstico. | Mensagem | | -------- | | `WhatsApp client does not support newsletter creation (CreateNewsletter not available)` | | `WhatsApp client does not support listing newsletters (GetSubscribedNewsletters not available)` | | `WhatsApp client does not support GetNewsletterInfo` | | `WhatsApp client does not support GetNewsletterInfoWithInvite` | | `WhatsApp client does not support FollowNewsletter` | | `WhatsApp client does not support UnfollowNewsletter` | ## Webhooks: erros de entrega Webhooks falhos não retornam erro síncrono, são persistidos numa fila com: - `status`: `pending` / `delivered` / `failed` - `attempts`, `max_attempts` (default 5) - `last_error`: mensagem completa - `next_retry_at`: timestamp do próximo retry **Backoff exponencial:** | Tentativa | Próximo retry | |-----------|---------------| | 1 (fail) | +1s | | 2 (fail) | +5s | | 3 (fail) | +30s | | 4 (fail) | +5min | | 5 (fail) | +30min | | 6+ | +1h (cap) | Após `max_attempts`, status vira `failed` e a row permanece como Dead Letter Queue (auditoria/manual replay). Detalhes em [Eventos](/pt/api/events/overview). ## Boas práticas **Sempre cheque `success`** antes de assumir que o conteúdo é válido. **Status HTTP é fonte de verdade**, diferentes payloads podem ter o mesmo `error.message`. **Retentar `429`** com backoff exponencial; respeite o limite global de 100/min. **Não retentar `4xx` em geral** (exceto `408`, `429`). **Para `503` no Chatwoot**, entenda que o módulo Chatwoot não está habilitado no servidor e pare tentativas, logue para o operador. ## Erros de validação de schema Quando você manda um body com formato errado (falta campo obrigatório, tipo incompatível, etc.), a API retorna `400` com texto descritivo: ```json { "success": false, "error": { "message": "Invalid request body" } } ``` Não faça parsing fino do texto, valide seu body antes de enviar usando os schemas documentados em cada endpoint. ## Próximo Detalhes sobre tokens e ownership. Limites por minuto e como reagir ao 429. ## Rate limit Para manter a estabilidade do serviço, a RyzeAPI aplica um limite de requisições por minuto. O limite é contado **por token**: todos que usam o mesmo token compartilham o mesmo balde. | Tipo de requisição | Limite padrão | | ------------------ | ------------- | | Maioria dos endpoints | **100 requisições por minuto** | | Criação de instância (`POST /api/instance/new`) | **20 requisições por minuto** | Se você precisar de um limite maior para sua conta, entre em contato com o suporte. ### Cabeçalhos de rate limit **Todas as respostas** (inclusive as bem-sucedidas) trazem três cabeçalhos que permitem ao seu cliente se adaptar: | Header | Significado | | ------ | ----------- | | `X-RateLimit-Limit` | Limite máximo no período atual (ex.: `100`) | | `X-RateLimit-Remaining` | Quantas requisições ainda cabem | | `X-RateLimit-Reset` | Timestamp Unix (segundos) de quando o contador zera | Use `X-RateLimit-Remaining` para implementar backoff exponencial **antes** de bater no limite. Por exemplo: quando ficar abaixo de 10, espere um pouco antes da próxima chamada. ### O que acontece quando estoura A API responde com `HTTP 429 Too Many Requests`: ```json { "success": false, "error": { "message": "Rate limit exceeded. Try again later." } } ``` Os cabeçalhos `X-RateLimit-*` continuam sendo enviados, use `X-RateLimit-Reset` para saber quando pode tentar de novo. ### Exemplo de tratamento ```javascript async function callAPI(path, options = {}) { const res = await fetch(`https://ryzeapi.cloud${path}`, options); // Se estourou o limite, espere até o reset if (res.status === 429) { const resetAt = parseInt(res.headers.get("X-RateLimit-Reset"), 10) * 1000; const waitMs = Math.max(1000, resetAt - Date.now()); await new Promise(r => setTimeout(r, waitMs)); return callAPI(path, options); // tenta novamente } // Backoff preventivo: se estiver quase no limite, desacelera const remaining = parseInt(res.headers.get("X-RateLimit-Remaining"), 10); if (remaining < 10) { await new Promise(r => setTimeout(r, 500)); } return res.json(); } ``` ## CORS Se você está chamando a API **de um navegador** (JavaScript rodando em uma página web), precisa se atentar ao CORS. ### Origens permitidas A RyzeAPI aceita requisições vindas apenas das origens autorizadas para a sua conta. Origens não autorizadas recebem o erro clássico de CORS no console do navegador: ``` Access to fetch at 'https://ryzeapi.cloud/...' from origin 'https://meusite.com' has been blocked by CORS policy. ``` Se sua aplicação frontend precisa chamar a RyzeAPI, peça ao suporte para adicionar a sua origem (`https://meusite.com`) na allowlist da sua conta. ### Métodos e headers aceitos A API responde ao preflight (`OPTIONS`) com os seguintes cabeçalhos: ```http Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization, token Access-Control-Expose-Headers: Content-Length ``` Você pode enviar requisições com qualquer método HTTP usado pela API e com os headers `Content-Type`, `Authorization` ou `token`. ### Sem cookies A RyzeAPI **não usa cookies** para autenticação, o token vai no header `token`. Por isso, não é necessário (e não é suportado) enviar `credentials: "include"` nas requisições `fetch`. ## WebSocket em navegador Como JavaScript no browser não permite customizar headers em `new WebSocket(...)`, a única forma de autenticar é pela query string: ```javascript const ws = new WebSocket( `wss://ryzeapi.cloud/ws/$Instance_Name?token=${instanceToken}` ); ``` O WebSocket também segue a allowlist de origens da sua conta. ## Checklist Implemente backoff com base em `X-RateLimit-Remaining` para não ser bloqueado. Trate `HTTP 429` com re-tentativa após `X-RateLimit-Reset`. Se usa browser, peça ao suporte para adicionar sua origem na allowlist. Para WebSocket em browser, passe o token via `?token=` na URL. ## Referência da API ### Introdução Esta é a referência completa da API. Os endpoints estão organizados em **módulos**, onde cada um cobre um aspecto específico do WhatsApp. Se você está começando, a leitura recomendada é: [Início rápido](/pt/guide/quickstart) → [Conceitos](/pt/guide/concepts) → [Autenticação](/pt/guide/authentication) antes de mergulhar em um módulo específico. ## URL base e convenções - **Base URL:** `https://ryzeapi.cloud` - **Path prefix:** todos os endpoints começam com `/api//...` - **Probe de saúde:** `/health` (sem prefixo `/api/`) - **Eventos em tempo real:** `/ws/:instance` (WebSocket) - **Content-Type:** `application/json` em todo `POST` / `PUT` / `DELETE` com body - **Autenticação:** header `token` em todas as rotas (veja [Autenticação](/pt/guide/authentication)) - **Rate limit:** `100 req/min` por padrão; criação de instância tem limite estrito de `20 req/min` - **Body máximo:** 64 MB por requisição (cobre uploads em base64) ## Módulos Criar, conectar, configurar e operar suas instâncias de WhatsApp. Texto, mídia, sticker, localização, contato, reação, enquete, carrossel, botões, lista, formulário, PIX e status. Contatos, etiquetas, arquivar, fixar, mute, presença, histórico, encaminhar, editar, apagar e mais. Criar grupos, gerenciar participantes, links de convite e moderação. Criar comunidades e vincular subgrupos. Criar canais (channels) e gerenciar inscrições. Nome, foto, recado e privacidade da conta WhatsApp conectada. Webhooks (com fila persistida + retry) e WebSockets em tempo real. Upgrade `/ws/:instance`, protocolo, heartbeat, reconexão. Integração nativa com o Chatwoot. `/health`, probe combinado para orquestradores. ## Formato das respostas Toda chamada retorna um JSON com o campo `success` indicando o resultado: ```json { "success": true, "message": "Message sent successfully", "status": "sent", "data": { "...": "..." } } ``` ```json { "success": false, "error": { "message": "Instance is not connected to WhatsApp" } } ``` O envelope de erro tem **apenas** `success` + `error.message`, não existe campo `code` numérico. A diferenciação programática usa o **status HTTP** + texto da mensagem. Veja [Tipos de erro](/pt/guide/errors) para o catálogo completo de mensagens. ## Identificadores WhatsApp (JIDs) | Tipo | Formato | Exemplo | |------|---------|---------| | Privado | `@s.whatsapp.net` | `5511999999999@s.whatsapp.net` | | Grupo | `@g.us` | `120363406289005073@g.us` | | Newsletter | `@newsletter` | `120363422585881117@newsletter` | | LID (oculto) | `@lid` | `199789077627112@lid` | | Broadcast (status) | `status@broadcast` | `status@broadcast` | A maioria dos endpoints aceita **número simples** (`5511999999999`) e converte internamente. Para números brasileiros (com prefixo `55`), a API tenta variações com/sem o "9" extra após o DDD. ## Glossário rápido - **JID**, identificador único no WhatsApp (contato, grupo, canal, LID). - **Conta**, seu espaço na RyzeAPI, identificado pelo TokenAccount. - **Instância**, uma conexão ativa com um número de WhatsApp. - **TokenAccount**, token de conta; usado para criar/listar/deletar instâncias e operar qualquer instância da conta. - **TokenInstance**, token específico de uma instância; usado nas operações do dia a dia daquela instância. - **Webhook**, `POST` que a API faz para sua URL quando um evento acontece (com retry exponencial e DLQ). - **WebSocket**, conexão persistente para receber eventos em tempo real. - **Integração Chatwoot**, recurso nativo da RyzeAPI que conecta suas instâncias ao Chatwoot. ## Variáveis em exemplos A Base URL é sempre `https://ryzeapi.cloud`. Os exemplos usam estas variáveis: | Variável | Significado | | -------- | ----------- | | `$Token_Account` | Seu TokenAccount | | `$Token_Instance` | TokenInstance de uma instância específica | | `$Instance_Name` | Nome da instância (ex.: `minhaInstancia`) | ### Instância O módulo **Instância** é o ponto de partida da integração com a RyzeAPI. Cada instância representa **uma conexão ativa com um número de WhatsApp**, você pode ter várias por conta (uma para vendas, outra para suporte, outra para marketing, por exemplo). Aqui você encontra tudo que precisa para: - **Provisionar** novas instâncias na sua conta, opcionalmente já com webhook, WebSocket e Chatwoot configurados no mesmo request - **Conectar** cada uma a um número via QR code ou pairing code - **Inspecionar** o estado atual e os dados de perfil - **Configurar** proxy, ajustes de comportamento e armazenamento S3 - **Desconectar** (logout) mantendo a instância, ou **deletar** completamente **Status atual de uma instância** é consultado via [`GET /api/instance/list?instanceName=`](/pt/api/instance/list). A resposta inclui o estado da conexão, perfil, e o resumo das integrações (webhook, websocket, chatwoot). ## Ciclo de vida típico [`POST /api/instance/new`](/pt/api/instance/create) provisiona a instância e retorna o **TokenInstance**. A instância nasce no estado `disconnected`. [`GET /api/instance/connect/:instance`](/pt/api/instance/connect) gera o QR code (ou pairing code) para escanear no celular. [`GET /api/instance/list?instanceName=`](/pt/api/instance/list) confirma que o estado virou `connected` e expõe os dados completos (perfil, integrações, settings). A instância fica pronta para enviar/receber mensagens, gerenciar grupos, etc. Webhooks e WebSocket avisam mudanças de estado em tempo real. Use [`logout`](/pt/api/instance/logout) para desconectar mantendo a instância, ou [`delete`](/pt/api/instance/delete) para remover tudo definitivamente. ## Configuração inline na criação Os blocos de **webhook**, **WebSocket** e **Chatwoot** podem ser enviados **dentro do body de `POST /api/instance/new`**, assim a instância já nasce integrada, sem precisar de chamadas adicionais. Veja a referência completa em [Criar instância](/pt/api/instance/create). Campos `webhookEnabled`, `webhookURL`, `webhookEvents`, `webhookAuthorization`... Campos `websocketEnabled`, `websocketEvents`, `websocketMediaBase64`. Campos `chatwootEnabled`, `chatwootBaseUrl`, `chatwootAccountId`, `chatwootApiToken`, `chatwootInboxName`... ## Gestão da instância `POST /api/instance/new`, provisiona uma nova, já aplicando proxy, webhook, websocket, chatwoot, settings e S3 inline. `GET /api/instance/list`, todas da sua conta (com TokenAccount) ou só a própria (com TokenInstance). Aceita `?instanceName=` para filtrar. `DELETE /api/instance/delete/:instance`, remove tudo definitivamente. ## Conexão com WhatsApp `GET /api/instance/connect/:instance`, gera QR code ou pairing code para vincular o número. `POST /api/instance/reconnect/:instance`, reativa uma sessão que caiu, sem precisar de QR novo. `DELETE /api/instance/logout/:instance`, desconecta do WhatsApp mantendo a instância (precisará de QR novo para reconectar). ## Configuração da instância `GET /api/instance/getSettings/:instance` `POST /api/instance/settings/:instance`, auto-rejeitar chamadas, ignorar grupos, manter online, etc. `GET /api/instance/getProxy/:instance` `POST /api/instance/proxy/:instance`, HTTP, HTTPS ou SOCKS5. `GET /api/instance/getS3/:instance` `POST /api/instance/s3/:instance`, armazenar mídias recebidas em bucket próprio. ## Estados possíveis de uma instância | Estado | O que significa | | ------ | --------------- | | `disconnected` | Instância criada mas sem sessão ativa | | `connecting` | Aguardando conexão via QrCode ou Pairing Code com o WhatsApp | | `connected` | Pronta para enviar e receber | | `loggedout` | Usuário desvinculou no celular ou foi feito logout pela API | | `banned` | Conta banida pelo WhatsApp | Para inspecionar o estado atual, use [`GET /api/instance/list?instanceName=`](/pt/api/instance/list). A resposta inclui `connection.state`, `connection.numberJid`, `connection.presenceStatus`, `connection.displayStatus` e o objeto `profile` (nome, foto, business). ## Forma do erro A API retorna sempre o mesmo shape de erro em qualquer endpoint deste módulo: ```json { "success": false, "error": { "message": "" } } ``` Não há campo `code`, use o status HTTP e o texto do `error.message` para classificar. ## Boas práticas Para checar o estado da instância, use `GET /api/instance/list?instanceName=` em vez de polling agressivo, webhooks/WebSocket são a forma recomendada para reagir a mudanças. Monitore eventos `instance.state` via webhook/WebSocket para reagir a `disconnected` / `loggedout` automaticamente. ## Relacionados Depois de conectar, comece a enviar. Receba eventos da instância em tempo real. **Auth:** `TokenAccount` • **Rate-limit:** `20/min` • **Idempotente:** não ## Descrição Cria uma nova instância de WhatsApp na sua conta. A instância nasce **desconectada**, o próximo passo é chamar [`GET /api/instance/connect/:instance`](/pt/api/instance/connect) para obter o QR code ou pairing code. Durante a criação, você pode enviar, no mesmo body, a configuração inicial de **proxy**, **webhook**, **WebSocket**, **integração Chatwoot**, **ajustes de comportamento** e **armazenamento S3**. Cada bloco é independente: passe apenas o que precisar. A instância é criada dentro da cota da sua conta. Se você atingiu o limite, recebe `403` com mensagem `Account instance quota exceeded`, delete uma instância que não está mais usando para liberar espaço. Falhas em sub-blocos (webhook / websocket / chatwoot) **não abortam** a criação da instância. Cada bloco é log-and-continue: a instância é criada, o sub-bloco aparece como `enabled: false` ou ausente na resposta, e os logs do servidor descrevem a causa. Para Chatwoot, a falha também é exposta em `chatwoot.status: "error"` + `chatwoot.error: ""` no payload de retorno. ## Exemplos ### Mínimo Cria a instância só com o `name`. O `TokenInstance` é gerado automaticamente pelo servidor e devolvido em `instance.token` na resposta, guarde-o para autenticar chamadas subsequentes. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/new" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{"name":"minha-instancia"}' ``` ```javascript JavaScript const response = await fetch("https://ryzeapi.cloud/api/instance/new", { method: "POST", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ name: "minha-instancia" }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/new", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={"name": "minha-instancia"} ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{"name":"minha-instancia"}`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/new", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com Token personalizado Define manualmente o `TokenInstance` no campo `token` em vez de deixar o servidor gerar. Útil para reutilizar um valor já cadastrado em outro sistema, o token precisa ser único na sua conta. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/new" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{ "name": "minha-instancia", "token": "meu-token-customizado-123" }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/new", { method: "POST", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ name: "minha-instancia", token: "meu-token-customizado-123" }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/new", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={ "name": "minha-instancia", "token": "meu-token-customizado-123" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "minha-instancia", "token": "meu-token-customizado-123" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/new", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com configurações personalizadas Cria a instância já com o bloco de `settings` definido: rejeita chamadas automaticamente com mensagem padrão, mantém presença online, desativa sync de histórico e ignora stories. Equivale a chamar `POST /api/instance/settings/:instance` logo depois. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/new" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{ "name": "minha-instancia", "autoRejectCalls": true, "callRejectMessage": "Este número não aceita ligações.", "ignoreGroupMessages": false, "keepOnlineStatus": true, "autoReadMessages": false, "disableHistorySync": true, "ignoreStatus": true }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/new", { method: "POST", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ name: "minha-instancia", autoRejectCalls: true, callRejectMessage: "Este número não aceita ligações.", ignoreGroupMessages: false, keepOnlineStatus: true, autoReadMessages: false, disableHistorySync: true, ignoreStatus: true }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/new", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={ "name": "minha-instancia", "autoRejectCalls": True, "callRejectMessage": "Este número não aceita ligações.", "ignoreGroupMessages": False, "keepOnlineStatus": True, "autoReadMessages": False, "disableHistorySync": True, "ignoreStatus": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "minha-instancia", "autoRejectCalls": true, "callRejectMessage": "Este número não aceita ligações.", "ignoreGroupMessages": false, "keepOnlineStatus": true, "autoReadMessages": false, "disableHistorySync": true, "ignoreStatus": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/new", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com Proxy Já provisiona a instância apontando para um proxy SOCKS5 autenticado. A senha é encriptada at-rest com AES-256-GCM e nunca volta na resposta. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/new" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{ "name": "minha-instancia", "proxyEnabled": true, "proxyProtocol": "socks5", "proxyHost": "proxy.empresa.com", "proxyPort": "1080", "proxyUsername": "usuario", "proxyPassword": "senha-do-proxy" }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/new", { method: "POST", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ name: "minha-instancia", proxyEnabled: true, proxyProtocol: "socks5", proxyHost: "proxy.empresa.com", proxyPort: "1080", proxyUsername: "usuario", proxyPassword: "senha-do-proxy" }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/new", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={ "name": "minha-instancia", "proxyEnabled": True, "proxyProtocol": "socks5", "proxyHost": "proxy.empresa.com", "proxyPort": "1080", "proxyUsername": "usuario", "proxyPassword": "senha-do-proxy" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "minha-instancia", "proxyEnabled": true, "proxyProtocol": "socks5", "proxyHost": "proxy.empresa.com", "proxyPort": "1080", "proxyUsername": "usuario", "proxyPassword": "senha-do-proxy" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/new", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com Webhook Configura, no mesmo request, o webhook `default` recebendo apenas os eventos `message.exchange` e `call.update`, com `Authorization` custom para validar a origem. Mídias não vão em base64, o destino busca pela URL retornada. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/new" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{ "name": "minha-instancia", "webhookEnabled": true, "webhookURL": "https://meuapp.com/webhook", "webhookAuthorization": "Bearer secret-key-123", "webhookByEvents": false, "webhookEvents": ["message.exchange", "call.update"], "webhookMediaBase64": false }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/new", { method: "POST", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ name: "minha-instancia", webhookEnabled: true, webhookURL: "https://meuapp.com/webhook", webhookAuthorization: "Bearer secret-key-123", webhookByEvents: false, webhookEvents: ["message.exchange", "call.update"], webhookMediaBase64: false }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/new", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={ "name": "minha-instancia", "webhookEnabled": True, "webhookURL": "https://meuapp.com/webhook", "webhookAuthorization": "Bearer secret-key-123", "webhookByEvents": False, "webhookEvents": ["message.exchange", "call.update"], "webhookMediaBase64": False } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "minha-instancia", "webhookEnabled": true, "webhookURL": "https://meuapp.com/webhook", "webhookAuthorization": "Bearer secret-key-123", "webhookByEvents": false, "webhookEvents": ["message.exchange", "call.update"], "webhookMediaBase64": false }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/new", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com Websocket Habilita o broadcast em tempo real via WebSocket filtrando pelos eventos `message.exchange` e `call.update`. Útil para dashboards e bots que precisam de latência mínima sem expor um endpoint público para webhook. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/new" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{ "name": "minha-instancia", "websocketEnabled": true, "websocketEvents": ["message.exchange", "call.update"], "websocketMediaBase64": false }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/new", { method: "POST", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ name: "minha-instancia", websocketEnabled: true, websocketEvents: ["message.exchange", "call.update"], websocketMediaBase64: false }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/new", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={ "name": "minha-instancia", "websocketEnabled": True, "websocketEvents": ["message.exchange", "call.update"], "websocketMediaBase64": False } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "minha-instancia", "websocketEnabled": true, "websocketEvents": ["message.exchange", "call.update"], "websocketMediaBase64": false }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/new", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com S3 Já aponta o storage de mídia para um bucket AWS S3 (`us-east-1`), com prefixo `media/` para organizar uploads. A `s3SecretKey` é encriptada at-rest e nunca aparece na resposta. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/new" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{ "name": "minha-instancia", "s3Enabled": true, "s3Region": "us-east-1", "s3Bucket": "meu-bucket-media", "s3AccessKey": "AKIA...", "s3SecretKey": "secret...", "s3Endpoint": "https://s3.amazonaws.com", "s3PathPrefix": "media/" }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/new", { method: "POST", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ name: "minha-instancia", s3Enabled: true, s3Region: "us-east-1", s3Bucket: "meu-bucket-media", s3AccessKey: "AKIA...", s3SecretKey: "secret...", s3Endpoint: "https://s3.amazonaws.com", s3PathPrefix: "media/" }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/new", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={ "name": "minha-instancia", "s3Enabled": True, "s3Region": "us-east-1", "s3Bucket": "meu-bucket-media", "s3AccessKey": "AKIA...", "s3SecretKey": "secret...", "s3Endpoint": "https://s3.amazonaws.com", "s3PathPrefix": "media/" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "minha-instancia", "s3Enabled": true, "s3Region": "us-east-1", "s3Bucket": "meu-bucket-media", "s3AccessKey": "AKIA...", "s3SecretKey": "secret...", "s3Endpoint": "https://s3.amazonaws.com", "s3PathPrefix": "media/" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/new", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com Chatwoot Provisiona a instância já vinculada a um inbox Chatwoot (`WhatsApp - Orion`), com assinatura de agente ativa e reabertura automática de conversas resolvidas. Se a integração falhar, a instância é criada mesmo assim e o objeto `chatwoot` volta com `status: "error"`. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/new" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{ "name": "suporte", "chatwootEnabled": true, "chatwootBaseUrl": "https://chatwoot.example.com", "chatwootAccountId": 5, "chatwootApiToken": "sk_live_abc123...", "chatwootInboxName": "WhatsApp - Orion", "chatwootSignMessages": true, "chatwootIgnoreGroups": false, "chatwootStartAsPending": false, "chatwootReopenResolved": true }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/new", { method: "POST", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ name: "suporte", chatwootEnabled: true, chatwootBaseUrl: "https://chatwoot.example.com", chatwootAccountId: 5, chatwootApiToken: "sk_live_abc123...", chatwootInboxName: "WhatsApp - Orion", chatwootSignMessages: true, chatwootIgnoreGroups: false, chatwootStartAsPending: false, chatwootReopenResolved: true }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/new", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={ "name": "suporte", "chatwootEnabled": True, "chatwootBaseUrl": "https://chatwoot.example.com", "chatwootAccountId": 5, "chatwootApiToken": "sk_live_abc123...", "chatwootInboxName": "WhatsApp - Orion", "chatwootSignMessages": True, "chatwootIgnoreGroups": False, "chatwootStartAsPending": False, "chatwootReopenResolved": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "suporte", "chatwootEnabled": true, "chatwootBaseUrl": "https://chatwoot.example.com", "chatwootAccountId": 5, "chatwootApiToken": "sk_live_abc123...", "chatwootInboxName": "WhatsApp - Orion", "chatwootSignMessages": true, "chatwootIgnoreGroups": false, "chatwootStartAsPending": false, "chatwootReopenResolved": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/new", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Completo Combina todos os blocos no mesmo request: token custom, proxy SOCKS5, webhook, WebSocket, integração Chatwoot, settings de comportamento e storage S3. Cada bloco continua independente, falhas em sub-blocos não abortam a criação da instância. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/new" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{ "name": "marketing", "token": "meu-token-customizado-123", "proxyEnabled": true, "proxyProtocol": "socks5", "proxyHost": "proxy.empresa.com", "proxyPort": "1080", "proxyUsername": "usuario", "proxyPassword": "senha-do-proxy", "webhookEnabled": true, "webhookURL": "https://meuservidor.com/webhook", "webhookAuthorization": "Bearer secret-key-123", "webhookByEvents": false, "webhookEvents": ["message.exchange", "call.update"], "webhookMediaBase64": false, "websocketEnabled": true, "websocketEvents": ["message.exchange", "call.update"], "websocketMediaBase64": false, "chatwootEnabled": true, "chatwootBaseUrl": "https://chatwoot.example.com", "chatwootAccountId": 5, "chatwootApiToken": "sk_live_abc123...", "chatwootInboxName": "Marketing", "chatwootSignMessages": true, "chatwootIgnoreGroups": false, "chatwootStartAsPending": false, "chatwootReopenResolved": true, "autoRejectCalls": true, "callRejectMessage": "Este número não aceita ligações.", "ignoreGroupMessages": false, "keepOnlineStatus": true, "autoReadMessages": false, "disableHistorySync": false, "ignoreStatus": true, "s3Enabled": true, "s3Region": "us-east-1", "s3Bucket": "meu-bucket-media", "s3AccessKey": "AKIA...", "s3SecretKey": "secret...", "s3Endpoint": "https://s3.amazonaws.com", "s3PathPrefix": "media/" }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/new", { method: "POST", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ name: "marketing", token: "meu-token-customizado-123", proxyEnabled: true, proxyProtocol: "socks5", proxyHost: "proxy.empresa.com", proxyPort: "1080", proxyUsername: "usuario", proxyPassword: "senha-do-proxy", webhookEnabled: true, webhookURL: "https://meuservidor.com/webhook", webhookAuthorization: "Bearer secret-key-123", webhookByEvents: false, webhookEvents: ["message.exchange", "call.update"], webhookMediaBase64: false, websocketEnabled: true, websocketEvents: ["message.exchange", "call.update"], websocketMediaBase64: false, chatwootEnabled: true, chatwootBaseUrl: "https://chatwoot.example.com", chatwootAccountId: 5, chatwootApiToken: "sk_live_abc123...", chatwootInboxName: "Marketing", chatwootSignMessages: true, chatwootIgnoreGroups: false, chatwootStartAsPending: false, chatwootReopenResolved: true, autoRejectCalls: true, callRejectMessage: "Este número não aceita ligações.", ignoreGroupMessages: false, keepOnlineStatus: true, autoReadMessages: false, disableHistorySync: false, ignoreStatus: true, s3Enabled: true, s3Region: "us-east-1", s3Bucket: "meu-bucket-media", s3AccessKey: "AKIA...", s3SecretKey: "secret...", s3Endpoint: "https://s3.amazonaws.com", s3PathPrefix: "media/" }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/new", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={ "name": "marketing", "token": "meu-token-customizado-123", "proxyEnabled": True, "proxyProtocol": "socks5", "proxyHost": "proxy.empresa.com", "proxyPort": "1080", "proxyUsername": "usuario", "proxyPassword": "senha-do-proxy", "webhookEnabled": True, "webhookURL": "https://meuservidor.com/webhook", "webhookAuthorization": "Bearer secret-key-123", "webhookByEvents": False, "webhookEvents": ["message.exchange", "call.update"], "webhookMediaBase64": False, "websocketEnabled": True, "websocketEvents": ["message.exchange", "call.update"], "websocketMediaBase64": False, "chatwootEnabled": True, "chatwootBaseUrl": "https://chatwoot.example.com", "chatwootAccountId": 5, "chatwootApiToken": "sk_live_abc123...", "chatwootInboxName": "Marketing", "chatwootSignMessages": True, "chatwootIgnoreGroups": False, "chatwootStartAsPending": False, "chatwootReopenResolved": True, "autoRejectCalls": True, "callRejectMessage": "Este número não aceita ligações.", "ignoreGroupMessages": False, "keepOnlineStatus": True, "autoReadMessages": False, "disableHistorySync": False, "ignoreStatus": True, "s3Enabled": True, "s3Region": "us-east-1", "s3Bucket": "meu-bucket-media", "s3AccessKey": "AKIA...", "s3SecretKey": "secret...", "s3Endpoint": "https://s3.amazonaws.com", "s3PathPrefix": "media/" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "marketing", "token": "meu-token-customizado-123", "proxyEnabled": true, "proxyProtocol": "socks5", "proxyHost": "proxy.empresa.com", "proxyPort": "1080", "proxyUsername": "usuario", "proxyPassword": "senha-do-proxy", "webhookEnabled": true, "webhookURL": "https://meuservidor.com/webhook", "webhookAuthorization": "Bearer secret-key-123", "webhookByEvents": false, "webhookEvents": ["message.exchange", "call.update"], "webhookMediaBase64": false, "websocketEnabled": true, "websocketEvents": ["message.exchange", "call.update"], "websocketMediaBase64": false, "chatwootEnabled": true, "chatwootBaseUrl": "https://chatwoot.example.com", "chatwootAccountId": 5, "chatwootApiToken": "sk_live_abc123...", "chatwootInboxName": "Marketing", "chatwootSignMessages": true, "chatwootIgnoreGroups": false, "chatwootStartAsPending": false, "chatwootReopenResolved": true, "autoRejectCalls": true, "callRejectMessage": "Este número não aceita ligações.", "ignoreGroupMessages": false, "keepOnlineStatus": true, "autoReadMessages": false, "disableHistorySync": false, "ignoreStatus": true, "s3Enabled": true, "s3Region": "us-east-1", "s3Bucket": "meu-bucket-media", "s3AccessKey": "AKIA...", "s3SecretKey": "secret...", "s3Endpoint": "https://s3.amazonaws.com", "s3PathPrefix": "media/" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/new", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta inclui o **TokenInstance** gerado e o resumo de cada integração configurada (proxy, webhook, websocket, chatwoot, settings, s3). Guarde o `instance.token`, é com ele que você autentica chamadas subsequentes da própria instância. ```json 201 Created { "success": true, "message": "Instance created", "instance": { "id": "5e1d...", "name": "minha-instancia", "token": "a1b2c3d4-...", "status": "disconnected", "numberJid": null, "proxy": { "enabled": false, "host": null, "port": null, "protocol": null, "username": null, "password": null }, "webhook": { "enabled": true, "url": "https://meuapp.com/webhook", "events": ["message.exchange", "call.update"] }, "websocket": { "enabled": true, "events": ["message.exchange"], "mediaBase64": false }, "chatwoot": { "enabled": true, "status": "active", "bridgeIntegrationId": "int_xyz789abc", "baseUrl": "https://chatwoot.example.com", "accountId": 5, "inboxName": "WhatsApp - Orion" }, "settings": { "autoRejectCalls": false, "callRejectMessage": "", "ignoreGroupMessages": false, "keepOnlineStatus": false, "autoReadMessages": false, "disableHistorySync": true, "ignoreStatus": false }, "s3": { "enabled": false, "region": null, "bucket": null, "accessKey": null, "secretKey": null, "endpoint": null, "pathPrefix": null }, "createdAt": "2026-04-28T10:30:00Z", "updatedAt": "2026-04-28T10:30:00Z" } } ``` O `chatwootApiToken` não é retornado nesta resposta (é exposto em plaintext apenas em [`GET /api/chatwoot/list/:instance`](/pt/api/chatwoot/info)). O `s3.secretKey` e o `proxy.password` **nunca** são retornados por nenhum endpoint. ### Chatwoot com erro Se o bloco `chatwoot*` foi enviado mas a configuração falhou (ex.: token inválido), a instância **é criada mesmo assim** e o objeto `chatwoot` na resposta vem com `status: "error"` e uma `error` acionável: ```json "chatwoot": { "enabled": false, "status": "error", "error": "Chatwoot API returned 401 - verifique o chatwootApiToken" } ``` Você pode corrigir as credenciais via `POST /api/chatwoot/set/:instance` sem precisar recriar a instância. ## Headers Seu **TokenAccount**. `application/json` ## Request body Identificador da instância (usado nos paths `:instance` em todos os outros endpoints). Não pode estar em branco e precisa ser único na sua conta. Recomenda-se kebab-case ou snake_case. Token custom para a instância. Se omitido, é gerado automaticamente (recomendado). ### Bloco proxy (opcional) Ativa uso de proxy específico para esta instância. IP ou hostname do proxy. Porta como string (ex.: `"8080"`). `http`, `https` ou `socks5`. Usuário do proxy (opcional). Senha do proxy (opcional, encriptada at-rest com AES-256-GCM). ### Bloco webhook (opcional) Ativa o envio de eventos para uma URL. URL para onde a RyzeAPI vai fazer POST dos eventos. Valor que a RyzeAPI envia no header `Authorization` de cada POST (útil para validar origem). Ex.: `Bearer secret-key-123`. Se `true`, cada tipo de evento pode ter sua própria URL (padrão: `false`). Lista de eventos que a instância deve despachar. Ex.: `["message.exchange", "call.update"]`. Inclui mídia recebida em base64 dentro do corpo do webhook. O bloco webhook cria **um** webhook com label `default`. Para múltiplos webhooks por instância, use [`POST /api/events/webhook`](/pt/api/events/overview) depois. ### Bloco WebSocket (opcional) Ativa o broadcast de eventos via WebSocket para essa instância. Lista de eventos que serão emitidos via WebSocket. Se vier vazio com `websocketEnabled=true`, **todos** os eventos são emitidos. Inclui mídia recebida em base64 nos frames do WebSocket. ### Bloco Chatwoot (opcional) Ativa a integração nativa com o Chatwoot. URL da instalação Chatwoot (ex.: `https://chatwoot.example.com`). **Obrigatório** se `chatwootEnabled=true`. ID numérico da conta Chatwoot. **Obrigatório** se `chatwootEnabled=true`. API token da conta Chatwoot (`access_token` do agente). **Obrigatório** se `chatwootEnabled=true`. Encriptado at-rest. Não é retornado nesta resposta, mas é exposto em plaintext em [`GET /api/chatwoot/list/:instance`](/pt/api/chatwoot/info). Nome do inbox que será criado no Chatwoot (ex.: `"WhatsApp - Orion"`). Se `true`, mensagens enviadas pela API são prefixadas com a assinatura do agente Chatwoot. Se `true`, mensagens de grupos não viram conversas no Chatwoot. Se `true`, conversas novas começam como `pending` em vez de `open`. Se `true`, mensagens novas em conversas resolvidas reabrem-nas automaticamente. A integração depende do **módulo Chatwoot** estar habilitado no servidor. Se o módulo não está disponível, a criação da instância continua e o `chatwoot` retorna `enabled: false` (a falha aparece nos logs do servidor). Veja [chatwoot.md](/pt/api/chatwoot/overview) para detalhes. ### Bloco settings (opcional) Rejeita chamadas recebidas automaticamente. Mensagem automática enviada ao chamador quando a chamada é rejeitada. Não processa mensagens de grupo (útil para bots 1-a-1). Mantém a instância marcada como "online" no WhatsApp. Marca automaticamente mensagens recebidas como lidas. **Padrão `true`** (histórico não é sincronizado na primeira conexão). Envie `false` se quiser receber o backlog. Ignora mensagens do tipo "status" (stories). ### Bloco S3 (opcional, armazenamento de mídias) Ativa upload de mídias recebidas para S3 ou MinIO. Região (ex.: `us-east-1`). Nome do bucket. Access Key ID. Secret Access Key (encriptada at-rest, nunca retornada). Endpoint custom para MinIO ou DigitalOcean Spaces. Omita para AWS S3 oficial. Prefixo de path (ex.: `media/`). ## Erros | HTTP | `error.message` | Quando | | :--: | --------------- | ------ | | 400 | `Invalid request body` | JSON malformado | | 400 | `The 'name' field is required` | `name` ausente ou em branco | | 401 | `Invalid token` | TokenAccount inválido | | 403 | `Account instance quota exceeded` | Cota de instâncias atingida | | 409 | `Instance or token already exists` | Nome ou token já em uso | | 429 | `Rate limit exceeded. Try again later.` | Mais de 20 criações por minuto | | 500 | `Failed to create instance` | Erro interno | **Exemplo de erro:** ```json { "success": false, "error": { "message": "Instance or token already exists" } } ``` ## Próximo Gere o QR code ou pairing code para vincular o número. Use `GET /api/instance/list?instanceName=` para conferir o status atual. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** parcial ## Descrição Inicia a conexão via whatsmeow. Sem parâmetros, gera **QR code**. Com `?number=...`, gera **pairing code** (8 caracteres). Bloqueia até obter código ou erro (timeout interno ~60s). ## Exemplos ### QR code (uso padrão) Sem nenhum query param, o servidor gera o QR code para escanear no celular. Retorna a string ASCII e o PNG em base64 prontos para renderizar. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/instance/connect/minha-instancia" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/connect/minha-instancia", { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/instance/connect/minha-instancia", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/instance/connect/minha-instancia", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Pairing code Passando `?number=5511999999999`, o servidor força login via pairing code de 8 caracteres em vez de QR, útil quando o usuário não tem acesso à câmera para escanear. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/instance/connect/minha-instancia?number=5511999999999" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/connect/minha-instancia?number=5511999999999", { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/instance/connect/minha-instancia?number=5511999999999", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/instance/connect/minha-instancia?number=5511999999999", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Com 7 dias de histórico Solicita os últimos 7 dias de mensagens no primeiro pareamento via `?history=7`. A presença do parâmetro força a sincronização mesmo se `disableHistorySync=true` estiver nos settings da instância. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/instance/connect/minha-instancia?history=7" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/connect/minha-instancia?history=7", { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/instance/connect/minha-instancia?history=7", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/instance/connect/minha-instancia?history=7", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` **Pairing code**: sem câmera. O usuário digita os 8 caracteres no WhatsApp em **Dispositivos Vinculados > Vincular Outro Dispositivo > Vincular com número**. ## Resposta de sucesso ```json 200 OK { "success": true, "message": "QR code generated", "qrCode": "1@abc...,xyz==,base64string", "qrCodeBase64": "data:image/png;base64,iVBORw0K...", "status": "qr" } ``` - `qrCode`, string ASCII do QR (o que o WhatsApp espera). Pode ser renderizado em qualquer biblioteca de QR. - `qrCodeBase64`, PNG renderizado pelo servidor (base64). Pronto para usar como ``. ```json 200 OK { "success": true, "message": "Pairing code generated", "pairingCode": "ABCD-EFGH", "status": "qr" } ``` `pairingCode` é o que o usuário digita no celular. ## Path parameters Nome da instância a conectar. ## Headers TokenAccount ou TokenInstance da instância do path. ## Query parameters Telefone em formato internacional (ex.: `5511999999999`). Se preenchido, força login via **pairing code** em vez de QR. Solicita os últimos **N dias** de histórico no primeiro pareamento (ex.: `?history=5`). Requer suporte do servidor WhatsApp, não é garantido. A presença do parâmetro força a sincronização mesmo se `disableHistorySync=true` nos settings. ## Notas **O QR expira**, o WhatsApp emite um novo QR após ~20s. Se o usuário demorar e você precisar de uma nova tentativa, refaça a chamada. **Pairing code** não é reutilizável. Se o usuário errar, refaça o request para gerar um novo. Após chamar `connect`, faça poll de [`GET /api/instance/list?instanceName=`](/pt/api/instance/list) para detectar quando o estado virar `connected`. Webhook/WebSocket avisam em tempo real via evento `instance.state`. ## Erros | HTTP | `error.message` | Quando | | :--: | --------------- | ------ | | 400 | `Instance name is required` | Path vazio. | | 401 | `Invalid token` | Token ausente ou inválido. | | 404 | `Instance not found` | Nome não existe. | | 429 | `Rate limit exceeded. Try again later.` | Mais de 100 req/min. | | 500 | `Failed to generate QR code` | Falha ao gerar QR/pairing (rede, proxy, store corrompido). | ```json { "success": false, "error": { "message": "Instance not found" } } ``` ## Próximo `POST /api/instance/reconnect/:instance`, reaproveita a sessão salva. `GET /api/instance/list?instanceName=` mostra o estado atual. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** parcial ## Descrição Reutiliza a sessão whatsmeow já armazenada para restabelecer a conexão **sem novo QR/pairing**. Funciona se a instância já foi conectada pelo menos uma vez (sessão salva no whatsmeow_device). ## Exemplo ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/reconnect/minha-instancia" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/reconnect/minha-instancia", { method: "POST", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/reconnect/minha-instancia", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/reconnect/minha-instancia", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Reconnect initiated", "status": "connecting" } ``` Após a chamada, faça poll de [`GET /api/instance/list?instanceName=`](/pt/api/instance/list) para detectar quando o estado virar `connected`. O evento `instance.state` também é emitido via webhook/WebSocket. ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. Body é aceito mas **ignorado**, pode enviar vazio. ## Erros | HTTP | `error.message` | Quando | | :--: | --------------- | ------ | | 400 | `Instance has no saved session, call /connect first` | Não há sessão para reutilizar (instância nunca conectou ou foi deslogada). | | 401 | `Invalid token` | Token ausente ou inválido. | | 404 | `Instance not found` | Nome não existe. | | 429 | `Rate limit exceeded. Try again later.` | Mais de 100 req/min. | | 500 | `Failed to reconnect: ` | Falha em `Connect()` (rede, proxy, DNS). | ```json { "success": false, "error": { "message": "Instance has no saved session, call /connect first" } } ``` ## Notas **Diferença para `connect`**: `connect` sempre (re)cria um cliente novo e gera QR/pairing. `reconnect` reutiliza sessão existente, **não funciona** se a instância nunca foi conectada ou foi deslogada do lado do celular. Se o WhatsApp deslogou o dispositivo (estado `loggedout`), o `reconnect` falha com 400. Use [`connect`](/pt/api/instance/connect) para gerar um novo QR. ## Próximo Para reconectar quando a sessão foi perdida. Desliga o dispositivo no WhatsApp mantendo a instância. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Cada item retornado inclui o **status atual**, o estado da conexão, dados de perfil e o resumo das integrações (webhook, websocket, chatwoot, proxy, settings, s3). Esta é a forma recomendada para inspecionar o estado de uma instância. O resultado depende do tipo de token: - **TokenAccount**, retorna **todas as instâncias da sua conta**. Aceita filtro `?instanceName=`. - **TokenInstance**, retorna **apenas a própria instância** do token (filtro é ignorado). ## Exemplos ### Listar todas as instancias da conta Sem filtro e usando o `TokenAccount`, devolve todas as instâncias visíveis para a conta com status, perfil e resumo das integrações de cada uma. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/instance/list" \ -H "token: $Token_Account" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/list", { method: "GET", headers: { "token": process.env.Token_Account } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/instance/list", headers={ "token": os.environ["Token_Account"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/instance/list", nil) req.Header.Set("token", os.Getenv("Token_Account")) http.DefaultClient.Do(req) } ``` ### Filtrar por nome único (recomendado para checar status) Passando `?instanceName=minha-instancia`, retorna apenas aquela instância, ou **404** se não existir. Forma mais barata para fazer poll do estado de conexão após chamar `/connect`. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/instance/list?instanceName=minha-instancia" \ -H "token: $Token_Account" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/list?instanceName=minha-instancia", { method: "GET", headers: { "token": process.env.Token_Account } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/instance/list?instanceName=minha-instancia", headers={ "token": os.environ["Token_Account"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/instance/list?instanceName=minha-instancia", nil) req.Header.Set("token", os.Getenv("Token_Account")) http.DefaultClient.Do(req) } ``` ### Filtrar várias Aceita múltiplos nomes separados por vírgula em `?instanceName=vendas,suporte`. Nomes inexistentes são ignorados silenciosamente, só dá 404 quando o filtro tem um único nome e ele não existe. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/instance/list?instanceName=vendas,suporte" \ -H "token: $Token_Account" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/list?instanceName=vendas,suporte", { method: "GET", headers: { "token": process.env.Token_Account } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/instance/list?instanceName=vendas,suporte", headers={ "token": os.environ["Token_Account"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/instance/list?instanceName=vendas,suporte", nil) req.Header.Set("token", os.Getenv("Token_Account")) http.DefaultClient.Do(req) } ``` ### Ver dados da própria instância Usando o `TokenInstance`, qualquer filtro é ignorado e a resposta traz somente a instância dona do token. Cenário típico para clientes que só conhecem o token instance e querem inspecionar o próprio estado. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/instance/list" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/list", { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/instance/list", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/instance/list", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "1 Instance found", "instances": [ { "id": "5e1d...", "name": "minha-instancia", "token": "a1b2c3d4-...", "status": "connected", "connection": { "state": "connected", "numberJid": "5511999999999@s.whatsapp.net", "presenceStatus": "available", "displayStatus": "Active" }, "profile": { "name": "Orion", "pictureUrl": "https://pps.whatsapp.net/...", "isBusiness": false, "businessName": "" }, "proxy": { "enabled": false, "host": null, "port": null, "protocol": null, "username": null, "password": null }, "webhook": { "enabled": true, "url": "https://meuapp.com/webhook", "events": ["message.exchange"] }, "websocket": { "enabled": true, "events": ["message.exchange"], "mediaBase64": false }, "chatwoot": { "enabled": true, "status": "active", "bridgeIntegrationId": "int_xyz789abc", "baseUrl": "https://chatwoot.example.com", "accountId": 5, "inboxName": "WhatsApp - Orion", "apiToken": "sk_live_abc123...", "signMessages": true, "ignoreGroups": false, "startAsPending": false, "reopenResolved": true }, "settings": { "autoRejectCalls": false, "callRejectMessage": "", "ignoreGroupMessages": false, "keepOnlineStatus": false, "autoReadMessages": false, "disableHistorySync": true, "ignoreStatus": false }, "s3": { "enabled": false, "region": null, "bucket": null, "accessKey": null, "secretKey": null, "endpoint": null, "pathPrefix": null }, "createdAt": "2026-04-28T10:30:00Z", "updatedAt": "2026-04-28T10:35:00Z" } ], "meta": { "total": 1 } } ``` O campo `message` varia: `"1 Instance found"` quando o total é 1, e `" Instances found"` para outros valores. ## Headers TokenAccount ou TokenInstance. ## Query parameters Filtra por nome. Aceita um ou mais nomes separados por vírgula (ex.: `?instanceName=vendas,suporte`). Funciona apenas com TokenAccount. ## Campos da resposta ### `connection` | Campo | Tipo | Descrição | | ----- | ---- | --------- | | `state` | string | Estado real da conexão whatsmeow (`connected`, `connecting`, `disconnected`, `loggedout`) | | `numberJid` | string \| null | JID do número (`5511999999999@s.whatsapp.net`) ou `null` se nunca conectou | | `presenceStatus` | string | `available`, `unavailable`, `composing`... | | `displayStatus` | string | Status amigável para UI | ### `profile` | Campo | Tipo | Descrição | | ----- | ---- | --------- | | `name` | string | Nome de exibição do WhatsApp | | `pictureUrl` | string | URL da foto de perfil (cache do whatsmeow) | | `isBusiness` | boolean | `true` se é WhatsApp Business | | `businessName` | string | Nome comercial (vazio se não for Business) | ### Integrações - `webhook`, webhook default (label `default`). `enabled: false` significa sem webhook. - `websocket`, `{ enabled, events, mediaBase64 }`. `enabled: false` significa que o WebSocket está desligado para a instância. - `chatwoot`, `{ enabled, status, bridgeIntegrationId, baseUrl, accountId, inboxName, apiToken, signMessages, ignoreGroups, startAsPending, reopenResolved }`. `enabled: false` significa sem integração Chatwoot ligada (ou módulo Chatwoot não habilitado no servidor). O `apiToken` vem em **plaintext** (mesma exposição intencional do [`GET /api/chatwoot/list/:instance`](/pt/api/chatwoot/info)). Trate como sensível e só aparece quando há integração. Os quatro flags são **sempre** retornados como `true`/`false` (instâncias sem Chatwoot reportam todos `false` junto de `enabled: false`). - `proxy`, proxy individual (não inclui o global do deploy). - `settings`, flags de comportamento (ver [Atualizar settings](/pt/api/instance/settings-update)). - `s3`, config de storage S3 individual. ## Regras do filtro | Situação | Comportamento | | -------- | ------------- | | TokenAccount sem filtro | Retorna todas da conta | | TokenAccount com 1 nome | Retorna aquela específica, ou **404** se não existir | | TokenAccount com vários nomes | Retorna as que existirem, nomes inexistentes são ignorados | | TokenAccount com filtro só de vírgulas/espaços | Retorna lista vazia sem erro | | TokenInstance | Ignora filtro; retorna apenas a própria instância | ## Erros | HTTP | `error.message` | Quando | | :--: | --------------- | ------ | | 401 | `Invalid token` | Token ausente ou inválido | | 404 | `Instance not found` | Nome único solicitado não existe | | 429 | `Rate limit exceeded. Try again later.` | Mais de 100 requisições por minuto | ```json { "success": false, "error": { "message": "Instance not found" } } ``` ## Próximo Provisiona mais uma na sua conta, já com webhook, websocket e chatwoot configurados inline se quiser. Gere o QR code ou pairing code para vincular o número. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Aceita **body parcial**, campos não enviados são mantidos. Pelo menos um campo precisa ser informado. Se `keepOnlineStatus` é enviado **e** a instância está conectada, a presença é aplicada em tempo real (envia `PresenceAvailable` ou `PresenceUnavailable`). ## Exemplos ### Alterar só autoReadMessages Body parcial com apenas `autoReadMessages: true`, os outros 6 settings ficam intocados, o servidor só atualiza o campo enviado e devolve o objeto completo na resposta. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/settings/minha-instancia" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"autoReadMessages":true}' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/settings/minha-instancia", { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ autoReadMessages: true }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/settings/minha-instancia", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={"autoReadMessages": True} ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{"autoReadMessages":true}`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/settings/minha-instancia", body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Combo anti-ruído Aplica de uma vez quatro settings para silenciar o número: ignora mensagens de grupo, ignora stories, rejeita chamadas e responde ao chamador com a `callRejectMessage`. Combinação típica para bots 1-a-1 que não querem ser interrompidos. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/settings/minha-instancia" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "ignoreGroupMessages": true, "ignoreStatus": true, "autoRejectCalls": true, "callRejectMessage": "Não atendo ligações por aqui." }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/settings/minha-instancia", { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ ignoreGroupMessages: true, ignoreStatus: true, autoRejectCalls: true, callRejectMessage: "Não atendo ligações por aqui." }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/settings/minha-instancia", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "ignoreGroupMessages": True, "ignoreStatus": True, "autoRejectCalls": True, "callRejectMessage": "Não atendo ligações por aqui." } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "ignoreGroupMessages": true, "ignoreStatus": true, "autoRejectCalls": true, "callRejectMessage": "Não atendo ligações por aqui." }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/settings/minha-instancia", body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta inclui **todos os 7 settings** (os não modificados vêm com o valor atual do banco). ```json 200 OK { "success": true, "message": "Settings updated successfully", "settings": { "autoRejectCalls": true, "callRejectMessage": "Não atendo ligações por aqui.", "ignoreGroupMessages": true, "keepOnlineStatus": false, "autoReadMessages": false, "disableHistorySync": true, "ignoreStatus": true } } ``` ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. `application/json`. ## Request body Rejeita chamadas recebidas automaticamente. Mensagem automática ao rejeitar chamada. Não processa mensagens recebidas em grupos. Mantém presença `available`. Aplicado imediatamente se a instância estiver conectada. Marca mensagens recebidas como lidas. Desliga sincronização de histórico no primeiro `connect`. **Default `true`.** Ignora mensagens tipo "status" (stories) do WhatsApp. ## Notas `disableHistorySync=true` no update **não apaga** histórico já importado, só afeta futuros `connect`. Aplicar `ignoreGroupMessages=true` **não** apaga mensagens de grupos já gravadas; só para de gravar novas. ## Erros | HTTP | `error.message` | Quando | | :--: | --------------- | ------ | | 400 | `Invalid request body` | JSON malformado. | | 400 | `At least one setting must be provided` | Body sem nenhum campo. | | 401 | `Invalid token` | Token ausente ou inválido. | | 404 | `Instance not found` | Nome não existe. | | 429 | `Rate limit exceeded. Try again later.` | Mais de 100 req/min. | | 500 | `Failed to update settings configuration` | Erro de banco. | ```json { "success": false, "error": { "message": "At least one setting must be provided" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna as 7 flags de comportamento da instância: rejeição de chamadas, ignorar grupos, manter online, marcar como lida, sync de histórico, ignorar status. ## Exemplo Faz `GET` no path da instância e devolve o objeto `settings` com todas as flags atuais. Não aceita query params, apenas o nome no path e o `token` no header. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/instance/getSettings/minha-instancia" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/getSettings/minha-instancia", { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/instance/getSettings/minha-instancia", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/instance/getSettings/minha-instancia", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Settings retrieved successfully", "settings": { "autoRejectCalls": false, "callRejectMessage": "", "ignoreGroupMessages": false, "keepOnlineStatus": false, "autoReadMessages": false, "disableHistorySync": true, "ignoreStatus": false } } ``` ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. ## Campos | Campo | Tipo | Descrição | | ----- | ---- | --------- | | `autoRejectCalls` | boolean | Rejeita chamadas automaticamente | | `callRejectMessage` | string | Mensagem enviada ao rejeitar | | `ignoreGroupMessages` | boolean | Ignora eventos de grupo na ingestão | | `keepOnlineStatus` | boolean | Mantém presença `available` | | `autoReadMessages` | boolean | Marca mensagens recebidas como lidas | | `disableHistorySync` | boolean | Desliga sync de histórico (default `true`) | | `ignoreStatus` | boolean | Ignora mensagens do tipo "status" (stories) | ## Erros | HTTP | `error.message` | Quando | | :--: | --------------- | ------ | | 401 | `Invalid token` | Token ausente ou inválido. | | 404 | `Instance not found` | Nome não existe. | | 429 | `Rate limit exceeded. Try again later.` | Mais de 100 req/min. | | 500 | `Failed to get settings configuration` | Erro de banco. | ```json { "success": false, "error": { "message": "Instance not found" } } ``` ## Próximo `POST /api/instance/settings/:instance` para alterar. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Define proxy HTTP, HTTPS ou SOCKS5 específico da instância. A configuração só toma efeito após `/reconnect` ou novo `/connect`. ## Exemplos ### SOCKS5 autenticado Configura um proxy SOCKS5 na porta `1080` com usuário e senha. A senha é encriptada at-rest com AES-256-GCM e nunca volta em plaintext na resposta. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/proxy/minha-instancia" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "enabled": true, "host": "proxy.exemplo.com", "port": "1080", "protocol": "socks5", "username": "user1", "password": "secret" }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/proxy/minha-instancia", { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ enabled: true, host: "proxy.exemplo.com", port: "1080", protocol: "socks5", username: "user1", password: "secret" }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/proxy/minha-instancia", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "enabled": True, "host": "proxy.exemplo.com", "port": "1080", "protocol": "socks5", "username": "user1", "password": "secret" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "enabled": true, "host": "proxy.exemplo.com", "port": "1080", "protocol": "socks5", "username": "user1", "password": "secret" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/proxy/minha-instancia", body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### HTTP sem auth Aponta para um proxy HTTP interno (`10.0.0.5:3128`) sem credenciais, cenário comum em redes corporativas com auth-by-IP ou proxy aberto. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/proxy/minha-instancia" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"enabled":true,"host":"10.0.0.5","port":"3128","protocol":"http"}' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/proxy/minha-instancia", { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ enabled: true, host: "10.0.0.5", port: "3128", protocol: "http" }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/proxy/minha-instancia", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "enabled": True, "host": "10.0.0.5", "port": "3128", "protocol": "http" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{"enabled":true,"host":"10.0.0.5","port":"3128","protocol":"http"}`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/proxy/minha-instancia", body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Desabilitar Envia apenas `enabled: false` para remover o proxy individual da instância. Ela volta a usar o proxy global do deploy (se houver) ou conexão direta na próxima reconexão. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/proxy/minha-instancia" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"enabled":false}' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/proxy/minha-instancia", { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ enabled: false }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/proxy/minha-instancia", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={"enabled": False} ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{"enabled":false}`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/proxy/minha-instancia", body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Proxy configuration updated successfully", "proxy": { "enabled": true, "host": "proxy.exemplo.com", "port": "1080", "protocol": "socks5", "username": "user1", "password": "" } } ``` O `password` aparece como string vazia (`""`) na resposta, o servidor nunca devolve a senha em plaintext. ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. `application/json`. ## Request body Ativa/desativa o proxy de instância. IP ou hostname. **Obrigatório** se `enabled=true`. Porta como string (`"1080"`, `"8080"`). **Obrigatório** se `enabled=true`. `"http"`, `"https"` ou `"socks5"`. **Obrigatório** se `enabled=true`. Usuário (opcional). Senha (opcional, encriptada at-rest). ## Regras - `enabled=true` exige `host`, `port` e `protocol`. - `protocol` deve ser `http`, `https` ou `socks5`. - `username` / `password` são opcionais (proxy aberto ou auth-by-IP). - `enabled=false` faz a instância voltar a usar o proxy padrão do deploy (se houver). - A senha é **encriptada at-rest** (AES-256-GCM) e **redigida** na resposta. ## Notas O proxy individual da instância tem **prioridade** sobre o proxy global do deploy. Se `enabled=false`, a instância usa o proxy global (se houver) ou conexão direta. Mudanças no proxy **não reconectam automaticamente**, chame [`reconnect`](/pt/api/instance/reconnect) para aplicar. ## Erros | HTTP | `error.message` | Quando | | :--: | --------------- | ------ | | 400 | `Invalid request body` | JSON malformado. | | 400 | `Host is required when proxy is enabled` | `enabled=true` sem `host`. | | 400 | `Port is required when proxy is enabled` | `enabled=true` sem `port`. | | 400 | `Protocol is required when proxy is enabled` | `enabled=true` sem `protocol`. | | 400 | `Protocol must be one of: http, https, socks5` | `protocol` fora do enum. | | 401 | `Invalid token` | Token ausente ou inválido. | | 404 | `Instance not found` | Nome não existe. | | 429 | `Rate limit exceeded. Try again later.` | Mais de 100 req/min. | | 500 | `Failed to update proxy configuration` | Erro de banco. | ```json { "success": false, "error": { "message": "Protocol must be one of: http, https, socks5" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna **apenas** o proxy individual da instância (não inclui fallback para o proxy global do deploy). Se a instância não tem proxy próprio, retorna `enabled: false` com os demais campos vazios. A senha **não** é retornada. ## Exemplo Faz `GET` no path da instância e devolve o objeto `proxy` com a configuração ativa (sem incluir a `password`). Se a instância não tem proxy individual, vem com `enabled: false` e os demais campos `null`. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/instance/getProxy/minha-instancia" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/getProxy/minha-instancia", { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/instance/getProxy/minha-instancia", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/instance/getProxy/minha-instancia", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Proxy configuration retrieved successfully", "proxy": { "enabled": true, "host": "proxy.exemplo.com", "port": "1080", "protocol": "socks5", "username": "user1" } } ``` ```json 200 OK { "success": true, "message": "Proxy configuration retrieved successfully", "proxy": { "enabled": false, "host": null, "port": null, "protocol": null, "username": null } } ``` O campo `password` **nunca** é retornado por este endpoint. ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. ## Erros | HTTP | `error.message` | Quando | | :--: | --------------- | ------ | | 401 | `Invalid token` | Token ausente ou inválido. | | 404 | `Instance not found` | Nome não existe. | | 429 | `Rate limit exceeded. Try again later.` | Mais de 100 req/min. | | 500 | `Failed to get proxy configuration` | Erro de banco. | ```json { "success": false, "error": { "message": "Instance not found" } } ``` ## Notas Este endpoint **não expõe** o proxy global do deploy intencionalmente, você só vê o que configurou para esta instância. ## Próximo `POST /api/instance/proxy/:instance` para alterar. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Configura o storage S3 da instância. `secretKey` é **encriptado at-rest** e nunca retornado. Para storage compatível com S3 (MinIO, Backblaze, DO Spaces), preencha `endpoint` com a URL. ## Exemplos ### AWS S3 Aponta o storage para AWS S3 oficial: bucket `ryzeapi-media` em `us-east-1`, com `endpoint` vazio para usar o domínio padrão da AWS e prefixo `media/myinstance/` para isolar os arquivos. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/s3/minha-instancia" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "enabled": true, "region": "us-east-1", "bucket": "ryzeapi-media", "accessKey": "AKIA...", "secretKey": "secret-redacted", "endpoint": "", "pathPrefix": "media/myinstance/" }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/s3/minha-instancia", { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ enabled: true, region: "us-east-1", bucket: "ryzeapi-media", accessKey: "AKIA...", secretKey: "secret-redacted", endpoint: "", pathPrefix: "media/myinstance/" }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/s3/minha-instancia", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "enabled": True, "region": "us-east-1", "bucket": "ryzeapi-media", "accessKey": "AKIA...", "secretKey": "secret-redacted", "endpoint": "", "pathPrefix": "media/myinstance/" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "enabled": true, "region": "us-east-1", "bucket": "ryzeapi-media", "accessKey": "AKIA...", "secretKey": "secret-redacted", "endpoint": "", "pathPrefix": "media/myinstance/" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/s3/minha-instancia", body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### MinIO self-hosted Mesmo formato do AWS, mas com `endpoint` apontando para um MinIO interno (`https://minio.interno.empresa.com`). O mesmo padrão funciona para DigitalOcean Spaces, Backblaze B2 e outros storages compatíveis com S3. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/s3/minha-instancia" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "enabled": true, "region": "us-east-1", "bucket": "whatsapp", "endpoint": "https://minio.interno.empresa.com", "accessKey": "minioadmin", "secretKey": "minioadmin", "pathPrefix": "ryzeapi/" }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/s3/minha-instancia", { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ enabled: true, region: "us-east-1", bucket: "whatsapp", endpoint: "https://minio.interno.empresa.com", accessKey: "minioadmin", secretKey: "minioadmin", pathPrefix: "ryzeapi/" }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/s3/minha-instancia", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "enabled": True, "region": "us-east-1", "bucket": "whatsapp", "endpoint": "https://minio.interno.empresa.com", "accessKey": "minioadmin", "secretKey": "minioadmin", "pathPrefix": "ryzeapi/" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "enabled": true, "region": "us-east-1", "bucket": "whatsapp", "endpoint": "https://minio.interno.empresa.com", "accessKey": "minioadmin", "secretKey": "minioadmin", "pathPrefix": "ryzeapi/" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/s3/minha-instancia", body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Desabilitar Envia apenas `enabled: false` para desativar o storage e **apagar** as credenciais do banco. Para reabilitar depois é necessário reenviar todos os campos novamente. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/instance/s3/minha-instancia" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"enabled":false}' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/s3/minha-instancia", { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ enabled: false }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/instance/s3/minha-instancia", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={"enabled": False} ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{"enabled":false}`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/instance/s3/minha-instancia", body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "S3 configuration updated", "s3": { "enabled": true, "region": "us-east-1", "bucket": "ryzeapi-media", "accessKey": "AKIA...", "endpoint": "", "pathPrefix": "media/myinstance/" } } ``` `secretKey` **não** aparece na resposta, o servidor nunca devolve a chave em plaintext. ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. `application/json`. ## Request body Ativa/desativa o S3 da instância. `false` zera todos os campos. Região (ex.: `us-east-1`). Nome do bucket (deve existir, não há criação). Access Key ID. Secret Access Key. Encriptada at-rest. Endpoint custom (MinIO, DO Spaces, Backblaze). Vazio para AWS S3 oficial. Prefixo de path (ex.: `media/myinstance/`). ## Notas **Não há teste de credenciais.** O endpoint salva a config sem validar se o bucket existe ou se as credenciais funcionam, o erro só aparece quando o próximo upload de mídia tentar autenticar (visível nos logs do servidor). Desabilitar (`enabled=false`) **apaga** as credenciais do banco. Para reabilitar depois, é necessário reenviar todos os campos. ## Erros | HTTP | `error.message` | Quando | | :--: | --------------- | ------ | | 400 | `Invalid request body` | JSON malformado. | | 401 | `Invalid token` | Token ausente ou inválido. | | 404 | `Instance not found` | Nome não existe. | | 429 | `Rate limit exceeded. Try again later.` | Mais de 100 req/min. | | 500 | `Failed to update S3 configuration` | Erro de banco. | ```json { "success": false, "error": { "message": "Failed to update S3 configuration" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna a configuração S3 individual da instância. O `secretKey` **nunca** é retornado. ## Exemplo Faz `GET` no path da instância e devolve o objeto `s3` com a configuração de armazenamento. O campo `secretKey` sai sempre `null` por segurança. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/instance/getS3/minha-instancia" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/getS3/minha-instancia", { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/instance/getS3/minha-instancia", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/instance/getS3/minha-instancia", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "s3": { "enabled": true, "region": "us-east-1", "bucket": "ryzeapi-media", "accessKey": "AKIA...", "secretKey": null, "endpoint": "", "pathPrefix": "media/myinstance/" } } ``` ```json 200 OK { "success": true, "s3": { "enabled": false, "region": null, "bucket": null, "accessKey": null, "secretKey": null, "endpoint": null, "pathPrefix": null } } ``` `secretKey` sai como `null`, o servidor nunca devolve a chave secreta em plaintext. ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. ## Erros | HTTP | `error.message` | Quando | | :--: | --------------- | ------ | | 401 | `Invalid token` | Token ausente ou inválido. | | 404 | `Instance not found` | Nome não existe. | | 429 | `Rate limit exceeded. Try again later.` | Mais de 100 req/min. | | 500 | `Failed to get S3 configuration` | Erro de banco. | ```json { "success": false, "error": { "message": "Instance not found" } } ``` ## Notas `endpoint` em branco (`""`) ou `null` indica AWS S3 oficial; para MinIO, DigitalOcean Spaces ou Backblaze, esse campo guarda a URL completa do endpoint. ## Próximo `POST /api/instance/s3/:instance` para alterar. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna contadores de mensagens (total, por tipo, por status, últimos 7/30 dias, contatos únicos, etc.) para uma instância **ou** um conjunto consolidado de várias ao mesmo tempo. ## Como funciona O parâmetro `:instance` aceita dois formatos: - **Uma instância única**: `/metrics/vendas` retorna as métricas dela - **Múltiplas instâncias**: `/metrics/vendas,suporte,marketing` retorna **um único objeto** com os valores **somados** entre elas Quando você passa várias, os valores numéricos são somados, os timestamps retornam o menor/maior (primeira mensagem / última mensagem), e o campo `instance` da resposta traz a lista original. ## Exemplos ### Uma instância Passando um único nome no path, retorna as métricas daquela instância isoladamente, ideal para dashboards individuais ou para ler estatísticas de um número específico. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/instance/metrics/vendas" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/metrics/vendas", { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/instance/metrics/vendas", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/instance/metrics/vendas", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Múltiplas (consolidado) Passando vários nomes separados por vírgula, retorna **um único objeto** com os valores numéricos somados entre as instâncias. Útil para visões agregadas de departamento ou conta. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/instance/metrics/vendas,suporte,marketing" \ -H "token: $Token_Account" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/metrics/vendas,suporte,marketing", { method: "GET", headers: { "token": process.env.Token_Account } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/instance/metrics/vendas,suporte,marketing", headers={ "token": os.environ["Token_Account"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/instance/metrics/vendas,suporte,marketing", nil) req.Header.Set("token", os.Getenv("Token_Account")) http.DefaultClient.Do(req) } ``` ## Path parameters Nome único (ex.: `vendas`) ou lista separada por vírgula (ex.: `vendas,suporte`). Espaços ao redor das vírgulas são aceitos. ## Headers TokenAccount ou TokenInstance. ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Metrics retrieved successfully", "metrics": { "instance": "vendas", "totalMessages": 1523, "messagesReceived": 981, "messagesSent": 542, "messagesByType": { "text": 1205, "image": 187, "audio": 72, "video": 34, "document": 15, "sticker": 10 }, "groupMessages": 384, "individualMessages": 1139, "messagesWithMedia": 318, "mediaByType": { "image": 187, "audio": 72, "video": 34, "document": 15, "sticker": 10 }, "messagesByStatus": { "sent": 542, "received": 981, "delivered": 320, "read": 210, "pending": 12 }, "uniqueChats": 97, "uniqueGroups": 14, "uniqueContacts": 83, "messagesLast7Days": 412, "messagesLast30Days": 1287, "firstMessageAt": "2026-02-01T15:32:10Z", "lastMessageAt": "2026-04-18T23:14:55Z" } } ``` ```json 200 OK { "success": true, "message": "Metrics retrieved successfully", "metrics": { "instance": "vendas,suporte", "totalMessages": 3410, "messagesReceived": 2105, "messagesSent": 1305, "messagesByType": { "text": 2890, "image": 290, "audio": 130 }, "...": "... demais campos somados" } } ``` ## Campos da resposta | Campo | O que é | | ----- | ------- | | `totalMessages` | Total de mensagens enviadas + recebidas | | `messagesReceived` / `messagesSent` | Quebra por direção | | `messagesByType` | Contagem por tipo (`text`, `image`, `audio`, `video`, `document`, `sticker`, etc.) | | `groupMessages` / `individualMessages` | Quebra entre grupo e 1-a-1 | | `messagesWithMedia` | Quantas tinham mídia anexada | | `mediaByType` | Contagem de mídias por tipo | | `messagesByStatus` | Quebra por status (`pending`, `sent`, `delivered`, `read`, `played`, `failed`) | | `uniqueChats` / `uniqueGroups` / `uniqueContacts` | Contagens únicas | | `messagesLast7Days` / `messagesLast30Days` | Recência | | `firstMessageAt` / `lastMessageAt` | Datas extremas | ## Erros | HTTP | Condição | | :--: | -------- | | 400 | Path vazio ou só com vírgulas | | 401 | Token ausente ou inválido | | 404 | Qualquer instância da lista não existe | | 429 | Mais de 100 requisições por minuto | ## Notas Se a instância tiver `ignoreGroupMessages` ativo, mensagens de grupo **não são contadas**, `groupMessages` pode aparecer como `0`. Para dashboards de alta frequência, considere armazenar os valores no seu lado, cada chamada executa cálculos completos e pode demorar em instâncias com muito volume. ## Próximo Estado atual de conexão e resumo das integrações. Ajuste flags de comportamento como `ignoreGroupMessages`. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Desvincula o dispositivo do WhatsApp (como clicar em "Sair" no WhatsApp Web) e fecha o websocket. O registro da instância **não** é apagado, o token continua válido para um futuro [`connect`](/pt/api/instance/connect) (com novo QR). ## Exemplo Faz `DELETE` no path de logout para desvincular o dispositivo do WhatsApp e fechar o WebSocket. O registro da instância é preservado, então o mesmo `token` pode ser reutilizado em uma futura chamada de `connect`. ```bash cURL curl -X DELETE "https://ryzeapi.cloud/api/instance/logout/minha-instancia" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/logout/minha-instancia", { method: "DELETE", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.delete( "https://ryzeapi.cloud/api/instance/logout/minha-instancia", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/instance/logout/minha-instancia", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Instance logged out", "instance": { "status": "loggedout" } } ``` ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. ## Erros | HTTP | `error.message` | Quando | | :--: | --------------- | ------ | | 401 | `Invalid token` | Token ausente ou inválido. | | 404 | `Instance not found` | Nome não existe. | | 429 | `Rate limit exceeded. Try again later.` | Mais de 100 req/min. | | 500 | `Failed to logout instance` | Falha interna. | ```json { "success": false, "error": { "message": "Instance not found" } } ``` ## Notas Após logout, **`reconnect` deixa de funcionar**, a sessão whatsmeow é apagada. Use [`connect`](/pt/api/instance/connect) para gerar um novo QR. **Não apaga** o registro da instância. Para remover completamente (incluindo configurações, webhook, dados), use [`DELETE /api/instance/delete/:instance`](/pt/api/instance/delete). ## Próximo Gere um novo QR para vincular outro dispositivo. Para remover tudo definitivamente. **Auth:** `TokenAccount` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim **Operação irreversível.** Faz logout no WhatsApp, remove a sessão whatsmeow e apaga todos os registros do banco (configurações, webhook, websocket, chatwoot, settings, S3 e a própria instância). Faça backup do que precisar antes. Não aceita **TokenInstance**, uma instância não pode pedir para se remover. Use o **TokenAccount** da conta dona. ## Exemplo Faz `DELETE` no path da instância usando o **TokenAccount**. A operação é irreversível: encerra a sessão whatsmeow, faz logout no WhatsApp e remove todos os registros da base (settings, webhook, websocket, chatwoot, proxy, S3 e a própria instância). ```bash cURL curl -X DELETE "https://ryzeapi.cloud/api/instance/delete/minha-instancia" \ -H "token: $Token_Account" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/instance/delete/minha-instancia", { method: "DELETE", headers: { "token": process.env.Token_Account } }); ``` ```python Python import os, requests requests.delete( "https://ryzeapi.cloud/api/instance/delete/minha-instancia", headers={ "token": os.environ["Token_Account"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/instance/delete/minha-instancia", nil) req.Header.Set("token", os.Getenv("Token_Account")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Instance deleted", "instance": { "status": "deleted" } } ``` ## Path parameters Nome da instância a deletar. ## Headers Seu **TokenAccount** (apenas o dono da instância pode deletá-la). ## O que acontece ao deletar - Se a instância estava conectada, a RyzeAPI faz **logout no WhatsApp** (o dispositivo some da lista de vinculados no celular). - Webhooks, WebSocket, integração Chatwoot e settings são removidos. - Todos os registros da instância são apagados da base. - Se você tinha S3 configurado, **os arquivos já enviados para o bucket não são apagados**, gerencie o bucket separadamente. ## Erros | HTTP | `error.message` | Quando | | :--: | --------------- | ------ | | 401 | `Invalid token` | Token ausente, inválido ou TokenInstance (não aceito). | | 404 | `Instance not found` | Nome não existe. | | 429 | `Rate limit exceeded. Try again later.` | Mais de 100 req/min. | | 500 | `Failed to delete instance: ` | Falha interna durante o teardown. | ```json { "success": false, "error": { "message": "Failed to delete instance: " } } ``` ## Notas Após deletar, criar uma nova instância com o **mesmo nome** pode eventualmente conflitar com a limpeza assíncrona do whatsmeow. Se acontecer, aguarde alguns segundos e tente novamente. ## Próximo Provisiona uma nova no lugar, opcionalmente já com webhook, websocket e chatwoot inline. Se você só quer desconectar do WhatsApp mantendo a instância. ### Eventos O módulo **Eventos** é como você recebe o que acontece na sua instância **em tempo real**. A RyzeAPI emite eventos por **dois canais independentes** que compartilham o mesmo envelope e o mesmo catálogo: A RyzeAPI faz `POST` na sua URL toda vez que um evento ocorre. Persistido em fila com retry/backoff e DLQ. Ideal para servidores, CRMs, automações. O cliente abre uma conexão e recebe os eventos na hora. Sem persistência, ideal para dashboards, painéis ao vivo, telas de atendimento. Nada impede usar os dois ao mesmo tempo: webhook cuida da entrega persistente para o backend, WebSocket faz a UI saltar quando o evento chega. ## Endpoints do módulo | Método | Path | Função | |--------|------|--------| | `POST` | `/api/events/webhook/:instance` | Configurar webhook (até 3 habilitados) | | `GET` | `/api/events/getWebhook/:instance` | Listar webhooks ou obter um por `?label=` | | `POST` | `/api/events/websocket/:instance` | Configurar WebSocket (`{"enabled": false}` desabilita) | | `GET` | `/api/events/getWebsocket/:instance` | Ler config atual do WebSocket | | `GET` | `/ws/:instance` | Conectar via WebSocket (upgrade) | A configuração de webhook e WebSocket também pode ser feita **na criação da instância**, basta enviar `webhookEnabled`, `webhookURL`, `websocketEnabled`, etc. no body de [`POST /api/instance/new`](/pt/api/instance/create). ## Envelope compartilhado Tanto webhook quanto WebSocket entregam o mesmo objeto: ```json { "event": "message.exchange", "data": { /* payload específico do evento */ }, "instanceData": { "baseUrl": "https://api.example.com", "instance": "$Instance_Name", "token": "" } } ``` `instanceData.token` é o token da própria instância, útil quando seu consumidor centraliza várias instâncias e precisa identificar a origem. ## Os 6 tipos de evento Mensagens enviadas/recebidas (texto, mídia, sticker, doc, enquete, contato, localização, botão/lista, edits e revogações). Recibos de entrega: `delivered`, `read`, `played`, `read_self`, `played_self` etc. Chamadas: `offer`, `accepted`, `rejected`, `terminated`, `notification`, `latency`. Mudanças em grupos: `joined`/`left`/`promoted`/`demoted` + metadata (`name`, `topic`, `announce`, `link`, etc.). Mudanças de conexão da instância: `connected`, `qr_ready`, `temp_banned`, `logged_out`, `pair_success`... Etiquetas WhatsApp Business: edição, associação a chats e mensagens. Schemas de payload, enums e exemplos de cada um dos 6 tipos. ## Webhook x WebSocket | Aspecto | Webhook | WebSocket | |---------|---------|-----------| | Protocolo | HTTP POST | WS (upgrade TCP) | | Entrega | Async, persistida em fila | Real-time, em memória | | Retry/DLQ | Backoff exponencial 1s→1h, até 5 tentativas + DLQ | Sem retry, eventos perdidos se cliente offline | | Filtro de eventos | `events[]` por config | `events[]` por config | | Mídia em base64 | `mediaBase64` opcional | `mediaBase64` opcional | | Autenticação | Header `Authorization` configurável | Token no upgrade (header ou `?token=`) | | Heartbeat | N/A | PING/PONG ~54s/60s | | Limite | Até **3 webhooks habilitados** por instância | 1 config por instância (broadcast a N clientes conectados) | | Backpressure | Fila cresce no DB | Buffer de 256 msgs por cliente; lentos são dropados | | Sobrevive a queda do consumer? | Sim | Não | ## Múltiplos webhooks por instância Cada instância aceita até **3 webhooks habilitados simultâneos**, você os identifica por um `label`. Permite, por exemplo: - `default` para o sistema principal de produção - `analytics-pipeline` para um sink de eventos - `staging-mirror` para validar mudanças em paralelo Cada `label` tem URL, eventos filtrados, `authorization` e `mediaBase64` próprios. Ver [configurar webhook](/pt/api/events/webhook-configure). ## Boas práticas **Responda 2xx em menos de 5s** no endpoint webhook, caso contrário, a entrega entra em retry com backoff exponencial. **Valide a origem** com o header `Authorization` configurado, não há HMAC automático, o consumidor é responsável. **`mediaBase64: false`** se você precisa só da URL/`s3Url`, economiza banda e DB. **Reconecte com backoff** no WebSocket, não há resumo de sessão; eventos perdidos não voltam. **Filtre `events[]`** quando souber exatamente o que vai consumir, reduz tráfego e processamento. ## Próximos passos `POST /api/events/webhook/:instance`, cria/atualiza por `label`. `GET /api/events/getWebhook/:instance`, todos ou por `?label=`. `POST /api/events/websocket/:instance`, `enabled`, `events`, `mediaBase64`. `GET /api/events/getWebsocket/:instance`. Schemas e exemplos dos 6 tipos. `GET /ws/:instance`, protocolo, auth, reconexão. Webhook e WebSocket compartilham o **mesmo envelope** e o **mesmo catálogo** de eventos. A única diferença é o canal de entrega, o `data` é idêntico. Esta página documenta os 6 tipos: **`message.exchange`**, **`message.status`**, **`call.update`**, **`group.flow`**, **`instance.state`** e **`label.update`**. ## Envelope ```json { "event": "", "data": { /* payload específico */ }, "instanceData": { "baseUrl": "https://api.example.com", "instance": "", "token": "" } } ``` Filtragem é feita pelo nome do `event` no campo `events` da config. Vazio = todos os tipos. ## Filtragem e roteamento ```json { "events": ["message.exchange", "call.update", "instance.state"] } ``` Quando `byEvents=true` (apenas em webhook), o nome do evento é **anexado à URL**: - Config: `url: "https://app/wh"`, `byEvents: true` - Delivery: `POST https://app/wh/message.exchange` Útil para roteamento por endpoint sem precisar inspecionar o payload. --- ## `message.exchange` Mensagens enviadas e recebidas (texto, mídia, sticker, documento, áudio, enquete, contato, localização, etc.), edits e revogações. ### Payload ```json { "id": "wamid.msg.123", "message": { "id": "wamid.msg.123", "direction": "incoming | outgoing", "timestamp": "2026-04-28T10:30:00Z", "chat": { "jid": "5511999999999", "lid": "100@s.whatsapp.net", "name": "João Silva", "type": "private | group | newsletter", "isCommunity": true }, "sender": { "jid": "5511999999999", "lid": "100@s.whatsapp.net", "name": "João Silva" }, "content": { "text": "Ola" }, "media": { "type": "image | video | audio | document | sticker | ptt | ptv", "url": "https://media-...whatsapp.net/...", "s3Url": "https://bucket.s3.amazonaws.com/...", "base64": "iVBORw0KGgo...", "mimetype": "image/jpeg", "size": 45678, "caption": "Foto do evento", "fileName": "doc.pdf" }, "edit": { "original_id": "wamid.original", "originalContent": "Texto antigo", "text": "Texto novo" }, "forward": { "count": 2 }, "reply": { "message_id": "wamid.replied", "sender": { "jid": "...", "name": "..." }, "text": "...", "media": { /* objeto media simplificado */ } }, "poll": { "title": "Qual sabor?", "options": [{ "name": "Chocolate", "count": 0 }] }, "location": { "latitude": -23.5505, "longitude": -46.6333, "address": "Av. Paulista, 1374" }, "contact": { "display_name": "Maria", "phone_number": "5511987654321", "vcard": "BEGIN:VCARD..." }, "list_response": { "title": "...", "body": "...", "list_type": "single_select", "single_select_reply": { "option_name": "p1" } }, "button_response": { "title": "Comprar", "body": "...", "selected_button_id": "buy_camiseta" }, "interactive": { /* form / native flow / outras superfícies */ }, "adOrigin": { "entryPointSource": "ctwa_ad | click_to_chat_link | global_search_new_chat | qr_code", "adType": "CTWA | CAWC", "sourceId": "120210350926390440", "ctwaClid": "AfjMN6zW...", "sourceApp": "instagram | facebook", "sourceType": "ad", "sourceUrl": "https://www.instagram.com/p/DGnlj_hAqGX/", "title": "Príncipe do Mutá Hotel Design", "body": "Aqui, seus dias são mais felizes!", "mediaType": "IMAGE | VIDEO", "thumbnailUrl": "https://scontent-...fbcdn.net/...", "greetingMessageBody": "Olá! Como podemos ajudar?", "conversionSource": "FB_Ads", "entryPointExternalSource": "FB_Ads", "ctwaPayload": "QWZqa2xTQkt...(base64)", "originalImageUrl": "https://.../ad-creative.jpg", "clickToWhatsappCall": true } } } ``` ### Campos condicionais Apenas os campos relevantes para o tipo da mensagem são preenchidos. Edits têm `edit` populado; revogações chegam com `type: "message_revoke"` em `data.message.type`. `chat.isCommunity` aparece **apenas quando `true`**, indica que o chat é o canal de aviso (parent / announcement channel) de uma comunidade WhatsApp. Subgrupos vinculados a uma comunidade continuam com `type: "group"` e **sem** o campo `isCommunity`. Em grupos comuns e DMs o campo também é omitido. `media.base64` só aparece quando `mediaBase64=true` na config (webhook ou WebSocket). Caso contrário, use `media.url` (whatsapp.net, expira) ou `media.s3Url` (se S3 estiver configurado na instância). ### `adOrigin` — atribuição de origem (Click-to-WhatsApp) Presente **apenas na primeira mensagem recebida** de uma conversa iniciada por um anúncio Meta ou por um ponto de entrada (link wa.me, busca, QR). Permite rotear o lead por campanha sem consultar o banco. Dois cenários: - **Anúncio nativo (Click-to-WhatsApp / Call Ads)** — `entryPointSource` é `ctwa_ad` e vem o bloco completo: `sourceId` (ID do anúncio — agrupa leads por campanha), `ctwaClid` (chave de atribuição da Meta, use na Conversions API), `sourceApp`, `sourceUrl`, `title`, `body`, `mediaType`, `greetingMessageBody`, além de `conversionSource`, `entryPointExternalSource`, `ctwaPayload` (token base64 para a Conversions API), `originalImageUrl` e `clickToWhatsappCall`. - **Ponto de entrada sem anúncio** — link wa.me (`click_to_chat_link`), busca do WhatsApp (`global_search_new_chat`), QR code, etc. Vem **apenas** `entryPointSource` (e, quando disponível, `entryPointApp` / `entryPointDelaySeconds`), **sem** `sourceId` / `ctwaClid` — a Meta não anexa dados de anúncio a esses. Mensagens orgânicas — e qualquer mensagem que não seja a **primeira** da conversa — **não** trazem `adOrigin`. O texto pré-preenchido de um anúncio **não** é prova de origem: a atribuição confiável vem de `sourceId` / `ctwaClid`. ### Exemplo (recebimento de imagem) ```json { "event": "message.exchange", "data": { "id": "wamid.123", "message": { "id": "wamid.123", "direction": "incoming", "timestamp": "2026-04-28T10:30:00Z", "chat": { "jid": "5511999999999", "name": "João", "type": "private" }, "sender": { "jid": "5511999999999", "name": "João" }, "media": { "type": "image", "url": "https://media-abc.whatsapp.net/...", "mimetype": "image/jpeg", "size": 45678, "caption": "Foto" } } }, "instanceData": { "baseUrl": "https://api...", "instance": "minha", "token": "..." } } ``` --- ## `message.status` Recibos de entrega: delivered, read, played, etc. ### Payload ```json { "status": "delivered | read | played | sender | read_self | played_self | retry | inactive | server_error", "messageIds": ["wamid.123", "wamid.124"], "timestamp": "2026-04-28T10:45:00Z", "chat": { "jid": "5511999999999", "type": "private | group | broadcast", "isCommunity": true }, "recipient": "5511999999999", "messageSender": null, "isFromMe": false } ``` ### Enum `status` | Valor | Significado | |-------|-------------| | `delivered` | Mensagem chegou no device do destinatário. | | `read` | Destinatário leu (read receipts ativos). | | `played` | Áudio/voice note foi reproduzido. | | `sender` | Eco interno (a própria origem reportando entrega). | | `read_self` | O **próprio usuário** marcou como lido em outro device. | | `played_self` | O próprio usuário reproduziu em outro device. | | `retry` | Servidor pediu reentrega (transient). | | `inactive` | Destinatário offline há tempo demais. | | `server_error` | Erro genérico do servidor WhatsApp. | - `messageSender` em **grupos**: JID do autor original da mensagem (relevante quando alguém leu uma mensagem de outro participante). - `chat.isCommunity` segue a mesma regra de `message.exchange`: presente e `true` apenas quando o chat é o canal de aviso de comunidade. --- ## `call.update` Eventos de chamada: oferta, aceite, recusa, encerramento, latência. ### Payload ```json { "type": "offer | accepted | rejected | terminated | notification | latency", "direction": "incoming | outgoing", "callId": "call-abc123", "from": "5511999999999", "to": "5511888888888", "timestamp": "2026-04-28T10:35:00Z", "groupJid": null, "callMedia": "audio | video | null", "remotePlatform": "Android | iPhone | null", "remoteVersion": "2.23.15.74", "noticeType": "group | null", "reason": null, "duration": 123, "latency": 45.6, "latencyStatus": "Excellent | Good | Average | Poor" } ``` ### Enum `type` | Valor | Quando dispara | |-------|----------------| | `offer` | Chamada recebida/enviada (toque inicial). | | `accepted` | Lado remoto atendeu. | | `rejected` | Lado remoto rejeitou. | | `terminated` | Chamada encerrada, `duration` em segundos vem populado. | | `notification` | Notificação contextual de chamada (ex.: chamada perdida em grupo). | | `latency` | Métrica de latência durante a chamada (`latency` em ms, `latencyStatus`). | Para rejeitar chamadas automaticamente, configure `autoRejectCalls=true` no [bloco settings da instância](/pt/api/instance/settings-update), você ainda recebe os eventos `offer` + `rejected` no webhook. --- ## `group.flow` Mudanças em grupos: membros, metadata, settings. ### Payload, participant change ```json { "type": "joined | left | promoted | demoted", "groupJid": "120363406289005073@g.us", "groupName": "Time de Dev", "timestamp": "2026-04-28T11:00:00Z", "participants": [ { "jid": "5511999999999", "action": "joined" } ] } ``` ### Subtipos de metadata | `type` | Significado | |--------|-------------| | `name` | Nome do grupo alterado | | `topic` | Descrição alterada | | `locked` / `unlocked` | Members can/cannot edit info | | `announce` / `not_announce` | Members can/cannot send messages | | `ephemeral` / `not_ephemeral` | Mensagens efêmeras on/off | | `invite` | Invite link gerado | | `link` / `unlink` | Subgrupo vinculado/desvinculado de comunidade | | `delete` | Grupo deletado | | `membership_approval` | Modo de aprovação de novos membros alterado | | `suspended` / `unsuspended` | Grupo suspenso/reativado pelo WhatsApp | --- ## `instance.state` Mudanças no estado da própria instância (conexão, QR, ban, pareamento). ### Payload ```json { "instance": "minha", "state": "connected | disconnected | logged_out | stream_replaced | temp_banned | client_outdated | connect_failure | stream_error | cat_refresh_error | qr_ready | pair_success | pair_error | qr_scanned_no_multidevice | keepalive_timeout | keepalive_restored | manual_reconnect", "timestamp": "2026-04-28T10:50:00Z", "reason": "...", "reasonCode": 123, "message": "...", "expireAt": "2026-04-28T11:50:00Z", "expireInSeconds": 3600, "onConnect": true, "codes": ["1@abc...,xyz==,base64string"], "jid": "5511999999999@s.whatsapp.net", "platform": "iPhone | Android | Desktop", "errorMsg": "..." } ``` ### Enum `state` | State | Significado | Campos extras populados | |-------|-------------|-------------------------| | `connected` | Sessão ativa, pronta para enviar/receber. |, | | `disconnected` | Desconectado (transient, geralmente reconecta sozinho). |, | | `logged_out` | Sessão invalidada (precisa novo `connect`). | `reason` | | `stream_replaced` | Outra sessão tomou o lugar (multi-device conflict). |, | | `temp_banned` | Ban temporário aplicado pelo WhatsApp. | `expireAt`, `expireInSeconds`, `reason` | | `client_outdated` | Versão do WhatsApp Web em uso está obsoleta, entre em contato com o suporte. | `message` | | `connect_failure` | Falha durante conexão. | `reason`, `reasonCode` | | `stream_error` | Erro de stream do WhatsApp. | `errorMsg` | | `cat_refresh_error` | Falha ao renovar credenciais (`cat`). | `errorMsg` | | `qr_ready` | Novo QR disponível para pareamento. | `codes[]`, `onConnect` | | `pair_success` | Pareamento concluído. | `jid`, `platform` | | `pair_error` | Erro durante o pareamento. | `errorMsg` | | `qr_scanned_no_multidevice` | QR escaneado mas o device-alvo não tem multidevice. |, | | `keepalive_timeout` | Conexão instável, keepalive não respondido. |, | | `keepalive_restored` | Conexão estabilizou após `keepalive_timeout`. |, | | `manual_reconnect` | Reconexão disparada manualmente via REST. |, | Para o seu cliente saber quando recarregar o QR na UI, escute `instance.state` com `state=qr_ready` e renderize `data.codes[0]`. --- ## `label.update` Edição/associação de etiquetas (WhatsApp Business labels). ### Payload ```json { "type": "edit | chat | message", "labelId": "1", "timestamp": "2026-04-28T10:00:00Z", "action": "updated | deleted | add | remove", // type=edit: "name": "Important", "color": 0, "labelType": "CUSTOM", "isActive": true, "isImmutable": false, "orderIndex": 1, "deleted": false, // type=chat: "chatJid": "5511999999999@s.whatsapp.net", "labeled": true, // type=message: "messageId": "wamid.abc" } ``` ### Combinações `type` × `action` | `type` | `action` válida | Campos extras | |--------|------------------|---------------| | `edit` | `updated`, `deleted` | `name`, `color`, `labelType`, `isActive`, `isImmutable`, `orderIndex`, `deleted` | | `chat` | `add`, `remove` | `chatJid`, `labeled` | | `message` | `add`, `remove` | `chatJid`, `messageId` | --- ## Eventos não emitidos (internos) Capturados pelo handler `whatsmeow` mas **não** propagados via webhook/WS: - `*events.Picture`, mudança de foto de perfil (apenas logado). - `*events.FBMessage`, Facebook Business (apenas logado). - `*events.HistorySync`, sync de histórico (processado e gravado em DB). Se você precisar consumir alguma dessas mudanças, faça polling pelos endpoints REST correspondentes (perfil, histórico). ## Referências Filtre os eventos via `events[]` na config. Mesma sintaxe de filtro do webhook. Receba os eventos em tempo real. Comparação webhook x WebSocket. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim (upsert por `label`) ## Descrição Cria ou atualiza a linha `(instance, label)` em `webhook_configs`. Cada instância aceita até **3 webhooks habilitados simultâneos**, identificados por um `label` livre. Webhooks com `enabled=false` ficam preservados no banco mas não contam para o limite e não recebem entregas. Casos de uso: - **Criar novo webhook**: envie um `label` ainda não usado. - **Atualizar existente**: envie o mesmo `label`, todos os campos novos sobrescrevem os antigos. - **Soft-disable**: envie o mesmo `label` com `{"enabled": false}` (limpa `url`, `authorization`, `events`, `mediaBase64`, mas preserva a linha). - **Migração paralela**: rode o webhook antigo enquanto valida o novo num `label` diferente; quando confirmar, desabilita o antigo. ## Exemplos ### Mínimo Habilita o webhook `default` apontando para `https://meuapp.com/webhook`. Sem `events`, recebe os 6 tipos; sem `authorization`, não envia header de autenticação. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/events/webhook/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "enabled": true, "url": "https://meuapp.com/webhook" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/events/webhook/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ enabled: true, url: "https://meuapp.com/webhook" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/events/webhook/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "enabled": True, "url": "https://meuapp.com/webhook" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "enabled": true, "url": "https://meuapp.com/webhook" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/events/webhook/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com label, filtro e Authorization Cria um webhook nomeado `analytics-pipeline` que recebe apenas `message.exchange` e `message.status`, envia o header `Authorization: Bearer svc-token-xyz` em cada entrega e desliga o backup em base64. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/events/webhook/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "label": "analytics-pipeline", "enabled": true, "url": "https://analytics.meuapp.com/events", "authorization": "Bearer svc-token-xyz", "events": ["message.exchange", "message.status"], "mediaBase64": false }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/events/webhook/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ label: "analytics-pipeline", enabled: true, url: "https://analytics.meuapp.com/events", authorization: "Bearer svc-token-xyz", events: ["message.exchange", "message.status"], mediaBase64: false }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/events/webhook/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "label": "analytics-pipeline", "enabled": True, "url": "https://analytics.meuapp.com/events", "authorization": "Bearer svc-token-xyz", "events": ["message.exchange", "message.status"], "mediaBase64": False } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "label": "analytics-pipeline", "enabled": true, "url": "https://analytics.meuapp.com/events", "authorization": "Bearer svc-token-xyz", "events": ["message.exchange", "message.status"], "mediaBase64": false }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/events/webhook/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### byEvents = true (roteamento por URL) Liga `byEvents: true` para que cada delivery seja enviada com o nome do evento como sufixo da URL (ex.: `https://meuapp.com/wh/message.exchange`), permitindo rotear no servidor por endpoint sem precisar inspecionar o payload. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/events/webhook/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "label": "router", "enabled": true, "url": "https://meuapp.com/wh", "byEvents": true, "events": [] }' # Entregas vão para https://meuapp.com/wh/message.exchange, /group.flow, ... ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/events/webhook/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ label: "router", enabled: true, url: "https://meuapp.com/wh", byEvents: true, events: [] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/events/webhook/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "label": "router", "enabled": True, "url": "https://meuapp.com/wh", "byEvents": True, "events": [] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "label": "router", "enabled": true, "url": "https://meuapp.com/wh", "byEvents": true, "events": [] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/events/webhook/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Soft-disable preservando o label Desliga o webhook `analytics-pipeline` enviando apenas `enabled: false`. A linha permanece no banco mas `url`, `authorization`, `events` e `mediaBase64` são zerados, e a entrada deixa de contar para o limite de 3 webhooks ativos. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/events/webhook/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "label": "analytics-pipeline", "enabled": false }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/events/webhook/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ label: "analytics-pipeline", enabled: false }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/events/webhook/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "label": "analytics-pipeline", "enabled": False } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "label": "analytics-pipeline", "enabled": false }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/events/webhook/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Mídia em base64 (backup raw) Configura um webhook dedicado a backup que recebe somente `message.exchange` com `mediaBase64: true`, fazendo cada mensagem com mídia incluir o conteúdo binário codificado em base64 dentro do payload. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/events/webhook/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "label": "backup-raw", "enabled": true, "url": "https://backup.meuapp.com/raw", "events": ["message.exchange"], "mediaBase64": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/events/webhook/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ label: "backup-raw", enabled: true, url: "https://backup.meuapp.com/raw", events: ["message.exchange"], mediaBase64: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/events/webhook/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "label": "backup-raw", "enabled": True, "url": "https://backup.meuapp.com/raw", "events": ["message.exchange"], "mediaBase64": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "label": "backup-raw", "enabled": true, "url": "https://backup.meuapp.com/raw", "events": ["message.exchange"], "mediaBase64": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/events/webhook/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta devolve o objeto `webhook` com a configuração efetivamente persistida (`label`, `enabled`, `url`, `authorization`, `byEvents`, `events`, `mediaBase64`), espelha o body do request após o upsert. Quando `enabled=false`, os campos `url`, `authorization`, `events` e `mediaBase64` voltam zerados. O dispatcher passa a usar a nova config imediatamente (o cache interno de 30s é invalidado no save). ```json 200 OK { "success": true, "message": "Webhook configured successfully", "webhook": { "label": "default", "enabled": true, "url": "https://meuapp.com/webhook", "authorization": "Bearer secret-key-123", "byEvents": false, "events": ["message.exchange", "call.update"], "mediaBase64": true } } ``` ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Identificador local. Máx. 50 chars; aceita `[a-zA-Z0-9_-]`. Vazio ou omitido vira `"default"`. Permite múltiplos webhooks por instância. Liga/desliga o webhook. Quando `false`, os campos `url`, `authorization`, `byEvents`, `events` e `mediaBase64` são **zerados antes de salvar**. URL de destino. **Obrigatória quando `enabled=true`**. Passa pelo SSRF guard (ver abaixo), bloqueia `localhost`, IPs privados, link-local, multicast. Conteúdo literal do header `Authorization` enviado em cada delivery (ex.: `Bearer secret-key-123`). **Encriptado at-rest** com AES-256-GCM quando `ENCRYPTION_KEY` está configurada. Se `true`, a URL recebe sufixo `/` em cada entrega, útil para roteamento por endpoint sem inspecionar o payload (`https://app/wh/message.exchange`). Filtro. Array vazio = recebe todos os 6 tipos. Cada entrada precisa estar em `{message.exchange, message.status, call.update, group.flow, instance.state, label.update}`. Quando `true`, eventos `message.exchange` com mídia incluem `media.base64` (aumenta payload, pode passar de 100KB). ## SSRF guard A `url` é validada **na configuração e antes de cada delivery**. Bloqueia destinos que apontem para a infraestrutura interna: | Faixa | Exemplo | |-------|---------| | Loopback | `localhost`, `127.0.0.1`, `::1` | | IPv4 privado | `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` | | Link-local IPv4 | `169.254.0.0/16` | | Link-local IPv6 | `fe80::/10` | | Multicast / broadcast | `224.0.0.0/4`, `255.255.255.255` | Se você precisa testar contra um servidor local em desenvolvimento, exponha-o por um túnel público (ngrok, cloudflared, localhost.run), a SSRF guard é ativa em todos os ambientes. ## Notas - **`authorization` vazio vs. `null`**: envie `null` ou omita para não enviar header. String vazia `""` resultaria em `Authorization:` vazio, que alguns proxies rejeitam. - **`enabled=false` zera os campos**: re-habilitar o mesmo `label` depois exige re-enviar a `url` (e os outros campos que você quiser preservar). - **Não há `DELETE`**: para "remover" um webhook, faça `POST` com `{"enabled": false}` mantendo o `label`. Operadores podem inspecionar/limpar a linha diretamente no banco quando necessário. - **Encriptação opcional**: se `ENCRYPTION_KEY` não está configurada, o `authorization` é armazenado em texto puro. Em produção, sempre configure a chave. - **Cache invalidado**: a config nova fica visível para o dispatcher imediatamente (TTL interno de 30s é invalidado no save). ## Erros | HTTP | `error.message` | |------|-----------------| | 400 | `Invalid request body` | | 400 | `URL is required when enabled is true` | | 400 | `URL must not target localhost or private network` | | 400 | `invalid event type: ` | | 400 | `label too long (max 50 chars)` | | 400 | `label may only contain letters, digits, underscore or dash` | | 401 | `Invalid token` | | 404 | `Instance not found` | | 409 | `webhook limit reached (max 3 enabled per instance)` | | 429 | `Rate limit exceeded. Try again later.` | | 500 | `Failed to get instance` | Envelope: ```json { "success": false, "error": { "message": "URL must not target localhost or private network" } } ``` O check do limite de 3 só é aplicado ao **criar** uma linha nova com `enabled=true`. Editar uma linha já existente (mesmo `label`) nunca aciona o limite. ## Próximo `GET /api/events/getWebhook/:instance`, todos ou por `?label=`. O que cada `event` carrega no `data`. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Endpoint de leitura. Sem query, retorna **todos** os webhooks (habilitados e desabilitados) da instância. Com `?label=`, retorna o único webhook desse label (ou `404`). ## Exemplos ### Listar todos Sem query string, retorna o array `webhooks[]` com todos os webhooks da instância (habilitados e desabilitados), ordenado alfabeticamente por `label`. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/events/getWebhook/$Instance_Name" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/events/getWebhook/${process.env.Instance_Name}`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/events/getWebhook/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/events/getWebhook/"+os.Getenv("Instance_Name"), nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Um específico Passando `?label=analytics-pipeline`, retorna apenas o webhook desse label no campo `webhook` do envelope (ou `404` se não existir). ```bash cURL curl -X GET "https://ryzeapi.cloud/api/events/getWebhook/$Instance_Name?label=analytics-pipeline" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/events/getWebhook/${process.env.Instance_Name}?label=analytics-pipeline`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/events/getWebhook/{os.environ['Instance_Name']}?label=analytics-pipeline", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/events/getWebhook/"+os.Getenv("Instance_Name")+"?label=analytics-pipeline", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Default explícito Busca o webhook padrão informando `?label=default`. Útil quando você criou o webhook sem especificar `label` e quer ler somente essa entrada em vez da lista completa. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/events/getWebhook/$Instance_Name?label=default" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/events/getWebhook/${process.env.Instance_Name}?label=default`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/events/getWebhook/{os.environ['Instance_Name']}?label=default", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/events/getWebhook/"+os.Getenv("Instance_Name")+"?label=default", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Sem `?label=`, retorna `webhooks[]` (ordenado alfabeticamente por `label`, sempre presente, vem `[]` se nenhum webhook existir, e **inclui também os com `enabled=false`** para inspeção operacional). Com `?label=`, retorna o objeto único em `webhook` (mesmo shape do `POST`). O `authorization` vem descriptografado quando `ENCRYPTION_KEY` está configurada; se a chave foi rotacionada e algum valor não decifra, o campo retorna criptografado em vez de derrubar o request. ```json 200 OK (lista, sem ?label=) { "success": true, "message": "Webhook configurations retrieved", "webhooks": [ { "label": "analytics-pipeline", "enabled": true, "url": "https://analytics.meuapp.com/events", "authorization": "Bearer svc-token-xyz", "byEvents": false, "events": ["message.exchange", "message.status"], "mediaBase64": false }, { "label": "default", "enabled": true, "url": "https://meuapp.com/webhook", "byEvents": false, "events": [], "mediaBase64": false }, { "label": "legacy", "enabled": false, "url": "", "byEvents": false, "events": [], "mediaBase64": false } ] } ``` ```json 200 OK (?label=analytics-pipeline) { "success": true, "message": "Webhook configuration retrieved", "webhook": { "label": "analytics-pipeline", "enabled": true, "url": "https://analytics.meuapp.com/events", "authorization": "Bearer svc-token-xyz", "byEvents": false, "events": ["message.exchange", "message.status"], "mediaBase64": false } } ``` Presente apenas quando **não** há `?label=`. Ordenado alfabeticamente por `label`. Sempre retornado mesmo sem nenhum webhook (`webhooks: []`). Presente apenas quando há `?label=`. Mesmo shape do `POST`. ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Query parameters Quando presente, retorna um único webhook (`webhook` no envelope). Ausente, retorna a lista (`webhooks[]`). Passar `?label=` (vazio) ainda é considerado "presente" → vira `"default"` e busca a linha desse label. - **Listagem inclui `enabled=false`**, operadores veem o histórico completo. Para listar só ativos, filtre no cliente por `w.enabled === true`. - **`authorization` descriptografado**: se `ENCRYPTION_KEY` está configurada e o valor estiver criptografado at-rest, o repositório descriptografa antes de retornar. Se a chave foi rotacionada e algum valor não decifra, o campo retorna criptografado (com warning no log) em vez de derrubar o request. --- ## Entrega: queue, retry, DLQ A entrega de webhooks é **assíncrona** e persistida. Cada evento que matcha um webhook é enfileirado em `webhook_queue` e processado por workers em paralelo. ### Fluxo ``` EventsService.sendWebhook() └─► webhook_queue.Enqueue() [INSERT row, status=pending] │ WebhookDispatcher (4 workers + janitor) ──┘ ├─► ClaimNext() (FOR UPDATE SKIP LOCKED) ├─► HTTP POST + SSRF re-check ├─► MarkDelivered (2xx) ├─► MarkRetry (4xx exceto 408/429, reentregar; 408/429/5xx, retry) └─► MarkFailed (attempts > max_attempts → DLQ) Janitor: a cada 15min, DELETE de rows delivered há > 24h. ``` ### Backoff exponencial | Tentativa | Próximo retry | |-----------|---------------| | 1 (fail) | +1s | | 2 (fail) | +5s | | 3 (fail) | +30s | | 4 (fail) | +5min | | 5 (fail) | +30min | | 6+ | +1h (cap) | Após `max_attempts` (default 5), `status` vira `failed` (DLQ). A linha **não é** deletada automaticamente, operadores podem inspecionar `last_error` e re-enfileirar manualmente (`UPDATE webhook_queue SET status='pending', next_retry_at=now()`). ### Tabela `webhook_queue` (resumo para ops) | Coluna | Descrição | |--------|-----------| | `id` | `BIGSERIAL` PK | | `instance_name` | Nome da instância | | `url` | Destino final (já com sufixo `/event-name` se `byEvents`) | | `payload` | `BYTEA`, corpo JSON do evento | | `auth_header` | `Authorization` configurado (encriptado at-rest) | | `event_type` | Nome do evento (`message.exchange` etc.) | | `status` | `pending` \| `delivered` \| `failed` | | `attempts` | Contador de tentativas | | `max_attempts` | Default 5 | | `next_retry_at` | Próxima janela de tentativa | | `last_error` | Última mensagem de erro do consumer | | `created_at` / `updated_at` | Timestamps | ## Headers entregues Cada `POST` ao seu webhook chega com: ```http POST HTTP/1.1 Host: Content-Type: application/json Authorization: # se configurado User-Agent: RyzeAPI/ { "event": "...", "data": { ... }, "instanceData": { ... } } ``` **Não há HMAC automático.** A validação de origem é responsabilidade do consumidor, configure um `authorization` (Bearer token, API key) e valide no seu endpoint. ## Erros | HTTP | `error.message` | Aplica em | |------|-----------------|-----------| | 400 | `label too long (max 50 chars)` | `?label=` | | 400 | `label may only contain letters, digits, underscore or dash` | `?label=` | | 401 | `Invalid token` | ambos | | 404 | `Instance not found` | ambos | | 404 | `Webhook not configured for this label` | `?label=` | | 429 | `Rate limit exceeded. Try again later.` | ambos | | 500 | `Failed to get instance` | ambos | | 500 | `Failed to list webhook configurations` | sem query | Envelope: ```json { "success": false, "error": { "message": "Webhook not configured for this label" } } ``` ## Próximo `POST /api/events/webhook/:instance` Schemas dos 6 tipos de evento. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim (upsert) ## Descrição Habilita / desabilita o canal WebSocket da instância e define o filtro de eventos. Diferente do webhook, existe **uma única configuração** por instância (não há `label`). Esse endpoint **não abre conexão**, apenas autoriza o upgrade posterior em [`GET /ws/:instance`](/pt/api/websocket). ## Exemplos ### Habilitar tudo Liga o WebSocket sem filtro: como `events` é omitido, o cliente recebe os 6 tipos de evento, e `mediaBase64` permanece em `false`. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/events/websocket/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"enabled": true}' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/events/websocket/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ enabled: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/events/websocket/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={"enabled": True} ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{"enabled": true}`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/events/websocket/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Filtro estreito Habilita o WebSocket recebendo somente `message.exchange` e `message.status` e ativa `mediaBase64: true` para que os frames com mídia já tragam o conteúdo binário codificado em base64. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/events/websocket/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "enabled": true, "events": ["message.exchange", "message.status"], "mediaBase64": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/events/websocket/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ enabled: true, events: ["message.exchange", "message.status"], mediaBase64: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/events/websocket/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "enabled": True, "events": ["message.exchange", "message.status"], "mediaBase64": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "enabled": true, "events": ["message.exchange", "message.status"], "mediaBase64": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/events/websocket/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Desativar o websocket Desliga o WebSocket enviando `enabled: false`. A linha de configuração é preservada, `events` e `mediaBase64` são zerados, e novas conexões em `/ws/:instance` passam a ser rejeitadas. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/events/websocket/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"enabled": false}' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/events/websocket/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ enabled: false }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/events/websocket/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={"enabled": False} ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{"enabled": false}`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/events/websocket/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta devolve o objeto `websocket` com a configuração efetivamente persistida (`enabled`, `events`, `mediaBase64`), espelha o body do request após o upsert. Quando `enabled=false`, `events` e `mediaBase64` voltam zerados; conexões já abertas em `/ws/:instance` permanecem até serem fechadas naturalmente, mas novas conexões passam a ser rejeitadas com `400`. ```json 200 OK { "success": true, "message": "WebSocket configured successfully", "websocket": { "enabled": true, "events": ["message.exchange", "instance.state"], "mediaBase64": false } } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Liga/desliga o WebSocket. Quando `false`, `events` e `mediaBase64` são **zerados antes do save**. Filtro. Array vazio = recebe todos os 6 tipos. Valores devem estar em `{message.exchange, message.status, call.update, group.flow, instance.state, label.update}`. Quando `true`, eventos `message.exchange` com mídia incluem `media.base64` nos frames WS. ## Notas - **Não persiste eventos**: WebSocket é efêmero. Se ninguém estiver conectado no momento do evento, ele é descartado (fast-path `HasClients` antes de qualquer trabalho de serialização). - **Sem retry**: se o socket cair durante o envio, a mensagem é perdida. Para entrega garantida, use webhook. - **`enabled=false` não desconecta clientes já abertos**: as conexões existentes em `/ws/:instance` permanecem até serem fechadas naturalmente; novas conexões falham com `400`. - **Sem limite documentado de conexões**: cada instância pode ter N clientes simultâneos (broadcast). O hub mantém buffer de 256 mensagens por cliente, clientes lentos são desconectados automaticamente. - **Configuração na criação**: o mesmo bloco pode ser passado em [`POST /api/instance/new`](/pt/api/instance/create) via `websocketEnabled`, `websocketEvents`, `websocketMediaBase64`. ## Erros | HTTP | `error.message` | |------|-----------------| | 400 | `Invalid request body` | | 401 | `Invalid token` | | 404 | `Instance not found` | | 429 | `Rate limit exceeded. Try again later.` | | 500 | `Failed to get instance` | Envelope: ```json { "success": false, "error": { "message": "Invalid request body" } } ``` ## Próximo `GET /api/events/getWebsocket/:instance` `GET /ws/:instance`, protocolo, auth, reconexão. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna a configuração atual do WebSocket da instância. Retorna `404` quando não há linha em `websocket_configs` (a instância nunca foi configurada via `POST`), não existe "config default implícita". Atenção à grafia: o path correto é **`getWebsocket`** (`w` minúsculo em `socket`), diferente de `POST /websocket`. Esta inconsistência é histórica, use o literal exato do registro. ## Parâmetros de rota Nome da instância. ## Exemplo ```bash cURL curl -X GET "https://ryzeapi.cloud/api/events/getWebsocket/$Instance_Name" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/events/getWebsocket/${process.env.Instance_Name}`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/events/getWebsocket/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/events/getWebsocket/"+os.Getenv("Instance_Name"), nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Headers `TokenAccount` ou `TokenInstance`. ## Resposta de sucesso Retorna o objeto `websocket` com a configuração atual: `enabled`, `events` e `mediaBase64`. Atenção: `404` significa "nunca foi configurado" (não há linha em `websocket_configs`), **diferente** de `enabled=false` (que retorna `200` com a linha persistida e os campos zerados). Trate os dois casos no cliente. ```json 200 OK { "success": true, "message": "WebSocket configuration retrieved", "websocket": { "enabled": true, "events": ["message.exchange"], "mediaBase64": false } } ``` ```json 200 OK (desabilitado) { "success": true, "message": "WebSocket configuration retrieved", "websocket": { "enabled": false, "events": [], "mediaBase64": false } } ``` ## Erros | HTTP | `error.message` | |------|-----------------| | 401 | `Invalid token` | | 404 | `Instance not found` | | 404 | `WebSocket not configured for this instance` | | 429 | `Rate limit exceeded. Try again later.` | | 500 | `Failed to get instance` | | 500 | `Failed to get websocket configuration` | Envelope: ```json { "success": false, "error": { "message": "WebSocket not configured for this instance" } } ``` ## Notas - **`404` ≠ "desabilitado"**: `404` significa "nunca criou linha"; um `enabled=false` retorna `200` normalmente com `"enabled": false`. Trate os dois casos no cliente. - **Sem alias `GET /websocket/:instance`**: diferente do webhook, **não há** um alias na grafia "regular", apenas `getWebsocket` (minúsculo). ## Próximo `POST /api/events/websocket/:instance` `GET /ws/:instance` **Auth:** `TokenAccount` ou `TokenInstance` (header ou query) • **Protocolo:** WSS/WS • **Rate-limit:** `Global` (100/min sobre o upgrade) ## Descrição Endpoint de upgrade HTTP → WebSocket para receber eventos em tempo real. Os frames enviados pelo servidor têm o **mesmo envelope** dos webhooks, texto JSON com `event`, `data` e `instanceData`. A configuração (habilitar, filtrar eventos, ativar `mediaBase64`) é feita em [`POST /api/events/websocket/:instance`](/pt/api/events/websocket-configure). Esta página cobre apenas a camada de conexão. Pré-requisito: a instância precisa ter o WebSocket configurado com `enabled=true`. Sem isso, o upgrade falha com `400` antes de virar conexão WS. ## Exemplos cURL (handshake) `websocat` ou `wscat` dão experiência interativa. cURL serve só para inspecionar o handshake. ### Handshake (inspeção) Faz o handshake bruto de upgrade HTTP→WebSocket apenas para inspecionar status, headers e validar que a instância está aceitando conexões. Não mantém o canal aberto, é uma sondagem one-shot. ```bash cURL curl -v --include \ --header "Connection: Upgrade" \ --header "Upgrade: websocket" \ --header "Sec-WebSocket-Key: $(openssl rand -base64 16)" \ --header "Sec-WebSocket-Version: 13" \ --header "token: $Token_Instance" \ "https://ryzeapi.cloud/ws/$Instance_Name" ``` ```javascript JavaScript // Handshake bruto via Node (sem libs WS) - apenas para inspecionar import https from "node:https"; import crypto from "node:crypto"; const req = https.request({ hostname: "ryzeapi.cloud", path: `/ws/${process.env.Instance_Name}`, method: "GET", headers: { "Connection": "Upgrade", "Upgrade": "websocket", "Sec-WebSocket-Key": crypto.randomBytes(16).toString("base64"), "Sec-WebSocket-Version": "13", "token": process.env.Token_Instance } }); req.on("upgrade", (res, socket) => { console.log("Upgraded:", res.statusCode); socket.end(); }); req.end(); ``` ```python Python # Handshake bruto - apenas para inspecionar import os, base64, secrets, http.client conn = http.client.HTTPSConnection("ryzeapi.cloud") conn.request( "GET", f"/ws/{os.environ['Instance_Name']}", headers={ "Connection": "Upgrade", "Upgrade": "websocket", "Sec-WebSocket-Key": base64.b64encode(secrets.token_bytes(16)).decode(), "Sec-WebSocket-Version": "13", "token": os.environ["Token_Instance"] } ) resp = conn.getresponse() print("Status:", resp.status) ``` ```go Go package main import ( "crypto/rand" "encoding/base64" "log" "net/http" "os" ) func main() { key := make([]byte, 16) rand.Read(key) req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/ws/"+os.Getenv("Instance_Name"), nil) req.Header.Set("Connection", "Upgrade") req.Header.Set("Upgrade", "websocket") req.Header.Set("Sec-WebSocket-Key", base64.StdEncoding.EncodeToString(key)) req.Header.Set("Sec-WebSocket-Version", "13") req.Header.Set("token", os.Getenv("Token_Instance")) resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } log.Println("Status:", resp.Status) } ``` ### wscat (interativo) Abre uma sessão WebSocket interativa real com `wscat` (ou cliente equivalente em cada linguagem) e fica imprimindo cada frame JSON recebido. Forma mais prática para debugar eventos em tempo real durante o desenvolvimento. ```bash cURL # npm i -g wscat wscat -c "wss://api.example.com/ws/$Instance_Name?token=$Token_Instance" ``` ```javascript JavaScript // Cliente WebSocket nativo (Node.js 22+ ou browser) const ws = new WebSocket(`wss://api.example.com/ws/${process.env.Instance_Name}?token=${process.env.Token_Instance}`); ws.addEventListener("message", (ev) => console.log(ev.data)); ``` ```python Python import asyncio, os, websockets async def main(): url = f"wss://api.example.com/ws/{os.environ['Instance_Name']}?token={os.environ['Token_Instance']}" async with websockets.connect(url) as ws: async for raw in ws: print(raw) asyncio.run(main()) ``` ```go Go package main import ( "log" "os" "github.com/gorilla/websocket" ) func main() { url := "wss://api.example.com/ws/" + os.Getenv("Instance_Name") + "?token=" + os.Getenv("Token_Instance") c, _, err := websocket.DefaultDialer.Dial(url, nil) if err != nil { log.Fatal(err) } defer c.Close() for { _, msg, err := c.ReadMessage() if err != nil { log.Fatal(err) } log.Printf("%s", msg) } } ``` ## Exemplos de cliente Browsers não permitem customizar headers no `WebSocket`, use `?token=`: ```js const BASE = "wss://api.example.com"; const INSTANCE = "$Instance_Name"; const TOKEN = "a1b2c3d4-..."; // Account ou Instance let ws; let reconnectDelay = 1000; // 1s, dobra até 30s function connect() { ws = new WebSocket(`${BASE}/ws/${INSTANCE}?token=${encodeURIComponent(TOKEN)}`); ws.addEventListener("open", () => { console.log("WS conectado"); reconnectDelay = 1000; }); ws.addEventListener("message", (ev) => { try { const env = JSON.parse(ev.data); switch (env.event) { case "message.exchange": handleMessage(env.data); break; case "instance.state": handleState(env.data); break; // ... demais tipos } } catch (e) { console.error("Frame não-JSON:", ev.data); } }); ws.addEventListener("close", (ev) => { console.warn(`Fechado (code=${ev.code}). Reconectando em ${reconnectDelay}ms...`); setTimeout(connect, reconnectDelay); reconnectDelay = Math.min(reconnectDelay * 2, 30000); }); ws.addEventListener("error", (ev) => { console.error("WS error:", ev); // 'close' dispara em seguida }); } connect(); ``` Server-side: prefira o header `token`: ```js import WebSocket from "ws"; const ws = new WebSocket("wss://api.example.com/ws/$Instance_Name", { headers: { token: process.env.Token_Instance } }); ws.on("open", () => console.log("conectado")); ws.on("message", (buf) => { const env = JSON.parse(buf.toString()); console.log(env.event, env.data); }); ws.on("close", (code, reason) => console.warn("fechou", code, reason?.toString())); ws.on("error", (e) => console.error(e)); ``` ```python import asyncio, json, os, websockets async def consume(): url = "wss://api.example.com/ws/$Instance_Name" headers = {"token": os.environ["Token_Instance"]} async with websockets.connect(url, extra_headers=headers) as ws: async for raw in ws: env = json.loads(raw) print(env["event"], env["data"]) asyncio.run(consume()) ``` ```go package main import ( "log" "net/http" "github.com/gorilla/websocket" ) func main() { h := http.Header{} h.Set("token", "a1b2c3d4-...") c, _, err := websocket.DefaultDialer.Dial( "wss://api.example.com/ws/$Instance_Name", h) if err != nil { log.Fatal(err) } defer c.Close() for { _, msg, err := c.ReadMessage() if err != nil { log.Fatal(err) } log.Printf("%s", msg) } } ``` ## Envelope dos frames recebidos Cada frame de texto é um JSON idêntico ao webhook: ```json { "event": "message.exchange", "data": { /* payload específico */ }, "instanceData": { "baseUrl": "https://api.example.com", "instance": "$Instance_Name", "token": "" } } ``` `instanceData.token` é o **token da própria instância**, útil quando o cliente consome múltiplas instâncias e precisa identificar a origem ou fazer chamadas REST de volta. Quando `ENCRYPTION_KEY` está configurada, o token vem **descriptografado** no payload. Filtre/redija em logs do cliente se você loggar o frame inteiro. ## Parâmetros de rota Nome da instância. Deve existir e ter WebSocket habilitado. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|:-----------:|---------|-----------| | `token` ou `Authorization` | sim, salvo se usar `?token=` | `token: a1b2c3...` | Auth flexível. | | `Upgrade` | sim | `websocket` | Exigido pelo protocolo. | | `Connection` | sim | `Upgrade` | Idem. | | `Sec-WebSocket-Key` | sim | (gerado pelo cliente) | Idem. | | `Sec-WebSocket-Version` | sim | `13` | Único valor aceito. | | `Origin` | condicional | `https://app.exemplo.com` | Validado contra allowlist em browsers. Clients sem `Origin` são aceitos. | ## Query parameters Token de autenticação. **Obrigatório** quando o cliente não pode enviar `token` ou `Authorization` no header (caso dos browsers). ## Pré-condições 1. Existe uma config em `websocket_configs` para a instância (criada via [`POST /api/events/websocket/:instance`](/pt/api/events/websocket-configure)) com `enabled=true`. 2. Token válido, `TokenAccount` ou `TokenInstance` da instância (mesma matriz do REST). 3. Se vier de browser, o `Origin` do request está em `ALLOWED_WS_ORIGINS` ou é same-origin. A validação acontece **antes do upgrade HTTP→WS**. Após o upgrade não há re-autenticação, a sessão TCP é confiável até ser fechada. ## Autenticação `ValidateTokenFlexible()` aceita o token em **três fontes**: | Fonte | Exemplo | |-------|---------| | Header `token` | `token: a1b2c3d4-...` | | Header `Authorization: Bearer` | `Authorization: Bearer a1b2c3d4-...` | | Query param `?token=` | `wss://api.example.com/ws/minha?token=a1b2c3d4-...` | O query param é **praticamente obrigatório para clients de browser**, já que a API `new WebSocket(url)` do navegador não permite customizar headers. Clients server-side (Node, Go, Python, etc.) devem **preferir o header `token`**, query param vaza em logs de proxy/CDN. ## Validação de Origin (`ALLOWED_WS_ORIGINS`) Independente do CORS (que afeta só REST), o WebSocket tem sua própria allowlist controlada pela env var `ALLOWED_WS_ORIGINS`. | `ALLOWED_WS_ORIGINS` | Comportamento | |----------------------|---------------| | Vazio / não definido | Apenas **same-origin** (Origin igual ao Host) é aceito. | | `"https://app.exemplo.com,https://dashboard.exemplo.com"` | Allowlist explícita, separada por vírgula. | **Clientes sem header `Origin`** (curl, Postman, libs Node/Python/Go) **são sempre aceitos**, `Origin` é mecanismo do browser, não universal. A segurança para esses clientes é o token. Bloqueios são logados como `WebSocket upgrade blocked from origin (host )`. O cliente recebe `403 Forbidden` (sem body) e a TCP é fechada. ## Heartbeat | Lado | Mensagem | Intervalo | |------|----------|-----------| | Servidor → cliente | PING | a cada ~54s (`pingPeriod`) | | Cliente → servidor | PONG | dentro de 60s (`pongWait`) | Sem PONG em 60s → o servidor dropa a conexão. **Não há resumo de sessão**: o cliente deve reconectar com backoff e os eventos perdidos no intervalo **não voltam**. A maioria das libs WebSocket (`gorilla/websocket`, `ws` do Node, `websockets` do Python, browser nativo) responde PONG automaticamente, o cliente quase nunca precisa implementar isso manualmente. ## Buffers e backpressure | Limite | Valor | Efeito ao estourar | |--------|-------|---------------------| | Read buffer (max client message) | 4096 bytes | Servidor fecha a conexão. | | Send buffer por cliente | 256 mensagens | Cliente lento é dropado pelo hub. | O servidor **não consome** mensagens enviadas pelo cliente (apenas PONG e fechamento). Enviar payloads JSON do cliente para o servidor não tem efeito. ## Catálogo de eventos Os 6 tipos possíveis (`message.exchange`, `message.status`, `call.update`, `group.flow`, `instance.state`, `label.update`) compartilham este envelope. Schemas e exemplos completos em [/api/events/catalog](/pt/api/events/catalog). ## Reconexão e resiliência O servidor **não replica** eventos perdidos durante quedas, o cliente WS é fire-and-forget. Para garantia de entrega, use webhook em paralelo. **Sempre tenha handler de `close` com reconexão automática**, idealmente com backoff exponencial e jitter, limitado a 30s entre tentativas. **Trate close codes**: `1006` (queda de rede), `1011` (erro do servidor), `1008` (policy violation), `4xxx` (custom, raros). **Catch-up via REST** depois de reconectar, use [`GET /api/chat/history/:instance`](/pt/api/chat/history) para puxar mensagens recentes que possam ter sido perdidas. **Buffer local no cliente**, nunca bloqueie o handler `message` com operações lentas; jogue em fila e processe em outra thread/worker. ## Efeitos colaterais - **Hub em memória**: o handler registra o cliente em `WebSocketHub` (`map[instanceName]map[*WebSocketClient]bool`). A conexão **não é persistida**. Reinício do processo derruba todos. - **Goroutines**: cada conexão lança 2 goroutines (`WritePump` e `ReadPump`) que vivem até o close. - **Sem DB write**: o upgrade em si não escreve nada. Broadcasts subsequentes passam pelo dispatcher de webhook (que toca DB) em paralelo, WS é apenas fanout adicional. - **Métricas Prometheus**: contadores de conexões ativas por instância (ver [/api/observability/overview](/pt/api/observability/overview)). ## Notas - **Sem retry/persistência**: cliente offline 5min = perde 5min de eventos. Para garantia, use webhook. - **Multi-cliente**: vários clientes podem conectar na mesma instância. Todos recebem todos os eventos (broadcast). Não há atomicidade de "quem processou primeiro". - **Filtros são globais por instância**: o filtro `events[]` configurado em `POST /api/events/websocket/:instance` vale para todos os clientes, não é configurável por conexão. - **Frame size do servidor**: o limite de 4096 bytes vale apenas para mensagens **enviadas pelo cliente**. O servidor envia frames potencialmente bem maiores (mídia em base64 passa de 100KB facilmente). Leia frames sem limite no cliente. ## Quando usar webhook vs WebSocket | Critério | WebSocket | Webhook | |----------|-----------|---------| | Latência | ms (push direto) | 100-500ms (HTTP + fila) | | Persistência | Eventos perdidos se offline | Fila + retry 5x + DLQ | | Multi-consumer | Vários clientes em broadcast | Até 3 webhooks habilitados (labels) | | Auth | Token no upgrade | Header `Authorization` opcional | | Setup | Cliente WS + config habilitada | Endpoint HTTP público + URL | | Dev-friendliness | Excelente (wscat, DevTools) | Requer tunneling em dev | - **Webhook**: integrações server-to-server onde perda é inaceitável (CRM, ERP, analytics, log sink). - **WebSocket**: UIs em tempo real (dashboard, inbox ao vivo, tela de atendimento) onde latência baixa é prioridade e perdas ocasionais são aceitáveis. - **Os dois em paralelo**: webhook persiste estado, WS faz a UI saltar. ## Erros antes do upgrade Todas ocorrem antes do `101 Switching Protocols`, o cliente recebe um HTTP normal. | HTTP | `error.message` | Quando | |------|-----------------|--------| | 400 | `WebSocket is not enabled for this instance. Configure it first using POST /api/events/websocket/` | Sem config ou `enabled=false`. | | 401 | `Missing token in header, Authorization header, or query parameter` | Nenhuma das 3 fontes forneceu token. | | 401 | `Invalid token` | Token inválido. | | 403 |, (body vazio, do upgrader) | `Origin` fora da allowlist. | | 404 | `Instance not found` | `:instance` inexistente. | | 429 | `Rate limit exceeded. Try again later.` | Rate-limit global. | | 500 | `Failed to get instance` / `Failed to get websocket configuration` | Erro de banco. | ```json 400 Bad Request { "success": false, "error": { "message": "WebSocket is not enabled for this instance. Configure it first using POST /api/events/websocket/$Instance_Name" } } ``` ## Erros após o upgrade Depois do `101`, qualquer falha de protocolo fecha a conexão com um **close code** padrão (`1001` going away, `1006` abnormal closure, `1011` server error). O servidor não escreve body, o cliente trata pelo close code. Causas comuns: - Frame do cliente maior que 4096 bytes. - `pongWait` (60s) expirou sem resposta ao PING. - Buffer de envio do cliente cheio (cliente lento), hub faz unregister. - Instância foi deletada enquanto o cliente estava conectado. ## Referências `POST /api/events/websocket/:instance` Schemas dos 6 tipos. Webhook x WebSocket, envelope, boas práticas. Matriz de tokens, header `token`, `?token=`. ### Mensagens **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não O módulo `/api/message/*` cobre o envio de **todos os formatos suportados pelo WhatsApp**, texto, mídias, sticker, localização, contato, reação, enquete, carrossel, botões, lista, formulário, PIX e status (stories). Todas as rotas validam ownership da instância e aceitam `TokenAccount` ou `TokenInstance`. Para **buscar uma mensagem por ID** use [`GET /api/chat/getMessage/:instance`](/pt/api/chat/find-message), esse endpoint deixou de pertencer ao módulo de mensagens. ## Endpoints disponíveis | Método | Path | Tipo | |--------|------|------| | POST | `/api/message/text/:instance` | [Texto](/pt/api/messages/text) | | POST | `/api/message/media/:instance` | [Imagem / vídeo / áudio / documento](/pt/api/messages/media) | | POST | `/api/message/sticker/:instance` | [Sticker (WebP)](/pt/api/messages/sticker) | | POST | `/api/message/location/:instance` | [Localização](/pt/api/messages/location) | | POST | `/api/message/contact/:instance` | [Contato (vCard)](/pt/api/messages/contact) | | POST | `/api/message/reaction/:instance` | [Reação (emoji)](/pt/api/messages/reaction) | | POST | `/api/message/poll/:instance` | [Enquete](/pt/api/messages/poll) | | POST | `/api/message/event/:instance` | [Evento](/pt/api/messages/event) | | POST | `/api/message/carousel/:instance` | [Carrossel de cards](/pt/api/messages/carousel) | | POST | `/api/message/button/:instance` | [Botões interativos](/pt/api/messages/buttons) | | POST | `/api/message/list/:instance` | [Lista (sections)](/pt/api/messages/list) | | POST | `/api/message/form/:instance` | [Formulário (Native Flow)](/pt/api/messages/form) | | POST | `/api/message/pix/:instance` | [PIX (pagamento BR)](/pt/api/messages/pix) | | POST | `/api/message/status/:instance` | [Status (stories)](/pt/api/messages/status) | | POST | `/api/call/fake/:instance` | [Chamada Fake](/pt/api/calls/fake) | | POST | `/api/call/audio/:instance` | [Chamada com Áudio](/pt/api/calls/audio) | ## Estrutura comum ### Destinatário (`number` ou `to`) A maioria dos endpoints aceita o destinatário no campo `number`. Os formatos suportados são: - Número simples: `"5511999999999"` (preferido). - JID privado: `"5511999999999@s.whatsapp.net"`. - JID oculto (`@lid`): `"123456789012345@lid"`, identificador anônimo usado pelo WhatsApp em grupos/canais quando o número real não está exposto. - JID grupo: `"120363406289005073@g.us"`. - JID newsletter: `"120363422585881117@newsletter"`. - Status broadcast: `"status@broadcast"`. ### Comportamento brasileiro do número Para números começando com `55` (Brasil), o serviço tenta automaticamente variações: - Com 9 (`5511999999999`) - Sem 9 (`551199999999`) Isso resolve um histórico de inconsistências em DDDs antigos. Se o número não for encontrado em nenhuma das variações, o handler retorna `400 Number is not registered on WhatsApp`. ### Campos opcionais comuns | Campo | Tipo | Aplica-se a | Descrição | |-------|------|-------------|-----------| | `delay` | int (segundos) | quase todos | Tempo de espera antes do envio. Durante o intervalo o servidor envia "digitando..." e depois "paused". | | `replyTo` | string | quase todos | ID da mensagem original a citar. A mensagem precisa pertencer à mesma instância e estar no banco. | | `replyPrivate` | bool | quase todos | Quando a mensagem citada é de grupo, redireciona a resposta para o privado do autor (mantém a citação). | | `mention` | string[] | text / media | Números (ou JIDs) a mencionar. **Apenas em grupos.** Limite de 10 por mensagem. | | `mentionAll` | bool | text / media | Menciona todos os membros do grupo (`@todos`). **Apenas em grupos.** | | `linkPreview` | bool | text | Quando `true`, busca metadados Open Graph da 1ª URL e envia como `ExtendedTextMessage` com card. Default `false`. | | `source` | string | todos | Identificador de origem para rastreabilidade (ex.: `crm`, `n8n`). Default: `"api"`. | `delay` é em **segundos** (não milissegundos). Valor `3` = 3 s de "digitando". Reactions (`/api/message/reaction/:instance`) não suportam `delay`/`replyTo`/`mention`, o payload é mínimo (`messageId`, `reaction`, `participant`). Status (`/api/message/status/:instance`) também não usa `number`/`replyTo` (sempre vai para `status@broadcast`). ### Resposta padrão (200) Todos os endpoints de envio retornam o mesmo envelope `MessageSentDetails`: ```json { "success": true, "message": "Message sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1B0F5", "direction": "outgoing", "messageType": "text", "content": "Ola!", "source": "api", "timestamp": "2026-04-30T14:30:00Z", "chat": { "jid": "5511999999999@s.whatsapp.net", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" } } } ``` Campos opcionais no `data`: `mentions` (quando há menção), `replyTo` (quando há citação), `chat.groupName` (quando é grupo), `mediaUrl`/`mediaMimeType`/`mediaSize`/`fileName` (quando é mídia), `vcard` (quando é contato). `status` ∈ `sent | disconnected | invalid_number | mentions_not_supported | reply_message_not_found | reply_message_instance_mismatch | private_reply_failed | send_failed | media_download_failed | media_upload_failed | media_validation_failed | unsupported_media_type | image_conversion_failed | sticker_upload_failed | audio_conversion_failed | invalid_message_id | missing_participant | invalid_request`. ### Erros comuns | Status | Mensagem | |--------|----------| | 400 | `Instance name is required` | | 400 | `Invalid request body: ` | | 400 | `Number is required` | | 400 | `Message is required` (e variantes por endpoint: `MediaURL is required`, `Question is required`, etc.) | | 400 | `Mentions are only supported in group chats` | | 400 | `Original message not found (ID: ...)` | | 400 | `Original message does not belong to this instance` | | 404 | `Instance not found` | | 503 | `Instance is not connected to WhatsApp` | | 500 | `Failed to send message: ` | Envelope de erro: ```json { "success": false, "error": { "message": "Number is not registered on WhatsApp" } } ``` ## Limites observados | Recurso | Limite | |---------|--------| | Texto | ~65k caracteres (mensagens > 4096 podem ser cortadas em alguns clients) | | Imagem / vídeo / áudio | até 16 MB | | Documento | até 100 MB | | Botões | máx 3 por mensagem | | Lista | máx 10 sections × 10 rows | | Carrossel | máx 10 cards | | Enquete | 2 a 12 opções | ## Próximos passos Endpoint mais usado, ideal como "Hello World". Imagem, vídeo, áudio e documento por URL ou base64. Recupera uma mensagem específica do histórico. Recebe eventos `message.exchange` em tempo real. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Envia uma mensagem de texto a um contato 1-a-1, grupo (`@g.us`) ou canal (`@newsletter`). Suporta `replyTo` (citação por ID), `replyPrivate` (responder no privado a partir de uma mensagem de grupo), `mention` / `mentionAll` (apenas em grupos), `linkPreview` e `delay` (em segundos) para simular digitação real. É o endpoint mais usado e serve como "Hello World" para validar que a instância está conectada. ## Exemplos ### Mínimo Envia uma mensagem de texto com o payload mínimo (`number` + `message`). Sem `delay`, sem preview, sem citação, útil como "Hello World" para validar que a instância está conectada. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/text/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "message": "Olá! Este é um teste do RyzeAPI." }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/text/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", message: "Olá! Este é um teste do RyzeAPI." }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/text/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "message": "Olá! Este é um teste do RyzeAPI." } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "message": "Olá! Este é um teste do RyzeAPI." }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/text/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com delay e link preview Envia indicador de "digitando..." por 3 segundos e depois envia a mensagem com prévia da URL detectada. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/text/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "message": "Confere aí: https://ryzeapi.cloud", "delay": 3, "linkPreview": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/text/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", message: "Confere aí: https://ryzeapi.cloud", delay: 3, linkPreview: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/text/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "message": "Confere aí: https://ryzeapi.cloud", "delay": 3, "linkPreview": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "message": "Confere aí: https://ryzeapi.cloud", "delay": 3, "linkPreview": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/text/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Como resposta Cita uma mensagem existente (`replyTo` recebe o `messageId` da mensagem original). A mensagem original precisa pertencer à mesma instância e ter sido salva no banco (qualquer mensagem trafegada via webhook/envio é salva automaticamente). ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/text/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "message": "Sim, confirmado!", "replyTo": "3EB08FCF27E532F1B0F5" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/text/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", message: "Sim, confirmado!", replyTo: "3EB08FCF27E532F1B0F5" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/text/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "message": "Sim, confirmado!", "replyTo": "3EB08FCF27E532F1B0F5" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "message": "Sim, confirmado!", "replyTo": "3EB08FCF27E532F1B0F5" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/text/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Como resposta no privado (grupo) Quando alguém manda uma mensagem em um **grupo** e você quer responder **no privado** dessa pessoa (sem retornar a resposta para o grupo), use `replyPrivate: true`. O `number` deve apontar para o JID do grupo onde a mensagem original foi enviada, o servidor extrai o autor original e redireciona a resposta para o privado dele, mantendo a citação. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/text/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "120363406289005073@g.us", "message": "Te respondendo no privado para não poluir o grupo.", "replyTo": "3EB08FCF27E532F1B0F5", "replyPrivate": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/text/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "120363406289005073@g.us", message: "Te respondendo no privado para não poluir o grupo.", replyTo: "3EB08FCF27E532F1B0F5", replyPrivate: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/text/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "120363406289005073@g.us", "message": "Te respondendo no privado para não poluir o grupo.", "replyTo": "3EB08FCF27E532F1B0F5", "replyPrivate": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "120363406289005073@g.us", "message": "Te respondendo no privado para não poluir o grupo.", "replyTo": "3EB08FCF27E532F1B0F5", "replyPrivate": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/text/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com menção a membro(s) (grupo) Adiciona menção visível com `@5511...` no corpo da mensagem e lista os números no array `mention`. O texto e o array precisam estar **consistentes**, o WhatsApp só transforma em link clicável o que estiver no `mention`. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/text/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "120363406289005073@g.us", "message": "Atenção @5511888888888 e @5511777777777, reunião às 14h.", "mention": ["5511888888888", "5511777777777"] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/text/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "120363406289005073@g.us", message: "Atenção @5511888888888 e @5511777777777, reunião às 14h.", mention: ["5511888888888", "5511777777777"] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/text/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "120363406289005073@g.us", "message": "Atenção @5511888888888 e @5511777777777, reunião às 14h.", "mention": ["5511888888888", "5511777777777"] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "120363406289005073@g.us", "message": "Atenção @5511888888888 e @5511777777777, reunião às 14h.", "mention": ["5511888888888", "5511777777777"] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/text/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Menção oculta (grupo) Faz **ping em todos os membros do grupo** sem precisar listar @s no texto, usando `mentionAll: true`. O texto exibido fica limpo, mas os participantes recebem a notificação de menção. Útil para anúncios silenciosos. Para mencionar pessoas específicas de forma oculta, basta usar `mention: ["..."]` **sem** colocar `@número` no texto, eles também recebem a notificação sem aparecer marcados. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/text/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "120363406289005073@g.us", "message": "Aviso geral: reunião confirmada para amanhã às 14h.", "mentionAll": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/text/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "120363406289005073@g.us", message: "Aviso geral: reunião confirmada para amanhã às 14h.", mentionAll: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/text/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "120363406289005073@g.us", "message": "Aviso geral: reunião confirmada para amanhã às 14h.", "mentionAll": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "120363406289005073@g.us", "message": "Aviso geral: reunião confirmada para amanhã às 14h.", "mentionAll": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/text/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta inclui o `messageId` (use-o em `replyTo` para citar essa mensagem em envios futuros), o `direction: "outgoing"` e a marcação de chat/sender. Os campos `mentions` e `replyTo` só aparecem quando aplicáveis, e `chat.groupName` é incluído em mensagens de grupo. ```json 200 OK { "success": true, "message": "Message sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1B0F5", "direction": "outgoing", "messageType": "text", "content": "Olá! Este é um teste do RyzeAPI.", "source": "api", "timestamp": "2026-04-30T14:30:00Z", "chat": { "jid": "5511999999999@s.whatsapp.net", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" }, "mentions": { "mentionedUsers": ["5511888888888"], "mentionAll": false, "totalMentions": 1 }, "replyTo": { "messageId": "3EB08FCF27E532F1A1A1", "content": "Tudo certo para amanhã?" } } } ``` ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Destino: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`, `@lid`, `@g.us`, `@newsletter`). Corpo do texto. Não pode estar vazio. Tempo em **segundos** para aguardar antes de enviar. Durante o intervalo, o servidor envia o indicador de "digitando..." ao destinatário e dispara o "paused" antes do envio real. Quando `true`, o servidor extrai a primeira URL do `message`, busca os metadados Open Graph (título, descrição, thumbnail) e envia a mensagem como `ExtendedTextMessage` com o card de preview embutido. Quando `false` ou omitido, a URL aparece como texto simples. ID da mensagem a ser citada (reply). A mensagem original precisa pertencer à mesma instância e ter sido salva no banco. Erros possíveis: `reply_message_not_found`, `reply_message_instance_mismatch`. Quando `true` **e** `replyTo` aponta para uma mensagem originária de um grupo, a resposta é redirecionada para o **privado** do autor original (mantendo a citação). Útil para responder dúvidas individualmente sem poluir o grupo. Ignorado se a mensagem original não for de grupo. Lista de números (ou JIDs) a mencionar. **Apenas em grupos** (`@g.us`). Limite de **10** menções por mensagem, números excedentes são ignorados com warning. Para que apareçam como link clicável, inclua `@5511...` no `message`. Sem isso, viram menções ocultas (apenas notificam). Quando `true`, menciona **todos os membros do grupo** (exceto a própria instância). Equivale ao `@todos`/`@everyone`. O servidor consulta a lista de participantes e injeta cada JID no `MentionedJID` do contexto, sem alterar o texto exibido. **Apenas em grupos.** Identificador de origem para rastreabilidade (ex.: `crm`, `bot-suporte`, `n8n`). Salvo no registro da mensagem no banco e propagado para webhooks. Quando omitido, assume `"api"`. ## Notas - **`delay` é em segundos** (diferente de muitas APIs que usam ms). Valor `3` = 3 segundos de "digitando". - Em grupos, o texto com `@5511...` por si só **não vira clickable**, é o array `mention` que o WhatsApp processa. Mantenha os dois consistentes. - `mention` **e** `mentionAll` são exclusivos a grupos. Se enviados para DM/canal, retorna `400 Mentions are only supported in group chats`. - Para números BR (começando com `55`), o serviço tenta automaticamente variações com e sem o 9º dígito, evitando o histórico de inconsistências em DDDs antigos. - `linkPreview` requer que o servidor consiga buscar os metadados Open Graph da URL, sites com bloqueio de bots podem cair no fallback de texto simples sem erro visível. - Mensagens enviadas via `replyPrivate: true` aparecem no privado do destinatário com o card da mensagem citada do grupo, ele consegue ver de qual conversa veio. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid request body: ` | | 400 |, | `Number is required` | | 400 |, | `Message is required` | | 400 | `invalid_number` | `Invalid phone number format: ` | | 400 | `mentions_not_supported` | `Mentions are only supported in group chats` | | 400 | `reply_message_not_found` | `Original message not found (ID: ...)` | | 400 | `reply_message_instance_mismatch` | `Original message does not belong to this instance` | | 400 | `private_reply_failed` | (motivo do erro de redirecionamento privado) | | 404 |, | `Instance not found` | | 500 | `send_failed` | `Failed to send message: ` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "Message is required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Envia uma mídia (`image`, `video`, `document` ou `audio`) a partir de uma **URL pública** ou de uma **string base64**. A origem pode ser informada em `mediaUrl` (que aceita tanto a URL quanto o base64 da mídia) **ou** no campo `mediaBase64`, envie um ou outro, nunca os dois. Suporta `message` como legenda (caption), `replyTo` (citação por ID), `replyPrivate`, `mention` / `mentionAll` (apenas em grupos), `delay` (em segundos) para simular digitação real e, para áudio, `isVoice` (PTT), `duration` e `waveform`. O servidor faz o download (quando URL) ou decodifica o base64, detecta o `mimeType` quando omitido e faz o upload nos servidores do WhatsApp antes do envio. ## Exemplos ### Imagem por URL Envia uma imagem (`mediaType: "image"`) baixada de uma URL pública, com `message` usado como legenda (caption) que aparece abaixo da foto no chat. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/media/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "mediaType": "image", "mediaUrl": "https://exemplo.com/foto.jpg", "message": "Confere essa foto!" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/media/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", mediaType: "image", mediaUrl: "https://exemplo.com/foto.jpg", message: "Confere essa foto!" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/media/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "mediaType": "image", "mediaUrl": "https://exemplo.com/foto.jpg", "message": "Confere essa foto!" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "mediaType": "image", "mediaUrl": "https://exemplo.com/foto.jpg", "message": "Confere essa foto!" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/media/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Imagem por base64 Em vez de uma URL, envie o conteúdo da mídia em base64. Você pode colocar o base64 no próprio `mediaUrl` (com ou sem prefixo data URI `data:image/jpeg;base64,`) **ou** no campo dedicado `mediaBase64`. Como não há URL para inferir o nome/tipo, recomenda-se informar `mimeType` (e `fileName`, para documentos). ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/media/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "mediaType": "image", "mediaBase64": "/9j/4AAQSkZJRgABAQAAAQABAAD...", "mimeType": "image/jpeg", "message": "Confere essa foto!" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/media/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", mediaType: "image", mediaBase64: "/9j/4AAQSkZJRgABAQAAAQABAAD...", mimeType: "image/jpeg", message: "Confere essa foto!" }) }); ``` ```python Python import os, base64, requests with open("foto.jpg", "rb") as f: media_b64 = base64.b64encode(f.read()).decode() requests.post( f"https://ryzeapi.cloud/api/message/media/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "mediaType": "image", "mediaBase64": media_b64, "mimeType": "image/jpeg", "message": "Confere essa foto!" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "mediaType": "image", "mediaBase64": "/9j/4AAQSkZJRgABAQAAAQABAAD...", "mimeType": "image/jpeg", "message": "Confere essa foto!" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/media/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Áudio de voz (PTT) Quando `mediaType: "audio"` e `isVoice` é omitido, o servidor assume `true` por padrão (mensagem de voz/PTT). Para enviar como áudio "regular" (faixa de música, por exemplo), passe `isVoice: false`. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/media/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "mediaType": "audio", "mediaUrl": "https://exemplo.com/audio.ogg", "isVoice": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/media/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", mediaType: "audio", mediaUrl: "https://exemplo.com/audio.ogg", isVoice: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/media/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "mediaType": "audio", "mediaUrl": "https://exemplo.com/audio.ogg", "isVoice": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "mediaType": "audio", "mediaUrl": "https://exemplo.com/audio.ogg", "isVoice": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/media/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Documento (PDF) com `fileName` Envia um PDF como documento. O `fileName` (`Contrato-2026.pdf`) define o nome exibido no card do anexo e o `message` aparece como texto acompanhante. Sem `fileName`, o WhatsApp mostra um nome genérico. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/media/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "mediaType": "document", "mediaUrl": "https://exemplo.com/contrato.pdf", "fileName": "Contrato-2026.pdf", "message": "Segue o contrato anexo." }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/media/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", mediaType: "document", mediaUrl: "https://exemplo.com/contrato.pdf", fileName: "Contrato-2026.pdf", message: "Segue o contrato anexo." }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/media/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "mediaType": "document", "mediaUrl": "https://exemplo.com/contrato.pdf", "fileName": "Contrato-2026.pdf", "message": "Segue o contrato anexo." } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "mediaType": "document", "mediaUrl": "https://exemplo.com/contrato.pdf", "fileName": "Contrato-2026.pdf", "message": "Segue o contrato anexo." }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/media/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Vídeo com legenda e delay Envia um vídeo MP4 com a legenda "Olha esse vídeo!" e `delay: 3`, o servidor envia o indicador de "digitando..." por 3 segundos antes de disparar o vídeo, simulando uma digitação real. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/media/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "mediaType": "video", "mediaUrl": "https://exemplo.com/video.mp4", "message": "Olha esse vídeo!", "delay": 3 }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/media/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", mediaType: "video", mediaUrl: "https://exemplo.com/video.mp4", message: "Olha esse vídeo!", delay: 3 }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/media/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "mediaType": "video", "mediaUrl": "https://exemplo.com/video.mp4", "message": "Olha esse vídeo!", "delay": 3 } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "mediaType": "video", "mediaUrl": "https://exemplo.com/video.mp4", "message": "Olha esse vídeo!", "delay": 3 }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/media/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Imagem em grupo com reply e menção Envia uma imagem em um grupo (`@g.us`) citando uma mensagem anterior via `replyTo` e mencionando um membro pelo array `mention`. O `@5511888888888` na caption fica clicável, gerando notificação para o usuário marcado. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/media/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "120363406289005073@g.us", "mediaType": "image", "mediaUrl": "https://exemplo.com/banner.jpg", "message": "Olha só @5511888888888, ficou pronto!", "replyTo": "3EB08FCF27E532F1B0F5", "mention": ["5511888888888"] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/media/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "120363406289005073@g.us", mediaType: "image", mediaUrl: "https://exemplo.com/banner.jpg", message: "Olha só @5511888888888, ficou pronto!", replyTo: "3EB08FCF27E532F1B0F5", mention: ["5511888888888"] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/media/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "120363406289005073@g.us", "mediaType": "image", "mediaUrl": "https://exemplo.com/banner.jpg", "message": "Olha só @5511888888888, ficou pronto!", "replyTo": "3EB08FCF27E532F1B0F5", "mention": ["5511888888888"] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "120363406289005073@g.us", "mediaType": "image", "mediaUrl": "https://exemplo.com/banner.jpg", "message": "Olha só @5511888888888, ficou pronto!", "replyTo": "3EB08FCF27E532F1B0F5", "mention": ["5511888888888"] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/media/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O `messageType` ecoa o `mediaType` enviado (`image`, `video`, `document` ou `audio`). Os metadados resolvidos pelo upload aparecem em `mediaUrl` (URL re-enviada para `mmg.whatsapp.net`), `mediaMimeType` e `mediaSize`. Para áudios PTT, o servidor também devolve `mediaDuration` quando consegue calcular. ```json 200 OK { "success": true, "message": "Media message sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1B0F5", "direction": "outgoing", "messageType": "image", "content": "Confere essa foto!", "source": "api", "timestamp": "2026-04-30T14:30:00Z", "mediaUrl": "https://mmg.whatsapp.net/v/t62.7118-24/...", "mediaMimeType": "image/jpeg", "mediaSize": 184320, "chat": { "jid": "5511999999999@s.whatsapp.net", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" } } } ``` Para `mediaType: "document"`, o `fileName` aparece no card. Para `mediaType: "audio"` com `isVoice: true`, a mensagem é entregue como PTT (forma de onda + ícone de microfone). Para áudios "regulares" (faixa de música), use `isVoice: false`. ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Destino: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`, `@lid`, `@g.us`, `@newsletter`). Um de: `image`, `video`, `document`, `audio`. Determina como o WhatsApp renderiza a mensagem. Origem da mídia: uma **URL pública** do arquivo **ou** o **base64** da mídia (com data URI `data:;base64,...` ou base64 cru). Quando é URL, precisa ser acessível publicamente (sem autenticação) e o servidor faz o download. Obrigatório informar `mediaUrl` **ou** `mediaBase64` (nunca os dois). Base64 da mídia (alternativa ao `mediaUrl`), com data URI `data:;base64,...` ou base64 cru. **Não pode ser usado junto com `mediaUrl`**, envie um ou outro. Como não há URL, recomenda-se informar `mimeType` (e `fileName` para documentos). Legenda (caption) da mídia. Para `mediaType: "document"`, aparece como texto acompanhante. Opcional para todos os tipos. MIME type do arquivo (ex.: `image/jpeg`, `application/pdf`). Quando omitido, o servidor detecta automaticamente a partir do download. Nome do arquivo exibido no card. **Recomendado para `mediaType: "document"`**, sem ele, o WhatsApp mostra um nome genérico. Aplicável apenas a `mediaType: "audio"`. Quando `true`, a mensagem é entregue como **PTT** (mensagem de voz, com forma de onda). Quando `false`, é entregue como áudio regular (faixa de música). Quando o campo é omitido em `audio`, o servidor assume `true`. Duração do áudio em segundos. Aplicável apenas a `mediaType: "audio"`. Opcional, quando omitido, o servidor tenta detectar automaticamente. Forma de onda pré-computada do áudio (PTT). Opcional, quando omitida, o servidor gera automaticamente uma forma de onda padrão. Aplicável apenas a `mediaType: "audio"` com `isVoice: true`. Tempo em **segundos** para aguardar antes de enviar. Durante o intervalo, o servidor envia o indicador de "digitando..." ao destinatário e dispara o "paused" antes do envio real. ID da mensagem a ser citada (reply). A mensagem original precisa pertencer à mesma instância e ter sido salva no banco. Erros possíveis: `reply_message_not_found`, `reply_message_instance_mismatch`. Quando `true` **e** `replyTo` aponta para uma mensagem originária de um grupo, a resposta é redirecionada para o **privado** do autor original (mantendo a citação). Ignorado se a mensagem original não for de grupo. Lista de números (ou JIDs) a mencionar. **Apenas em grupos** (`@g.us`). Para que apareçam como link clicável, inclua `@5511...` na `message` (caption). Sem isso, viram menções ocultas (apenas notificam). Quando `true`, menciona **todos os membros do grupo** (exceto a própria instância). Equivale ao `@todos`/`@everyone`. **Apenas em grupos.** Identificador de origem para rastreabilidade (ex.: `crm`, `bot-suporte`, `n8n`). Salvo no registro da mensagem no banco e propagado para webhooks. Quando omitido, assume `"api"`. ## Notas - **`delay` é em segundos**, não milissegundos. Valor `3` = 3 segundos de "digitando". - Para `mediaType: "audio"`, **`isVoice` assume `true`** automaticamente quando o campo é omitido. Para enviar como faixa de música, é necessário enviar `isVoice: false` explicitamente. - A mídia pode vir por **URL** ou **base64**. Use `mediaUrl` (que aceita URL ou base64) **ou** `mediaBase64`, enviar os dois retorna `400 use either mediaUrl or mediaBase64, not both`; não enviar nenhum retorna `400 mediaUrl or mediaBase64 is required`. - Quando `mediaUrl` é uma URL, precisa ser **publicamente acessível**. URLs com autenticação, sessão ou proteção contra bots costumam falhar com `media_download_failed`. - Para envio em base64, informar `mimeType` é recomendado (não há URL para inferir o tipo). O base64 aceita data URI (`data:;base64,...`) ou base64 cru. - Quando `mimeType` não é informado, o servidor detecta a partir dos primeiros bytes do download (`net/http` + sniff). Em casos raros (extensão atípica), informar manualmente evita problemas. - Para números BR (começando com `55`), o serviço tenta automaticamente variações com e sem o 9º dígito. - `mention` e `mentionAll` são exclusivos a grupos. Se enviados para DM/canal, retorna `400 Mentions are only supported in group chats`. - O campo `duration` (áudio) é informativo, `whatsmeow` ainda calcula seu próprio valor a partir do arquivo. Útil quando o servidor não consegue inferir. - O campo `waveform` é opcional e advisory: se omitido, o servidor gera uma forma de onda padronizada para PTT. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid request body: ` | | 400 |, | `Number is required` | | 400 |, | `MediaType is required` | | 400 |, | `mediaUrl or mediaBase64 is required` | | 400 |, | `use either mediaUrl or mediaBase64, not both` | | 400 |, | `MediaType must be one of: image, video, document, audio` | | 400 | `invalid_number` | `Invalid phone number format: ` | | 400 | `mentions_not_supported` | `Mentions are only supported in group chats` | | 400 | `media_download_failed` | `Failed to download media: ` | | 400 | `media_input_invalid` | `Failed to resolve media: ` | | 400 | `media_validation_failed` | `Invalid media file: ` | | 400 | `unsupported_media_type` | `Unsupported media type: ` | | 500 | `media_upload_failed` | `Failed to upload media to WhatsApp servers` | | 500 | `send_failed` | `Failed to send message: ` | | 404 |, | `Instance not found` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "mediaUrl or mediaBase64 is required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Envia uma figurinha (`sticker`) a partir de uma `imageUrl`. O servidor faz o download da imagem (PNG, JPEG ou GIF) e **converte automaticamente** para o formato esperado pelo WhatsApp: **WebP, 512×512**, com fundo preservado quando aplicável. Suporta `replyTo`, `replyPrivate`, `delay` (em segundos) e `source` para rastreabilidade. ## Exemplos ### Mínimo (URL pública) Envia uma figurinha apontando apenas para uma URL pública de imagem (PNG/JPEG/GIF). O servidor faz o download e converte automaticamente para WebP 512×512 antes de entregar. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/sticker/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "imageUrl": "https://exemplo.com/figurinha.png" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/sticker/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", imageUrl: "https://exemplo.com/figurinha.png" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/sticker/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "imageUrl": "https://exemplo.com/figurinha.png" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "imageUrl": "https://exemplo.com/figurinha.png" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/sticker/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### A partir de base64 (data URL) `imageUrl` aceita `data:` URL com base64 inline. O servidor decodifica e segue o mesmo fluxo de conversão para WebP 512×512. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/sticker/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "imageUrl": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/sticker/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", imageUrl: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/sticker/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "imageUrl": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "imageUrl": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/sticker/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Como resposta a uma mensagem Envia a figurinha citando uma mensagem anterior via `replyTo`. Combinação típica para reações visuais ("reaction stickers") em resposta direta a algo dito antes na conversa. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/sticker/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "imageUrl": "https://exemplo.com/reaction.png", "replyTo": "3EB08FCF27E532F1B0F5" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/sticker/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", imageUrl: "https://exemplo.com/reaction.png", replyTo: "3EB08FCF27E532F1B0F5" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/sticker/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "imageUrl": "https://exemplo.com/reaction.png", "replyTo": "3EB08FCF27E532F1B0F5" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "imageUrl": "https://exemplo.com/reaction.png", "replyTo": "3EB08FCF27E532F1B0F5" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/sticker/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O `messageType` é sempre `sticker` e o `mediaMimeType` fixo em `image/webp` (a imagem original foi convertida pelo servidor). Não há `content` no payload, stickers não carregam legenda. ```json 200 OK { "success": true, "message": "Sticker sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1B0F5", "direction": "sent", "messageType": "sticker", "source": "api", "timestamp": "2026-04-30T14:30:00Z", "chat": { "jid": "5511999999999@s.whatsapp.net", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" } } } ``` ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Destino: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`, `@lid`, `@g.us`, `@newsletter`). URL pública da imagem (PNG, JPEG ou GIF) **ou** `data:` URL com base64 inline. O servidor faz o download/decode e converte automaticamente para WebP 512×512. Tempo em **segundos** para aguardar antes de enviar. Durante o intervalo, o servidor envia o indicador de "digitando..." ao destinatário e dispara o "paused" antes do envio real. ID da mensagem a ser citada (reply). A mensagem original precisa pertencer à mesma instância e ter sido salva no banco. Quando `true` **e** `replyTo` aponta para uma mensagem originária de um grupo, a resposta é redirecionada para o **privado** do autor original (mantendo a citação). Ignorado se a mensagem original não for de grupo. Identificador de origem para rastreabilidade (ex.: `crm`, `bot-suporte`, `n8n`). Salvo no registro da mensagem no banco e propagado para webhooks. Quando omitido, assume `"api"`. ## Notas - **`delay` é em segundos**, não milissegundos. - O servidor **converte automaticamente** PNG/JPEG/GIF para **WebP 512×512** antes do envio. Não é necessário enviar um WebP pré-formatado. - GIFs animados são aceitos, mas a animação pode ser preservada apenas parcialmente dependendo do encoder; para stickers animados confiáveis, envie um WebP animado já no formato correto via `imageUrl`. - Stickers **não suportam menções** nem `mentionAll` (limitação do WhatsApp para mensagens do tipo sticker). - A `imageUrl` precisa ser publicamente acessível, ou ser um `data:` URL base64. - Para números BR (começando com `55`), o serviço tenta automaticamente variações com e sem o 9º dígito. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid request body: ` | | 400 |, | `Number is required` | | 400 |, | `ImageURL is required` | | 400 | `invalid_number` | `Invalid phone number format: ` | | 400 | `image_download_failed` | `Failed to download image: ` | | 500 | `image_conversion_failed` | `Failed to convert image to sticker (WebP 512x512)` | | 500 | `sticker_upload_failed` | `Failed to upload sticker to WhatsApp servers` | | 500 | `send_failed` | `Failed to send message: ` | | 404 |, | `Instance not found` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "ImageURL is required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Envia um ou mais contatos como vCard. O destinatário recebe um card clicável que pode adicionar o contato direto à agenda. O campo `vcard` aceita um **array** de objetos `VCard` (envio múltiplo) **ou** um único objeto (compatibilidade retroativa, o servidor aceita ambos os formatos). Suporta `replyTo`, `replyPrivate`, `delay` (em segundos) e `source`. **Não** suporta menções. ## Exemplos ### Contato único Envia um vCard com o mínimo necessário (`fullName` e `phone`). O destinatário recebe um único card clicável com o contato "João Silva" pronto para ser adicionado à agenda. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/contact/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "vcard": [ { "fullName": "João Silva", "phone": "5511888888888" } ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/contact/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", vcard: [ { fullName: "João Silva", phone: "5511888888888" } ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/contact/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "vcard": [ { "fullName": "João Silva", "phone": "5511888888888" } ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "vcard": [ { "fullName": "João Silva", "phone": "5511888888888" } ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/contact/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Múltiplos contatos Passa três contatos no array `vcard`. O servidor envia tudo como um `ContactsArrayMessage` único, o destinatário visualiza um card agrupado e escolhe quais nomes deseja adicionar à agenda. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/contact/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "vcard": [ { "fullName": "João Silva", "phone": "5511888888888" }, { "fullName": "Maria Souza", "phone": "5511777777777" }, { "fullName": "Carlos Lima", "phone": "5511666666666" } ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/contact/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", vcard: [ { fullName: "João Silva", phone: "5511888888888" }, { fullName: "Maria Souza", phone: "5511777777777" }, { fullName: "Carlos Lima", phone: "5511666666666" } ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/contact/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "vcard": [ {"fullName": "João Silva", "phone": "5511888888888"}, {"fullName": "Maria Souza", "phone": "5511777777777"}, {"fullName": "Carlos Lima", "phone": "5511666666666"} ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "vcard": [ { "fullName": "João Silva", "phone": "5511888888888" }, { "fullName": "Maria Souza", "phone": "5511777777777" }, { "fullName": "Carlos Lima", "phone": "5511666666666" } ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/contact/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Contato com organização, e-mail e site Inclui os campos opcionais `organization`, `email` e `url` no vCard. O card resultante exibe a empresa, o e-mail de contato e o site abaixo do nome, ideal para apresentar contatos comerciais completos. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/contact/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "vcard": [ { "fullName": "João Silva", "phone": "5511888888888", "organization": "Example S.A.", "email": "joao@example.com", "url": "https://example.com" } ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/contact/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", vcard: [ { fullName: "João Silva", phone: "5511888888888", organization: "Example S.A.", email: "joao@example.com", url: "https://example.com" } ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/contact/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "vcard": [ { "fullName": "João Silva", "phone": "5511888888888", "organization": "Example S.A.", "email": "joao@example.com", "url": "https://example.com" } ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "vcard": [ { "fullName": "João Silva", "phone": "5511888888888", "organization": "Example S.A.", "email": "joao@example.com", "url": "https://example.com" } ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/contact/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O array `vcard` da resposta ecoa exatamente o que você enviou (com todos os contatos). Quando há mais de um contato, a `message` vira `"X contacts sent successfully in one message"`. ```json 200 OK { "success": true, "message": "Contact sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1B0F5", "direction": "sent", "messageType": "contact", "vcard": [ { "fullName": "João Silva", "phone": "5511888888888", "organization": "Example S.A.", "email": "joao@example.com", "url": "https://example.com" } ], "source": "api", "timestamp": "2026-04-30T14:30:00Z", "chat": { "jid": "5511999999999@s.whatsapp.net", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" } } } ``` Quando o `vcard` contém apenas um item, o servidor envia como `ContactMessage`. Para múltiplos itens, é enviado como `ContactsArrayMessage` (uma única mensagem agrupando vários cards). ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Destino: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`, `@lid`, `@g.us`, `@newsletter`). Array de contatos a enviar. Cada item segue a estrutura abaixo. O handler também aceita um **único objeto** `VCard` (sem array) por compatibilidade retroativa, convertendo internamente para um array de um item. Pelo menos um contato é obrigatório. Nome completo do contato. Aparece como rótulo principal do card. Número de telefone do contato (com código do país, ex.: `5511888888888`). Organização/empresa do contato (opcional). E-mail do contato (opcional). URL/website do contato (opcional). Tempo em **segundos** para aguardar antes de enviar. Durante o intervalo, o servidor envia o indicador de "digitando..." ao destinatário e dispara o "paused" antes do envio real. ID da mensagem a ser citada (reply). A mensagem original precisa pertencer à mesma instância e ter sido salva no banco. Quando `true` **e** `replyTo` aponta para uma mensagem originária de um grupo, a resposta é redirecionada para o **privado** do autor original (mantendo a citação). Ignorado se a mensagem original não for de grupo. **Advisory:** este campo está definido na struct mas o handler de contatos faz parsing manual do JSON e não está extraindo o `replyPrivate` do payload, então atualmente é tratado como `false`. Identificador de origem para rastreabilidade (ex.: `crm`, `bot-suporte`, `n8n`). Salvo no registro da mensagem no banco e propagado para webhooks. Quando omitido, assume `"api"`. ## Notas - **`delay` é em segundos**, não milissegundos. - O campo `vcard` aceita **tanto array quanto objeto único**. Recomendado usar sempre array (`[ {...} ]`) para evitar ambiguidade. - Cada contato exige **`fullName`** e **`phone`** preenchidos. `organization`, `email` e `url` são opcionais. - Para envios múltiplos, o WhatsApp agrupa os contatos em um único card no chat, o destinatário pode escolher quais adicionar. - Mensagens de contato **não suportam** `mention` nem `mentionAll`. - Para números BR (começando com `55`), o serviço tenta automaticamente variações com e sem o 9º dígito. - Cada contato no array passa por validação individual; se o item `N` falha, a resposta é `400 Contact full name is required for contact N` ou `400 Contact phone number is required for contact N`. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid JSON format: ` | | 400 |, | `Failed to read request body: ` | | 400 |, | `Number is required` | | 400 |, | `At least one contact (vcard) is required` | | 400 |, | `Contact full name is required for contact N` | | 400 |, | `Contact phone number is required for contact N` | | 400 | `invalid_number` | `Invalid phone number format: ` | | 500 | `send_failed` | `Failed to send message: ` | | 404 |, | `Instance not found` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "At least one contact (vcard) is required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Envia uma localização geográfica como mensagem rica (`LocationMessage`), com `latitude`, `longitude`, `name` (rótulo principal) e `address` (linha secundária). O destinatário visualiza um card com prévia do mapa e botões de "Abrir no mapa". Suporta `replyTo`, `replyPrivate`, `delay` (em segundos) e `source`. **Não** suporta menções. ## Exemplos ### Localização simples Envia um card de localização com as coordenadas da Avenida Paulista (`-23.5614, -46.6558`), nome do lugar e endereço completo. O destinatário vê a prévia do mapa e pode abrir no app de navegação. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/location/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "latitude": -23.5614, "longitude": -46.6558, "name": "Avenida Paulista", "address": "Av. Paulista, 1578 - Bela Vista, São Paulo - SP" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/location/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", latitude: -23.5614, longitude: -46.6558, name: "Avenida Paulista", address: "Av. Paulista, 1578 - Bela Vista, São Paulo - SP" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/location/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "latitude": -23.5614, "longitude": -46.6558, "name": "Avenida Paulista", "address": "Av. Paulista, 1578 - Bela Vista, São Paulo - SP" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "latitude": -23.5614, "longitude": -46.6558, "name": "Avenida Paulista", "address": "Av. Paulista, 1578 - Bela Vista, São Paulo - SP" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/location/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Como resposta a uma mensagem Envia o card de localização citando uma mensagem anterior via `replyTo`. Útil para responder a uma pergunta do tipo "onde a gente se encontra?" mantendo a citação da mensagem original. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/location/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "latitude": -23.5614, "longitude": -46.6558, "name": "Ponto de encontro", "address": "Av. Paulista, 1578", "replyTo": "3EB08FCF27E532F1B0F5" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/location/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", latitude: -23.5614, longitude: -46.6558, name: "Ponto de encontro", address: "Av. Paulista, 1578", replyTo: "3EB08FCF27E532F1B0F5" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/location/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "latitude": -23.5614, "longitude": -46.6558, "name": "Ponto de encontro", "address": "Av. Paulista, 1578", "replyTo": "3EB08FCF27E532F1B0F5" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "latitude": -23.5614, "longitude": -46.6558, "name": "Ponto de encontro", "address": "Av. Paulista, 1578", "replyTo": "3EB08FCF27E532F1B0F5" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/location/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O `content` traz uma representação textual da localização (`📍 nome\nendereço\nLat: ..., Long: ...`) salva no histórico, e o `messageType` é fixo em `location`. ```json 200 OK { "success": true, "message": "Location sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1B0F5", "direction": "sent", "messageType": "location", "content": "📍 Avenida Paulista\nAv. Paulista, 1578 - Bela Vista, São Paulo - SP\nLat: -23.561414, Long: -46.655881", "source": "api", "timestamp": "2026-04-30T14:30:00Z", "chat": { "jid": "5511999999999@s.whatsapp.net", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" } } } ``` ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Destino: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`, `@lid`, `@g.us`, `@newsletter`). Latitude geográfica em graus decimais (ex.: `-23.5614`). Precisão recomendada de 4 a 6 casas decimais. Longitude geográfica em graus decimais (ex.: `-46.6558`). Rótulo principal exibido no card de localização (linha em destaque). Costuma ser o nome do lugar/estabelecimento. Endereço/descrição secundária exibida abaixo do `name` no card. Tempo em **segundos** para aguardar antes de enviar. Durante o intervalo, o servidor envia o indicador de "digitando..." ao destinatário e dispara o "paused" antes do envio real. ID da mensagem a ser citada (reply). A mensagem original precisa pertencer à mesma instância e ter sido salva no banco. Quando `true` **e** `replyTo` aponta para uma mensagem originária de um grupo, a resposta é redirecionada para o **privado** do autor original (mantendo a citação). Ignorado se a mensagem original não for de grupo. Identificador de origem para rastreabilidade (ex.: `crm`, `bot-suporte`, `n8n`). Salvo no registro da mensagem no banco e propagado para webhooks. Quando omitido, assume `"api"`. ## Notas - **`delay` é em segundos**, não milissegundos. - A validação atual rejeita o envio quando **ambos** `latitude` **e** `longitude` são exatamente `0`, o ponto `(0, 0)` no Atlântico raramente é uma intenção legítima e geralmente indica payload com campo faltando. - Mensagens de localização **não suportam** `mention` nem `mentionAll`. - Localização "ao vivo" (live location) **não** é suportada por este endpoint, apenas localização estática. - Para números BR (começando com `55`), o serviço tenta automaticamente variações com e sem o 9º dígito. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid request body: ` | | 400 |, | `Number is required` | | 400 |, | `Latitude and longitude are required` | | 400 |, | `Name is required` | | 400 |, | `Address is required` | | 400 | `invalid_number` | `Invalid phone number format: ` | | 500 | `send_failed` | `Failed to send message: ` | | 404 |, | `Instance not found` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "Latitude and longitude are required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Envia uma mensagem com **cartão visual de PIX** no chat do WhatsApp, exibindo o nome do beneficiário, o tipo da chave e a própria chave. O botão funciona como um **"copiar chave"** com design de PIX, ao tocar, o destinatário copia a chave para a área de transferência e cola no app do banco para pagar. Não abre tela de pagamento, não cria cobrança e não há callback de confirmação, é apenas a apresentação visual da chave em formato amigável e clicável. ## Exemplos ### PIX com Chave de CPF Envia o botão PIX usando uma chave do tipo `CPF`. Envie **apenas dígitos** (sem pontos ou traços). ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/pix/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "merchantName": "RyzeAPI Tecnologia", "pixKey": "12345678901", "pixKeyType": "CPF" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/pix/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", merchantName: "RyzeAPI Tecnologia", pixKey: "12345678901", pixKeyType: "CPF" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/pix/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "merchantName": "RyzeAPI Tecnologia", "pixKey": "12345678901", "pixKeyType": "CPF" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "merchantName": "RyzeAPI Tecnologia", "pixKey": "12345678901", "pixKeyType": "CPF" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/pix/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### PIX com Chave de CNPJ Envia o botão PIX usando uma chave do tipo `CNPJ`. Envie **apenas dígitos** (sem pontos, traços ou barras). ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/pix/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "merchantName": "RyzeAPI Tecnologia", "pixKey": "12345678000199", "pixKeyType": "CNPJ" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/pix/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", merchantName: "RyzeAPI Tecnologia", pixKey: "12345678000199", pixKeyType: "CNPJ" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/pix/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "merchantName": "RyzeAPI Tecnologia", "pixKey": "12345678000199", "pixKeyType": "CNPJ" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "merchantName": "RyzeAPI Tecnologia", "pixKey": "12345678000199", "pixKeyType": "CNPJ" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/pix/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### PIX com Chave de Email Envia o botão PIX usando uma chave do tipo `EMAIL`. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/pix/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "merchantName": "RyzeAPI Tecnologia", "pixKey": "contato@ryzeapi.cloud", "pixKeyType": "EMAIL" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/pix/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", merchantName: "RyzeAPI Tecnologia", pixKey: "contato@ryzeapi.cloud", pixKeyType: "EMAIL" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/pix/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "merchantName": "RyzeAPI Tecnologia", "pixKey": "contato@ryzeapi.cloud", "pixKeyType": "EMAIL" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "merchantName": "RyzeAPI Tecnologia", "pixKey": "contato@ryzeapi.cloud", "pixKeyType": "EMAIL" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/pix/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Message sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1B0F5", "direction": "outgoing", "messageType": "pix", "content": "", "source": "api", "timestamp": "2026-04-30T14:30:00Z", "chat": { "jid": "5511999999999@s.whatsapp.net", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" } } } ``` O botão **apenas copia a chave PIX** para a área de transferência do destinatário, não abre a tela de pagamento do banco nem inicia uma cobrança. O pagamento é feito manualmente pelo destinatário no app do próprio banco, e **não há callback de "pago" via WhatsApp**. ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Destino: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`, `@lid`, `@g.us`). Nome do beneficiário exibido no cartão visual do PIX (acima da chave). Chave PIX (CPF, CNPJ, e-mail, telefone ou chave aleatória). Tipo da chave. Valores aceitos: `CPF`, `CNPJ`, `EMAIL`, `PHONE`, `RANDOM`. Tempo em **segundos** para aguardar antes de enviar. Durante o intervalo, o servidor envia o indicador de "digitando..." ao destinatário. ID da mensagem a ser citada (reply). A mensagem original precisa pertencer à mesma instância e ter sido salva no banco. Quando `true` **e** `replyTo` aponta para uma mensagem originária de um grupo, a resposta é redirecionada para o **privado** do autor original. Identificador de origem para rastreabilidade (ex.: `crm`, `checkout`, `n8n`). ## Notas - O **`pixKeyType`** é validado por `oneof`, se enviar um valor fora de `CPF | CNPJ | EMAIL | PHONE | RANDOM`, a request é rejeitada com `400`. - Para chaves CPF/CNPJ envie **apenas dígitos** (sem pontos, traços ou barras): `12345678901` ou `12345678000199`. - Para `PHONE`, use o formato internacional sem `+` (ex.: `5511999999999`). - Para `RANDOM`, use a UUID que o banco gerou (ex.: `aabbccdd-1234-5678-90ab-cdef01234567`). - Esse endpoint não cria QR Code Brcode nem registra cobrança, é apenas a **representação visual** do pedido com chave PIX clicável. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid request: ` | | 400 |, | `Number is required` | | 400 |, | `MerchantName is required` | | 400 |, | `PixKey is required` | | 400 |, | `PixKeyType is required` | | 400 | `invalid_number` | `Invalid phone number format: ` | | 400 | `invalid_request` | (validação `oneof` ou outro motivo) | | 404 |, | `Instance not found` | | 500 | `send_failed` | `Failed to send message: ` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "PixKeyType is required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Envia uma mensagem com **até 3 botões interativos** (limite do WhatsApp). Os botões aceitam quatro tipos: `REPLY` (padrão, retorna o ID quando clicado), `URL` (abre link), `CALL` (disca número) e `COPY` (copia código). O header pode ser texto (`headerText`) **ou** mídia (`mediaUrl` + `mediaType`). Se `mediaUrl` for enviado, `mediaType` é obrigatório e deve ser **maiúsculo**: `IMAGE`, `VIDEO` ou `DOCUMENT`. Quando há mídia, `headerText` é ignorado (a mídia substitui o título). ## Exemplos ### 3 botões REPLY O botão `REPLY` serve para **responder uma mensagem específica dentro da conversa**: quando o cliente toca, o WhatsApp envia uma resposta citando a mensagem original com o **texto** do botão (`displayText`), é isso que aparece no chat. Já no **webhook/websocket**, o que chega para sua aplicação é o `id` do botão clicado, permitindo identificar a opção sem depender do texto exibido. Quando `REPLY` é **misturado** com outros tipos (`URL`, `CALL`, `COPY`) na mesma mensagem, o botão `REPLY` **não aparece no WhatsApp Web/Desktop**, só fica visível no app do smartphone. O ideal é usar **apenas botões `REPLY`** ou **apenas combinações de `URL`/`CALL`/`COPY`**, sem misturar. Caso clássico de menu rápido. Cada botão retorna seu `id` no webhook quando clicado. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/button/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "headerText": "Atendimento RyzeAPI", "contentText": "Como podemos ajudar você hoje?", "footerText": "Disponível 24/7", "buttons": [ { "id": "menu_sales", "displayText": "Falar com vendas", "type": "REPLY" }, { "id": "menu_support", "displayText": "Suporte técnico", "type": "REPLY" }, { "id": "menu_billing", "displayText": "Financeiro", "type": "REPLY" } ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/button/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", headerText: "Atendimento RyzeAPI", contentText: "Como podemos ajudar você hoje?", footerText: "Disponível 24/7", buttons: [ { id: "menu_sales", displayText: "Falar com vendas", type: "REPLY" }, { id: "menu_support", displayText: "Suporte técnico", type: "REPLY" }, { id: "menu_billing", displayText: "Financeiro", type: "REPLY" } ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/button/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "headerText": "Atendimento RyzeAPI", "contentText": "Como podemos ajudar você hoje?", "footerText": "Disponível 24/7", "buttons": [ {"id": "menu_sales", "displayText": "Falar com vendas", "type": "REPLY"}, {"id": "menu_support", "displayText": "Suporte técnico", "type": "REPLY"}, {"id": "menu_billing", "displayText": "Financeiro", "type": "REPLY"} ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "headerText": "Atendimento RyzeAPI", "contentText": "Como podemos ajudar você hoje?", "footerText": "Disponível 24/7", "buttons": [ { "id": "menu_sales", "displayText": "Falar com vendas", "type": "REPLY" }, { "id": "menu_support", "displayText": "Suporte técnico", "type": "REPLY" }, { "id": "menu_billing", "displayText": "Financeiro", "type": "REPLY" } ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/button/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com header de imagem Quando `mediaUrl` está presente, `mediaType` é obrigatório e deve ser **maiúsculo** (`IMAGE`, `VIDEO`, `DOCUMENT`). `headerText` é ignorado neste caso. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/button/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "contentText": "Promoção relâmpago: 30% OFF nas assinaturas anuais.", "footerText": "Válido até 23h59 de hoje", "mediaUrl": "https://exemplo.com/img/promo.jpg", "mediaType": "IMAGE", "buttons": [ { "id": "promo_subscribe", "displayText": "Assinar agora", "type": "REPLY" }, { "id": "promo_details", "displayText": "Ver detalhes", "type": "REPLY" } ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/button/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", contentText: "Promoção relâmpago: 30% OFF nas assinaturas anuais.", footerText: "Válido até 23h59 de hoje", mediaUrl: "https://exemplo.com/img/promo.jpg", mediaType: "IMAGE", buttons: [ { id: "promo_subscribe", displayText: "Assinar agora", type: "REPLY" }, { id: "promo_details", displayText: "Ver detalhes", type: "REPLY" } ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/button/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "contentText": "Promoção relâmpago: 30% OFF nas assinaturas anuais.", "footerText": "Válido até 23h59 de hoje", "mediaUrl": "https://exemplo.com/img/promo.jpg", "mediaType": "IMAGE", "buttons": [ {"id": "promo_subscribe", "displayText": "Assinar agora", "type": "REPLY"}, {"id": "promo_details", "displayText": "Ver detalhes", "type": "REPLY"} ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "contentText": "Promoção relâmpago: 30% OFF nas assinaturas anuais.", "footerText": "Válido até 23h59 de hoje", "mediaUrl": "https://exemplo.com/img/promo.jpg", "mediaType": "IMAGE", "buttons": [ { "id": "promo_subscribe", "displayText": "Assinar agora", "type": "REPLY" }, { "id": "promo_details", "displayText": "Ver detalhes", "type": "REPLY" } ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/button/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Mix de URL + CALL + COPY Combina três tipos diferentes de botão em uma só mensagem. O `id` carrega valores semânticos distintos por tipo. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/button/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "headerText": "Pedido #12345 confirmado", "contentText": "Escolha como quer prosseguir:", "footerText": "Cupom válido por 24h", "buttons": [ { "id": "https://loja.exemplo.com/pedido/12345", "displayText": "Acompanhar pedido", "type": "URL" }, { "id": "+551130000000", "displayText": "Falar com SAC", "type": "CALL" }, { "id": "RYZE10", "displayText": "Copiar cupom", "type": "COPY" } ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/button/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", headerText: "Pedido #12345 confirmado", contentText: "Escolha como quer prosseguir:", footerText: "Cupom válido por 24h", buttons: [ { id: "https://loja.exemplo.com/pedido/12345", displayText: "Acompanhar pedido", type: "URL" }, { id: "+551130000000", displayText: "Falar com SAC", type: "CALL" }, { id: "RYZE10", displayText: "Copiar cupom", type: "COPY" } ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/button/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "headerText": "Pedido #12345 confirmado", "contentText": "Escolha como quer prosseguir:", "footerText": "Cupom válido por 24h", "buttons": [ {"id": "https://loja.exemplo.com/pedido/12345", "displayText": "Acompanhar pedido", "type": "URL"}, {"id": "+551130000000", "displayText": "Falar com SAC", "type": "CALL"}, {"id": "RYZE10", "displayText": "Copiar cupom", "type": "COPY"} ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "headerText": "Pedido #12345 confirmado", "contentText": "Escolha como quer prosseguir:", "footerText": "Cupom válido por 24h", "buttons": [ { "id": "https://loja.exemplo.com/pedido/12345", "displayText": "Acompanhar pedido", "type": "URL" }, { "id": "+551130000000", "displayText": "Falar com SAC", "type": "CALL" }, { "id": "RYZE10", "displayText": "Copiar cupom", "type": "COPY" } ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/button/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O `content` retorna o `contentText` enviado, e o `messageType` é fixo em `buttons`. A definição dos botões em si não vem na resposta, guarde o `messageId` para correlacionar os cliques que chegam via webhook. ```json 200 OK { "success": true, "message": "Buttons message sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1F5F5", "direction": "sent", "messageType": "buttons", "content": "Como podemos ajudar você hoje?", "source": "api", "timestamp": "2026-04-30T14:30:00Z", "chat": { "jid": "5511999999999@s.whatsapp.net", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" } } } ``` Quando o usuário toca em um botão `REPLY`, a resposta chega no webhook com `message.type` igual a `template_button_reply` e o `id` do botão clicado em `message.content` (e também em `message.interactive.selectedButtonId`). Capture isso pelo webhook para encadear o fluxo. ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Destino: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`, `@lid`, `@g.us`, `@newsletter`). Texto principal do corpo da mensagem (entre header e botões). Lista de botões. **Mínimo 1, máximo 3** (limite do WhatsApp). Cada botão tem: - `id` (string, **obrigatório**), semântica varia por `type`: `REPLY` retorna esse ID; `URL` abre essa URL; `CALL` disca esse número; `COPY` copia esse código. - `displayText` (string, **obrigatório**), texto visível no botão. - `type` (string), `REPLY` (padrão), `URL`, `CALL` ou `COPY`. Outro valor retorna `400 Button N: Type must be one of: REPLY, URL, CALL, COPY`. Título exibido no topo da mensagem. **Ignorado se `mediaUrl` for fornecido** (a mídia substitui o header). Texto opcional exibido abaixo dos botões. URL de uma mídia (imagem, vídeo ou documento) para usar como header. Quando enviado, `mediaType` torna-se obrigatório. Tipo da mídia em `mediaUrl`. **Obrigatório se `mediaUrl` estiver presente**. Aceita apenas `IMAGE`, `VIDEO` ou `DOCUMENT` (maiúsculo). Outro valor retorna `400 MediaType must be one of: IMAGE, VIDEO, DOCUMENT`. Tempo em **segundos** para aguardar antes de enviar. Durante o intervalo, o servidor envia o indicador de "digitando..." e dispara o "paused" antes do envio real. ID da mensagem a ser citada (reply). A mensagem original precisa pertencer à mesma instância e ter sido salva no banco. Quando `true` **e** `replyTo` aponta para uma mensagem originária de um grupo, a mensagem é redirecionada para o **privado** do autor original (mantendo a citação). Identificador de origem para rastreabilidade (ex.: `crm`, `bot-suporte`, `n8n`). Salvo no registro da mensagem e propagado para webhooks. ## Notas - **`delay` é em segundos** (não milissegundos). - O WhatsApp aceita **no máximo 3 botões** por mensagem. Quatro ou mais retornam `400 Maximum of 3 buttons allowed`. - Se `mediaUrl` é enviado, `headerText` é silenciosamente ignorado. - `mediaType` é normalizado para maiúsculo internamente, envie sempre `IMAGE`, `VIDEO` ou `DOCUMENT`. - Para botões `URL`, garanta que o link comece com `https://` para evitar bloqueio pelo cliente. - Para botões `CALL`, use formato internacional (`+5511...`). - Aparelhos antigos podem cair em fallback de texto e exibir os botões como mensagem comum. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid request body: ` | | 400 |, | `Number is required` | | 400 |, | `ContentText is required` | | 400 |, | `At least one button is required` | | 400 |, | `Maximum of 3 buttons allowed` | | 400 |, | `MediaType is required when MediaURL is provided` | | 400 |, | `MediaType must be one of: IMAGE, VIDEO, DOCUMENT` | | 400 |, | `Button N: ID is required` | | 400 |, | `Button N: DisplayText is required` | | 400 |, | `Button N: Type must be one of: REPLY, URL, CALL, COPY` | | 400 | `invalid_number` | `Invalid phone number format: ` | | 400 | `invalid_request` | (motivo do request inválido detectado pelo serviço) | | 404 |, | `Instance not found` | | 500 | `send_failed` | `Failed to send buttons message: ` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "Maximum of 3 buttons allowed" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Envia uma **lista interativa** com até **10 seções** e **10 rows** por seção (limite total de **100 rows** somando todas as seções). O destinatário toca o botão (`buttonText`) para abrir o menu e escolher uma das opções, e o WhatsApp retorna o `id` da row selecionada como uma mensagem de resposta. Ideal para menus, catálogos curtos, FAQs guiadas e atendimento estruturado. ## Exemplos ### Lista simples (1 seção) Menu enxuto com uma única seção "Pratos" e duas rows. O destinatário toca em "Ver opções" para abrir o menu e selecionar uma das opções, que volta como resposta carregando o `id` (`p1` ou `p2`). ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/list/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "contentText": "Selecione um item do cardápio:", "buttonText": "Ver opções", "sections": [ { "title": "Pratos", "rows": [ { "id": "p1", "title": "Lasanha", "description": "Bolonhesa, 4 fatias" }, { "id": "p2", "title": "Risoto", "description": "Funghi" } ] } ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/list/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", contentText: "Selecione um item do cardápio:", buttonText: "Ver opções", sections: [ { title: "Pratos", rows: [ { id: "p1", title: "Lasanha", description: "Bolonhesa, 4 fatias" }, { id: "p2", title: "Risoto", description: "Funghi" } ] } ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/list/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "contentText": "Selecione um item do cardápio:", "buttonText": "Ver opções", "sections": [ { "title": "Pratos", "rows": [ {"id": "p1", "title": "Lasanha", "description": "Bolonhesa, 4 fatias"}, {"id": "p2", "title": "Risoto", "description": "Funghi"} ] } ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "contentText": "Selecione um item do cardápio:", "buttonText": "Ver opções", "sections": [ { "title": "Pratos", "rows": [ { "id": "p1", "title": "Lasanha", "description": "Bolonhesa, 4 fatias" }, { "id": "p2", "title": "Risoto", "description": "Funghi" } ] } ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/list/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Menu multi-seção (categorias) Lista com múltiplas categorias agrupadas em seções. Cada seção é renderizada com um cabeçalho próprio dentro do menu, separando visualmente os grupos. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/list/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "contentText": "Cardápio completo. Escolha uma opção:", "buttonText": "Abrir menu", "sections": [ { "title": "Pratos principais", "rows": [ { "id": "p1", "title": "Lasanha", "description": "Bolonhesa, 4 fatias" }, { "id": "p2", "title": "Risoto", "description": "Funghi" }, { "id": "p3", "title": "Salmão", "description": "Grelhado com legumes" } ] }, { "title": "Bebidas", "rows": [ { "id": "b1", "title": "Suco natural" }, { "id": "b2", "title": "Refrigerante" } ] }, { "title": "Sobremesas", "rows": [ { "id": "s1", "title": "Pudim" }, { "id": "s2", "title": "Sorvete", "description": "3 sabores" } ] } ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/list/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", contentText: "Cardápio completo. Escolha uma opção:", buttonText: "Abrir menu", sections: [ { title: "Pratos principais", rows: [ { id: "p1", title: "Lasanha", description: "Bolonhesa, 4 fatias" }, { id: "p2", title: "Risoto", description: "Funghi" }, { id: "p3", title: "Salmão", description: "Grelhado com legumes" } ] }, { title: "Bebidas", rows: [ { id: "b1", title: "Suco natural" }, { id: "b2", title: "Refrigerante" } ] }, { title: "Sobremesas", rows: [ { id: "s1", title: "Pudim" }, { id: "s2", title: "Sorvete", description: "3 sabores" } ] } ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/list/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "contentText": "Cardápio completo. Escolha uma opção:", "buttonText": "Abrir menu", "sections": [ { "title": "Pratos principais", "rows": [ {"id": "p1", "title": "Lasanha", "description": "Bolonhesa, 4 fatias"}, {"id": "p2", "title": "Risoto", "description": "Funghi"}, {"id": "p3", "title": "Salmão", "description": "Grelhado com legumes"} ] }, { "title": "Bebidas", "rows": [ {"id": "b1", "title": "Suco natural"}, {"id": "b2", "title": "Refrigerante"} ] }, { "title": "Sobremesas", "rows": [ {"id": "s1", "title": "Pudim"}, {"id": "s2", "title": "Sorvete", "description": "3 sabores"} ] } ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "contentText": "Cardápio completo. Escolha uma opção:", "buttonText": "Abrir menu", "sections": [ { "title": "Pratos principais", "rows": [ { "id": "p1", "title": "Lasanha", "description": "Bolonhesa, 4 fatias" }, { "id": "p2", "title": "Risoto", "description": "Funghi" }, { "id": "p3", "title": "Salmão", "description": "Grelhado com legumes" } ] }, { "title": "Bebidas", "rows": [ { "id": "b1", "title": "Suco natural" }, { "id": "b2", "title": "Refrigerante" } ] }, { "title": "Sobremesas", "rows": [ { "id": "s1", "title": "Pudim" }, { "id": "s2", "title": "Sorvete", "description": "3 sabores" } ] } ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/list/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Lista com cabeçalho e rodapé Adiciona `headerText` (título acima do corpo) e `footerText` (texto em cinza abaixo do botão), úteis para branding e disclaimers curtos. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/list/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "headerText": "Atendimento RyzeAPI", "contentText": "Como podemos ajudar você hoje?", "footerText": "Atendimento 24/7", "buttonText": "Ver opções", "sections": [ { "title": "Suporte", "rows": [ { "id": "sup_tec", "title": "Suporte técnico" }, { "id": "sup_fin", "title": "Financeiro" }, { "id": "sup_com", "title": "Comercial", "description": "Falar com vendas" } ] } ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/list/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", headerText: "Atendimento RyzeAPI", contentText: "Como podemos ajudar você hoje?", footerText: "Atendimento 24/7", buttonText: "Ver opções", sections: [ { title: "Suporte", rows: [ { id: "sup_tec", title: "Suporte técnico" }, { id: "sup_fin", title: "Financeiro" }, { id: "sup_com", title: "Comercial", description: "Falar com vendas" } ] } ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/list/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "headerText": "Atendimento RyzeAPI", "contentText": "Como podemos ajudar você hoje?", "footerText": "Atendimento 24/7", "buttonText": "Ver opções", "sections": [ { "title": "Suporte", "rows": [ {"id": "sup_tec", "title": "Suporte técnico"}, {"id": "sup_fin", "title": "Financeiro"}, {"id": "sup_com", "title": "Comercial", "description": "Falar com vendas"} ] } ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "headerText": "Atendimento RyzeAPI", "contentText": "Como podemos ajudar você hoje?", "footerText": "Atendimento 24/7", "buttonText": "Ver opções", "sections": [ { "title": "Suporte", "rows": [ { "id": "sup_tec", "title": "Suporte técnico" }, { "id": "sup_fin", "title": "Financeiro" }, { "id": "sup_com", "title": "Comercial", "description": "Falar com vendas" } ] } ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/list/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O `content` retornado é o `contentText` enviado, e o `messageType` fica fixo em `list`. Guarde o `messageId` para correlacionar com os eventos de seleção que chegam via webhook. ```json 200 OK { "success": true, "message": "List message sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1B0F5", "direction": "sent", "messageType": "list", "content": "Selecione um item do cardápio:", "source": "api", "timestamp": "2026-04-30T14:30:00Z", "chat": { "jid": "5511999999999@s.whatsapp.net", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" } } } ``` Quando o destinatário escolhe uma opção, o WhatsApp envia uma mensagem de resposta contendo o `id` da row selecionada, você captura essa resposta via webhook/websocket de eventos para dar continuidade ao fluxo. ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Destino: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`, `@lid`, `@g.us`, `@newsletter`). Corpo principal da mensagem (descrição). Aparece acima do botão que abre a lista. Texto do botão que abre a lista (ex.: `"Ver opções"`, `"Abrir menu"`). Limitado pelo WhatsApp a poucos caracteres. Array de **1 a 10 seções**. O total de rows somando todas as seções não pode exceder **100**. Título da seção (cabeçalho do grupo dentro do menu). Array de **1 a 10 rows** por seção. Identificador único da row. É o valor que volta como resposta quando o usuário seleciona essa opção. Título exibido para a opção. Texto secundário em cinza, abaixo do título. Título exibido acima do `contentText`. Opcional. Texto em cinza claro abaixo do botão. Opcional, ideal para disclaimers curtos. Tempo em **segundos** para aguardar antes de enviar. Durante o intervalo, o servidor envia o indicador de "digitando..." ao destinatário e dispara o "paused" antes do envio real. ID da mensagem a ser citada (reply). A mensagem original precisa pertencer à mesma instância e ter sido salva no banco. Quando `true` **e** `replyTo` aponta para uma mensagem originária de um grupo, a resposta é redirecionada para o **privado** do autor original (mantendo a citação). Identificador de origem para rastreabilidade (ex.: `crm`, `bot-suporte`, `n8n`). Salvo no registro da mensagem no banco e propagado para webhooks. ## Notas - **Limites do WhatsApp:** máximo de **10 seções**, **10 rows por seção** e **100 rows no total**. Excedeu? O servidor responde com `400` antes de tentar enviar. - A row selecionada pelo usuário retorna pelo evento de mensagem como uma resposta carregando o `id` original, capture isso via webhook para mapear a escolha. - Listas funcionam bem em DM, grupos e canais, mas a UX pode variar entre WhatsApp Business e WhatsApp pessoal. - `description` é opcional por row, mas melhora bastante a legibilidade quando o título sozinho é ambíguo. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid request body: ` | | 400 |, | `Number is required` | | 400 |, | `ContentText is required` | | 400 |, | `ButtonText is required` | | 400 |, | `At least one section is required` | | 400 |, | `Maximum of 10 sections allowed` | | 400 |, | `Section N: Title is required` | | 400 |, | `Section N: At least one row is required` | | 400 |, | `Section N: Maximum of 10 rows allowed` | | 400 |, | `Section N, Row M: ID is required` | | 400 |, | `Section N, Row M: Title is required` | | 400 |, | `Total number of rows across all sections cannot exceed 100` | | 400 | `invalid_number` | `Invalid phone number format: ` | | 404 |, | `Instance not found` | | 500 | `send_failed` | `Failed to send message: ` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "ContentText is required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Envia uma mensagem com **carrossel de cards** deslizáveis. Cada card tem `header` (título obrigatório, com mídia opcional via `imageUrl` ou `videoUrl`), `body.text` (obrigatório), `footer` opcional e até alguns botões interativos. Os botões aceitam quatro tipos: `REPLY` (padrão, retorna o ID quando clicado), `URL` (abre link), `CALL` (disca número) e `COPY` (copia código). É possível adicionar `message` (texto antes do carrossel) e `footer` (texto abaixo). Suporta `delay`, `replyTo` e `replyPrivate`. ## Exemplos ### Carrossel simples com botões REPLY Dois cards apenas com texto e botões de resposta rápida. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/carousel/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "message": "Escolha um dos planos abaixo:", "footer": "Promoção válida até o fim do mês.", "cards": [ { "header": { "title": "Plano Básico" }, "body": { "text": "5 instâncias, suporte por e-mail." }, "footer": "R$ 49/mês", "buttons": [ { "displayText": "Quero o Básico", "id": "plan_basic", "type": "REPLY" } ] }, { "header": { "title": "Plano Pro" }, "body": { "text": "20 instâncias, suporte prioritário, webhooks." }, "footer": "R$ 149/mês", "buttons": [ { "displayText": "Quero o Pro", "id": "plan_pro", "type": "REPLY" } ] } ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/carousel/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", message: "Escolha um dos planos abaixo:", footer: "Promoção válida até o fim do mês.", cards: [ { header: { title: "Plano Básico" }, body: { text: "5 instâncias, suporte por e-mail." }, footer: "R$ 49/mês", buttons: [ { displayText: "Quero o Básico", id: "plan_basic", type: "REPLY" } ] }, { header: { title: "Plano Pro" }, body: { text: "20 instâncias, suporte prioritário, webhooks." }, footer: "R$ 149/mês", buttons: [ { displayText: "Quero o Pro", id: "plan_pro", type: "REPLY" } ] } ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/carousel/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "message": "Escolha um dos planos abaixo:", "footer": "Promoção válida até o fim do mês.", "cards": [ { "header": {"title": "Plano Básico"}, "body": {"text": "5 instâncias, suporte por e-mail."}, "footer": "R$ 49/mês", "buttons": [ {"displayText": "Quero o Básico", "id": "plan_basic", "type": "REPLY"} ] }, { "header": {"title": "Plano Pro"}, "body": {"text": "20 instâncias, suporte prioritário, webhooks."}, "footer": "R$ 149/mês", "buttons": [ {"displayText": "Quero o Pro", "id": "plan_pro", "type": "REPLY"} ] } ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "message": "Escolha um dos planos abaixo:", "footer": "Promoção válida até o fim do mês.", "cards": [ { "header": { "title": "Plano Básico" }, "body": { "text": "5 instâncias, suporte por e-mail." }, "footer": "R$ 49/mês", "buttons": [ { "displayText": "Quero o Básico", "id": "plan_basic", "type": "REPLY" } ] }, { "header": { "title": "Plano Pro" }, "body": { "text": "20 instâncias, suporte prioritário, webhooks." }, "footer": "R$ 149/mês", "buttons": [ { "displayText": "Quero o Pro", "id": "plan_pro", "type": "REPLY" } ] } ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/carousel/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Carrossel com header de imagem e botões URL `imageUrl` no header e botões do tipo `URL` (o `id` recebe a URL a abrir). ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/carousel/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "message": "Conheça nossos produtos:", "cards": [ { "header": { "title": "Tênis Runner X", "subtitle": "Edição limitada", "imageUrl": "https://exemplo.com/img/runner-x.jpg" }, "body": { "text": "Amortecimento de alta performance, ideal para corridas longas." }, "buttons": [ { "displayText": "Ver detalhes", "id": "https://loja.exemplo.com/runner-x", "type": "URL" }, { "displayText": "Falar com vendedor", "id": "talk_seller_runner", "type": "REPLY" } ] } ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/carousel/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", message: "Conheça nossos produtos:", cards: [ { header: { title: "Tênis Runner X", subtitle: "Edição limitada", imageUrl: "https://exemplo.com/img/runner-x.jpg" }, body: { text: "Amortecimento de alta performance, ideal para corridas longas." }, buttons: [ { displayText: "Ver detalhes", id: "https://loja.exemplo.com/runner-x", type: "URL" }, { displayText: "Falar com vendedor", id: "talk_seller_runner", type: "REPLY" } ] } ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/carousel/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "message": "Conheça nossos produtos:", "cards": [ { "header": { "title": "Tênis Runner X", "subtitle": "Edição limitada", "imageUrl": "https://exemplo.com/img/runner-x.jpg" }, "body": {"text": "Amortecimento de alta performance, ideal para corridas longas."}, "buttons": [ {"displayText": "Ver detalhes", "id": "https://loja.exemplo.com/runner-x", "type": "URL"}, {"displayText": "Falar com vendedor", "id": "talk_seller_runner", "type": "REPLY"} ] } ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "message": "Conheça nossos produtos:", "cards": [ { "header": { "title": "Tênis Runner X", "subtitle": "Edição limitada", "imageUrl": "https://exemplo.com/img/runner-x.jpg" }, "body": { "text": "Amortecimento de alta performance, ideal para corridas longas." }, "buttons": [ { "displayText": "Ver detalhes", "id": "https://loja.exemplo.com/runner-x", "type": "URL" }, { "displayText": "Falar com vendedor", "id": "talk_seller_runner", "type": "REPLY" } ] } ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/carousel/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Carrossel com header de vídeo `videoUrl` substitui `imageUrl` no header. Use um dos dois, não os dois. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/carousel/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "cards": [ { "header": { "title": "Tutorial: como ativar", "videoUrl": "https://exemplo.com/videos/onboarding.mp4" }, "body": { "text": "Veja em 30 segundos como configurar sua primeira instância." }, "buttons": [ { "displayText": "Começar agora", "id": "start_onboarding", "type": "REPLY" }, { "displayText": "Ligar para suporte", "id": "+551130000000", "type": "CALL" }, { "displayText": "Copiar cupom", "id": "RYZE10", "type": "COPY" } ] } ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/carousel/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", cards: [ { header: { title: "Tutorial: como ativar", videoUrl: "https://exemplo.com/videos/onboarding.mp4" }, body: { text: "Veja em 30 segundos como configurar sua primeira instância." }, buttons: [ { displayText: "Começar agora", id: "start_onboarding", type: "REPLY" }, { displayText: "Ligar para suporte", id: "+551130000000", type: "CALL" }, { displayText: "Copiar cupom", id: "RYZE10", type: "COPY" } ] } ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/carousel/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "cards": [ { "header": { "title": "Tutorial: como ativar", "videoUrl": "https://exemplo.com/videos/onboarding.mp4" }, "body": {"text": "Veja em 30 segundos como configurar sua primeira instância."}, "buttons": [ {"displayText": "Começar agora", "id": "start_onboarding", "type": "REPLY"}, {"displayText": "Ligar para suporte", "id": "+551130000000", "type": "CALL"}, {"displayText": "Copiar cupom", "id": "RYZE10", "type": "COPY"} ] } ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "cards": [ { "header": { "title": "Tutorial: como ativar", "videoUrl": "https://exemplo.com/videos/onboarding.mp4" }, "body": { "text": "Veja em 30 segundos como configurar sua primeira instância." }, "buttons": [ { "displayText": "Começar agora", "id": "start_onboarding", "type": "REPLY" }, { "displayText": "Ligar para suporte", "id": "+551130000000", "type": "CALL" }, { "displayText": "Copiar cupom", "id": "RYZE10", "type": "COPY" } ] } ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/carousel/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O `messageType` retornado é `interactive` (carrossel é uma variação de mensagem interativa do WhatsApp), e o `content` traz uma descrição agregada (`" - Carousel with N card(s)"`) usada pelo histórico. Os cards individuais não vêm na resposta, guarde o `messageId` para correlacionar com cliques via webhook. ```json 200 OK { "success": true, "message": "Carousel sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1E4E4", "direction": "sent", "messageType": "interactive", "content": "Escolha um dos planos abaixo: - Carousel with 3 card(s)", "source": "api", "timestamp": "2026-04-30T14:30:00Z", "chat": { "jid": "5511999999999@s.whatsapp.net", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" } } } ``` Quando o usuário toca em um botão `REPLY`, a resposta chega no webhook com `message.type` igual a `template_button_reply` e o `id` do botão clicado em `message.content` (e também em `message.interactive.selectedButtonId`). Capture isso pelo webhook para encadear o fluxo. Em carrosséis, `message.interactive.selectedCarouselCardIndex` indica qual card foi tocado. ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Destino: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`, `@lid`, `@g.us`, `@newsletter`). Texto exibido **acima** do carrossel (opcional). Texto exibido **abaixo** do carrossel (opcional). Lista de cards do carrossel. **Mínimo 1**. Cada card é um objeto com `header`, `body`, `footer` e `buttons` (descritos abaixo). Header do card. Sub-campos: - `title` (string, **obrigatório**), título do card. - `subtitle` (string), subtítulo opcional. - `imageUrl` (string), URL de imagem para o header. - `videoUrl` (string), URL de vídeo para o header. Use **uma das duas** mídias por card. Corpo do card. Sub-campo: - `text` (string, **obrigatório**), conteúdo textual do card. Texto opcional exibido no rodapé do card individual. Lista de botões do card. Cada botão tem: - `displayText` (string, **obrigatório**), texto visível. - `id` (string, **obrigatório**), semântica varia conforme `type`: para `REPLY`, é o ID retornado quando clicado; para `URL`, a URL a abrir; para `CALL`, o número a discar; para `COPY`, o código a copiar. - `type` (string), `REPLY` (padrão), `URL`, `CALL` ou `COPY`. Valores diferentes retornam `400 Card N, Button M: Type must be one of: REPLY, URL, CALL, COPY`. Tempo em **segundos** para aguardar antes de enviar. Durante o intervalo, o servidor envia o indicador de "digitando..." e dispara o "paused" antes do envio real. ID da mensagem a ser citada (reply). A mensagem original precisa pertencer à mesma instância e ter sido salva no banco. Quando `true` **e** `replyTo` aponta para uma mensagem originária de um grupo, o carrossel é redirecionado para o **privado** do autor original (mantendo a citação). Identificador de origem para rastreabilidade (ex.: `crm`, `bot-suporte`, `n8n`). Salvo no registro da mensagem e propagado para webhooks. ## Notas - **`delay` é em segundos** (não milissegundos). - Em cada `header`, escolha **uma única** mídia: ou `imageUrl` ou `videoUrl`. Enviar ambas pode resultar em renderização inconsistente no cliente. - Validação do servidor: cada card precisa de `header.title` e `body.text` não vazios; cada botão precisa de `displayText` e `id` não vazios. Erros são retornados com o índice (`Card N, Button M: ...`) para facilitar debug. - Aparelhos antigos do WhatsApp podem cair em fallback de texto e exibir o carrossel como mensagem comum. - Para botões `URL`, garanta que o link comece com `https://` para evitar ser bloqueado pelo cliente. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid request body: ` | | 400 |, | `Number is required` | | 400 |, | `At least one card is required` | | 400 |, | `Card N: Header title is required` | | 400 |, | `Card N: Body text is required` | | 400 |, | `Card N, Button M: Display text is required` | | 400 |, | `Card N, Button M: ID is required` | | 400 |, | `Card N, Button M: Type must be one of: REPLY, URL, CALL, COPY` | | 400 | `invalid_number` | `Invalid phone number format: ` | | 400 | `media_download_failed` | (motivo do download de mídia do header falhar) | | 404 |, | `Instance not found` | | 500 | `media_upload_failed` | (motivo do upload de mídia do header falhar) | | 500 | `send_failed` | `Failed to send carousel: ` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "Card 1: Header title is required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Envia uma mensagem com um botão que abre um **Native Flow** do WhatsApp, um formulário nativo que coleta dados estruturados (nome, telefone, e-mail, CPF/CNPJ, endereço) sem sair da conversa. Suporta dois fluxos prontos: `contact_details` (Dados do cliente) e `registration_offer` (Oferta de Cadastro), além de uma escape hatch via `buttonParamsJSON` para flows totalmente customizados. Ideal para captação de leads, cadastros e ofertas com confirmação rápida. **Compatibilidade de cliente:** Native Flows ainda **não são suportados no WhatsApp Web/Desktop**. O botão do formulário só será renderizado para destinatários nos apps oficiais de **Android** e **iOS**, em outros clientes a mensagem aparecerá sem o botão interativo. ## Exemplos ### Oferta de cadastro (registration_offer) Envia uma oferta com título e descrição. O botão abre o Flow padrão de cadastro com os campos visíveis configuráveis. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/form/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "message": "Garanta sua vaga no curso com 50% off!", "formType": "registration_offer", "buttonLabel": "Quero garantir", "offerName": "Curso de Go - Turma Maio", "offerDescription": "Acesso vitalício + certificado + suporte 30 dias" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/form/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", message: "Garanta sua vaga no curso com 50% off!", formType: "registration_offer", buttonLabel: "Quero garantir", offerName: "Curso de Go - Turma Maio", offerDescription: "Acesso vitalício + certificado + suporte 30 dias" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/form/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "message": "Garanta sua vaga no curso com 50% off!", "formType": "registration_offer", "buttonLabel": "Quero garantir", "offerName": "Curso de Go - Turma Maio", "offerDescription": "Acesso vitalício + certificado + suporte 30 dias" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "message": "Garanta sua vaga no curso com 50% off!", "formType": "registration_offer", "buttonLabel": "Quero garantir", "offerName": "Curso de Go - Turma Maio", "offerDescription": "Acesso vitalício + certificado + suporte 30 dias" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/form/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Captura de dados de contato (contact_details) Usa o flow oficial `contact_details` do WhatsApp. Esconda campos que você não quer pedir via flags `*Visible`. Aqui pedimos só nome, telefone e e-mail. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/form/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "message": "Para finalizar seu atendimento, complete seus dados:", "formType": "contact_details", "buttonLabel": "Preencher dados", "fullNameVisible": true, "phoneNumberVisible": true, "emailVisible": true, "cpfOrCnpjVisible": false, "deliveryAddressVisible": false }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/form/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", message: "Para finalizar seu atendimento, complete seus dados:", formType: "contact_details", buttonLabel: "Preencher dados", fullNameVisible: true, phoneNumberVisible: true, emailVisible: true, cpfOrCnpjVisible: false, deliveryAddressVisible: false }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/form/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "message": "Para finalizar seu atendimento, complete seus dados:", "formType": "contact_details", "buttonLabel": "Preencher dados", "fullNameVisible": True, "phoneNumberVisible": True, "emailVisible": True, "cpfOrCnpjVisible": False, "deliveryAddressVisible": False } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "message": "Para finalizar seu atendimento, complete seus dados:", "formType": "contact_details", "buttonLabel": "Preencher dados", "fullNameVisible": true, "phoneNumberVisible": true, "emailVisible": true, "cpfOrCnpjVisible": false, "deliveryAddressVisible": false }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/form/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Flow customizado via `buttonParamsJSON` Escape hatch para flows próprios (criados no WhatsApp Business Manager). Quando `buttonParamsJSON` é fornecido, o servidor **ignora** todos os outros campos relacionados ao flow (`formType`, `flowId`, visibilidades, `offerName`, etc.) e usa o JSON literal como `params` do botão Native Flow. Acesse o WhatsApp Business Manager para criar e gerenciar seus Flows customizados (obtenha o `flow_id` aqui). Teste e prototipe schemas de Flow no playground oficial da Meta antes de publicar em produção. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/form/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "message": "Responda à pesquisa rápida e ganhe 10% off", "buttonLabel": "Responder pesquisa", "buttonParamsJSON": "{\"flow_message_version\":\"3\",\"flow_token\":\"meu-token-123\",\"flow_id\":\"123456789012345\",\"flow_cta\":\"Responder pesquisa\",\"flow_action\":\"navigate\",\"flow_action_payload\":{\"screen\":\"WELCOME\"}}" }' ``` ```javascript JavaScript const flowParams = { flow_message_version: "3", flow_token: "meu-token-123", flow_id: "123456789012345", flow_cta: "Responder pesquisa", flow_action: "navigate", flow_action_payload: { screen: "WELCOME" } }; await fetch(`https://ryzeapi.cloud/api/message/form/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", message: "Responda à pesquisa rápida e ganhe 10% off", buttonLabel: "Responder pesquisa", buttonParamsJSON: JSON.stringify(flowParams) }) }); ``` ```python Python import os, json, requests flow_params = { "flow_message_version": "3", "flow_token": "meu-token-123", "flow_id": "123456789012345", "flow_cta": "Responder pesquisa", "flow_action": "navigate", "flow_action_payload": {"screen": "WELCOME"} } requests.post( f"https://ryzeapi.cloud/api/message/form/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "message": "Responda à pesquisa rápida e ganhe 10% off", "buttonLabel": "Responder pesquisa", "buttonParamsJSON": json.dumps(flow_params) } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "message": "Responda à pesquisa rápida e ganhe 10% off", "buttonLabel": "Responder pesquisa", "buttonParamsJSON": "{\"flow_message_version\":\"3\",\"flow_token\":\"meu-token-123\",\"flow_id\":\"123456789012345\",\"flow_cta\":\"Responder pesquisa\",\"flow_action\":\"navigate\",\"flow_action_payload\":{\"screen\":\"WELCOME\"}}" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/form/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O `messageType` retornado é `interactive` (formulário é uma variação de mensagem interativa Native Flow), e o `content` ecoa o `message` enviado. Guarde o `messageId` (e o `flowToken`, gerado automaticamente quando você não envia) para correlacionar com a resposta do flow no webhook. ```json 200 OK { "success": true, "message": "Form message sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1B0F5", "direction": "sent", "messageType": "interactive", "content": "Garanta sua vaga no curso com 50% off!", "source": "api", "timestamp": "2026-04-30T14:30:00Z", "chat": { "jid": "5511999999999@s.whatsapp.net", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" } } } ``` Quando o usuário preenche e envia o formulário, o WhatsApp envia uma mensagem do tipo `interactive_response` carregando o `flow_token` (UUID que você informou ou o gerado automaticamente) e o JSON com as respostas. Capture via webhook/websocket para correlacionar com o envio. ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Destino: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`, `@lid`, `@g.us`). Texto exibido no balão da mensagem, acima do botão que abre o Flow. Tipo de formulário pré-configurado: `"contact_details"` (Dados do cliente) ou `"registration_offer"` (Oferta de Cadastro). Ignorado se `buttonParamsJSON` for fornecido. Texto exibido no botão que abre o Flow (`flow_cta`). Token para correlacionar a resposta do formulário com o envio. Quando omitido, o servidor gera um **UUID** automaticamente. Você pode usar esse token para amarrar com um lead/oportunidade no seu CRM. ID do Flow no WhatsApp Business. Padrões por `formType`: - `contact_details` → `1889354358373616` - `registration_offer` → `892701196712475` Sobrescreva apenas se for usar um Flow customizado pelo nome (sem usar `buttonParamsJSON`). Versão do `flow_message_version` enviada para o WhatsApp. Versão do `message_version` do payload Native Flow. **Escape hatch** para flows totalmente customizados. Quando fornecido, o servidor envia esse JSON literal como `params` do botão Native Flow e **ignora** `formType`, `flowId`, `flowToken`, `flowMessageVersion`, `messageVersion`, todas as flags `*Visible`, `offerName` e `offerDescription`. Útil para integrar com flows que você criou no WhatsApp Business Manager com schemas específicos. Exibe o campo "Nome completo" no formulário. Ignorado se `buttonParamsJSON` for fornecido. Exibe o campo "Número de telefone". Exibe o campo "E-mail". Exibe o campo "CPF/CNPJ". Exibe o campo "Endereço de entrega". Título da oferta exibido dentro do Flow. Usado quando `formType=registration_offer`. Descrição da oferta exibida dentro do Flow. Usado quando `formType=registration_offer`. Tempo em **segundos** para aguardar antes de enviar. Durante o intervalo, o servidor envia o indicador de "digitando..." ao destinatário. ID da mensagem a ser citada (reply). A mensagem original precisa pertencer à mesma instância e ter sido salva no banco. Quando `true` **e** `replyTo` aponta para uma mensagem originária de um grupo, a resposta é redirecionada para o **privado** do autor original. Identificador de origem para rastreabilidade (ex.: `crm`, `landing-vendas`, `n8n`). ## Notas - Os Flows `contact_details` e `registration_offer` são templates do WhatsApp já aprovados e prontos para uso. Se quiser um formulário com campos específicos (perguntas custom, lógicas de tela), use **`buttonParamsJSON`** com um Flow seu. - O **`flowToken`** é o seu identificador para amarrar a resposta do formulário com o registro de origem (lead, pedido, etc.). Se não enviar, salve o UUID gerado para conseguir correlacionar depois. - Quando `buttonParamsJSON` é enviado, todos os outros campos relacionados ao Flow são ignorados, você assume controle total do payload, incluindo o `flow_id`, `flow_action`, `flow_action_payload` e `flow_message_version`. - Native Flow só funciona em chats 1-a-1 (`@s.whatsapp.net`) e em grupos (`@g.us`); canais (`@newsletter`) não são suportados pelo WhatsApp. - A resposta do formulário chega como evento `interactive_response`, não é uma mensagem de texto comum, então trate o webhook adequadamente. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid request: ` | | 400 |, | `Number is required` | | 400 |, | `Message is required` | | 400 | `invalid_number` | `Invalid phone number format: ` | | 400 | `reply_message_not_found` | `Original message not found (ID: ...)` | | 400 | `reply_message_instance_mismatch` | `Original message does not belong to this instance` | | 400 | `private_reply_failed` | (motivo do erro de redirecionamento privado) | | 404 |, | `Instance not found` | | 500 | `send_failed` | `Failed to send message: ` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "Message is required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Envia uma enquete a um contato 1-a-1, grupo (`@g.us`) ou canal (`@newsletter`). Suporta de **2 a 12 opções** (limite do WhatsApp). O campo `maxAnswer` controla quantas opções o usuário pode marcar: `1` (padrão) = escolha única; `> 1` = múltipla escolha. Se `maxAnswer` for omitido, inválido (`< 1`) ou maior que `len(options)`, o servidor normaliza automaticamente para `1` ou para o total de opções, respectivamente. Suporta `delay`, `replyTo` e `replyPrivate`. ## Exemplos ### Enquete simples (escolha única) Cria uma enquete com três opções (`09h`, `14h`, `16h`) e `maxAnswer` implícito em `1`, o respondente só pode marcar uma alternativa. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/poll/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "question": "Qual horário você prefere para a reunião?", "options": ["09h", "14h", "16h"] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/poll/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", question: "Qual horário você prefere para a reunião?", options: ["09h", "14h", "16h"] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/poll/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "question": "Qual horário você prefere para a reunião?", "options": ["09h", "14h", "16h"] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "question": "Qual horário você prefere para a reunião?", "options": ["09h", "14h", "16h"] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/poll/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Enquete de múltipla escolha `maxAnswer: 3` permite que o respondente marque até três opções. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/poll/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "120363406289005073@g.us", "question": "Quais linguagens você usa no dia a dia?", "options": ["Go", "Python", "JavaScript", "TypeScript", "Rust", "Java"], "maxAnswer": 3 }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/poll/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "120363406289005073@g.us", question: "Quais linguagens você usa no dia a dia?", options: ["Go", "Python", "JavaScript", "TypeScript", "Rust", "Java"], maxAnswer: 3 }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/poll/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "120363406289005073@g.us", "question": "Quais linguagens você usa no dia a dia?", "options": ["Go", "Python", "JavaScript", "TypeScript", "Rust", "Java"], "maxAnswer": 3 } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "120363406289005073@g.us", "question": "Quais linguagens você usa no dia a dia?", "options": ["Go", "Python", "JavaScript", "TypeScript", "Rust", "Java"], "maxAnswer": 3 }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/poll/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Enquete como resposta a uma mensagem Cita uma mensagem existente via `replyTo`. A mensagem original precisa pertencer à mesma instância. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/poll/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "question": "Confirma o evento na data X?", "options": ["Sim, confirmo", "Não posso", "Talvez"], "replyTo": "3EB08FCF27E532F1B0F5", "delay": 2 }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/poll/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", question: "Confirma o evento na data X?", options: ["Sim, confirmo", "Não posso", "Talvez"], replyTo: "3EB08FCF27E532F1B0F5", delay: 2 }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/poll/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "question": "Confirma o evento na data X?", "options": ["Sim, confirmo", "Não posso", "Talvez"], "replyTo": "3EB08FCF27E532F1B0F5", "delay": 2 } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "question": "Confirma o evento na data X?", "options": ["Sim, confirmo", "Não posso", "Talvez"], "replyTo": "3EB08FCF27E532F1B0F5", "delay": 2 }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/poll/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O `content` retornado pré-formata a pergunta junto das opções numeradas (`1. ... 2. ...`), é a representação textual usada para indexar a enquete no histórico. O `messageId` é o que você precisa guardar para correlacionar votos via webhook. ```json 200 OK { "success": true, "message": "Poll sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1D3D3", "direction": "sent", "messageType": "poll", "content": "Qual horário você prefere para a reunião?\n\nOpções:\n1. 14:00\n2. 15:00\n3. 16:00\n", "source": "api", "timestamp": "2026-04-30T14:30:00Z", "chat": { "jid": "5511999999999@s.whatsapp.net", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" } } } ``` Respostas dos participantes não chegam síncronamente nesta resposta, elas trafegam como eventos `poll-update` no webhook/WebSocket configurado, referenciando o `messageId` da enquete. ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Destino: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`, `@lid`, `@g.us`, `@newsletter`). Pergunta exibida no topo da enquete. Lista de opções. **Mínimo 2, máximo 12** (limite do WhatsApp). Strings duplicadas são aceitas, mas não recomendadas. Número máximo de opções que o respondente pode marcar. `1` = escolha única; `> 1` = múltipla escolha. Valores `< 1` são normalizados para `1`; valores maiores que `len(options)` são reduzidos ao tamanho da lista. Tempo em **segundos** para aguardar antes de enviar. Durante o intervalo, o servidor envia o indicador de "digitando..." ao destinatário e dispara o "paused" antes do envio real. ID da mensagem a ser citada (reply). A mensagem original precisa pertencer à mesma instância e ter sido salva no banco. Quando `true` **e** `replyTo` aponta para uma mensagem originária de um grupo, a enquete é redirecionada para o **privado** do autor original (mantendo a citação). Identificador de origem para rastreabilidade (ex.: `crm`, `bot-suporte`, `n8n`). Salvo no registro da mensagem e propagado para webhooks. ## Notas - **`delay` é em segundos** (não milissegundos). - O WhatsApp aceita **de 2 a 12 opções** por enquete. Mais que isso é truncado pelo cliente do destinatário. - `maxAnswer` é normalizado pelo servidor: `< 1` vira `1`, e qualquer valor maior que `len(options)` cai para `len(options)`. - Os votos não voltam nesta chamada, assine os eventos do webhook/WebSocket para receber `poll-update` quando alguém responder. - Em canais (`@newsletter`), enquetes podem ter comportamento limitado dependendo das permissões do canal. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid request body: ` | | 400 |, | `Number is required` | | 400 |, | `Question is required` | | 400 |, | `At least 2 options are required` | | 400 | `invalid_number` | `Invalid phone number format: ` | | 404 |, | `Instance not found` | | 500 | `send_failed` | `Failed to send poll: ` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "At least 2 options are required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Envia uma mensagem de **evento** (card de reunião/agenda) a um contato 1-a-1 ou, mais comumente, a um grupo (`@g.us`). Os campos `startAt` e `endAt` aceitam datas no formato **ISO 8601 (RFC3339)** com fuso horário (ex.: `2026-04-28T14:00:00-03:00`) e são convertidos para Unix (segundos) internamente. Opcionalmente o evento pode incluir `description`, `location`, `joinLink`, lembrete (`hasReminder` + `reminderOffsetSec`) e flags como `isScheduleCall` e `extraGuestsAllowed`. Suporta `delay`, `replyTo` e `replyPrivate`. ## Exemplos ### Reunião com local e lembrete Cria um evento em um grupo com início e fim, local e um lembrete 15 minutos antes (`reminderOffsetSec: 900`). ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/event/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "120363312345678901@g.us", "name": "Reunião do time", "description": "Planejamento do trimestre", "startAt": "2026-04-28T14:00:00-03:00", "endAt": "2026-04-28T16:00:00-03:00", "location": { "name": "Sala 3, Sede", "address": "Av. Paulista, 1000" }, "hasReminder": true, "reminderOffsetSec": 900 }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/event/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "120363312345678901@g.us", name: "Reunião do time", description: "Planejamento do trimestre", startAt: "2026-04-28T14:00:00-03:00", endAt: "2026-04-28T16:00:00-03:00", location: { name: "Sala 3, Sede", address: "Av. Paulista, 1000" }, hasReminder: true, reminderOffsetSec: 900 }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/event/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "120363312345678901@g.us", "name": "Reunião do time", "description": "Planejamento do trimestre", "startAt": "2026-04-28T14:00:00-03:00", "endAt": "2026-04-28T16:00:00-03:00", "location": {"name": "Sala 3, Sede", "address": "Av. Paulista, 1000"}, "hasReminder": True, "reminderOffsetSec": 900 } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "120363312345678901@g.us", "name": "Reunião do time", "description": "Planejamento do trimestre", "startAt": "2026-04-28T14:00:00-03:00", "endAt": "2026-04-28T16:00:00-03:00", "location": { "name": "Sala 3, Sede", "address": "Av. Paulista, 1000" }, "hasReminder": true, "reminderOffsetSec": 900 }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/event/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Chamada agendada com link `isScheduleCall: true` marca o evento como uma chamada e `joinLink` informa o link para entrar. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/event/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "120363312345678901@g.us", "name": "Daily da equipe", "startAt": "2026-05-04T09:00:00-03:00", "isScheduleCall": true, "joinLink": "https://meet.example.com/daily" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/event/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "120363312345678901@g.us", name: "Daily da equipe", startAt: "2026-05-04T09:00:00-03:00", isScheduleCall: true, joinLink: "https://meet.example.com/daily" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/event/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "120363312345678901@g.us", "name": "Daily da equipe", "startAt": "2026-05-04T09:00:00-03:00", "isScheduleCall": True, "joinLink": "https://meet.example.com/daily" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "120363312345678901@g.us", "name": "Daily da equipe", "startAt": "2026-05-04T09:00:00-03:00", "isScheduleCall": true, "joinLink": "https://meet.example.com/daily" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/event/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O `content` retornado é o nome do evento, usado para indexar a mensagem no histórico. Guarde o `messageId` para correlacionar respostas (going/not-going) recebidas via webhook. ```json 200 OK { "success": true, "message": "Event sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1D3D3", "direction": "sent", "messageType": "event", "content": "Reunião do time", "source": "api", "timestamp": "2026-04-28T14:30:00Z", "chat": { "jid": "120363312345678901@g.us", "isGroup": true }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" } } } ``` As confirmações de presença dos participantes não chegam síncronamente nesta resposta, elas trafegam como eventos no webhook/WebSocket configurado, referenciando o `messageId` do evento. ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Destino: telefone (`5511999999999`) ou JID. Eventos funcionam melhor em grupos (`@g.us`). Título do evento exibido no card. Data/hora de início no formato **ISO 8601 (RFC3339)** com fuso, ex.: `2026-04-28T14:00:00-03:00`. Data/hora de término no formato **ISO 8601 (RFC3339)**. Opcional. Descrição/detalhes do evento. Local do evento. Campos: `name`, `address`, `latitude`, `longitude` (todos opcionais). Link para entrar (usado em chamadas agendadas). Marca o evento como uma chamada agendada. Habilita um lembrete para o evento. Antecedência do lembrete, em **segundos** antes do `startAt` (ex.: `900` = 15 min). Permite que convidados tragam acompanhantes. Marca o evento como cancelado. Tempo em **segundos** para aguardar antes de enviar. Durante o intervalo, o servidor envia o indicador de "digitando..." ao destinatário e dispara o "paused" antes do envio real. ID da mensagem a ser citada (reply). A mensagem original precisa pertencer à mesma instância e ter sido salva no banco. Quando `true` **e** `replyTo` aponta para uma mensagem originária de um grupo, o evento é redirecionado para o **privado** do autor original (mantendo a citação). Identificador de origem para rastreabilidade (ex.: `crm`, `bot-suporte`, `n8n`). Salvo no registro da mensagem e propagado para webhooks. ## Notas - **`startAt`/`endAt` são ISO 8601 (RFC3339)** com fuso horário; o servidor converte para Unix (segundos). - **`delay` é em segundos** (não milissegundos); **`reminderOffsetSec` também é em segundos**. - Eventos são exibidos melhor em **grupos** (`@g.us`). - As confirmações de presença não voltam nesta chamada, assine os eventos do webhook/WebSocket para recebê-las referenciando o `messageId`. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid request body: ` | | 400 |, | `Number is required` | | 400 |, | `Name is required` | | 400 |, | `startAt is required` | | 400 | `invalid_request` | `Invalid startAt, expected ISO 8601 (RFC3339): ` | | 400 | `invalid_number` | `Invalid phone number format: ` | | 404 |, | `Instance not found` | | 500 | `send_failed` | `Failed to send event: ` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "startAt is required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Adiciona ou remove uma reação (emoji) a uma mensagem existente. O campo `reaction` recebe o emoji (`"👍"`, `"❤️"`, `"😂"`, etc.) ou a string literal `"remove"` para remover a reação. Em conversas 1-a-1, basta `messageId` + `fromMe`. Em **grupos**, quando a mensagem original **não foi enviada pela instância** (`fromMe: false`), é obrigatório informar `participant` com o JID do autor original, sem isso o WhatsApp não consegue localizar o alvo. Reações **não suportam** `delay`, `replyTo` nem `mention`. ## Exemplos ### Reagir em conversa 1-a-1 `fromMe: false` indica que a mensagem alvo foi recebida (não enviada) pela instância. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/reaction/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "messageId": "3EB08FCF27E532F1B0F5", "reaction": "👍", "fromMe": false }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/reaction/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", messageId: "3EB08FCF27E532F1B0F5", reaction: "👍", fromMe: false }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/reaction/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "messageId": "3EB08FCF27E532F1B0F5", "reaction": "👍", "fromMe": False } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "messageId": "3EB08FCF27E532F1B0F5", "reaction": "👍", "fromMe": false }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/reaction/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Reagir em grupo (mensagem de outro participante) Em grupos, quando você reage a uma mensagem que **não é sua** (`fromMe: false`), o `participant` com o JID do autor original é **obrigatório**. Sem ele o servidor responde `400 missing_participant`. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/reaction/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "120363406289005073@g.us", "messageId": "3EB08FCF27E532F1B0F5", "reaction": "❤️", "fromMe": false, "participant": "5511888888888@s.whatsapp.net" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/reaction/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "120363406289005073@g.us", messageId: "3EB08FCF27E532F1B0F5", reaction: "❤️", fromMe: false, participant: "5511888888888@s.whatsapp.net" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/reaction/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "120363406289005073@g.us", "messageId": "3EB08FCF27E532F1B0F5", "reaction": "❤️", "fromMe": False, "participant": "5511888888888@s.whatsapp.net" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "120363406289005073@g.us", "messageId": "3EB08FCF27E532F1B0F5", "reaction": "❤️", "fromMe": false, "participant": "5511888888888@s.whatsapp.net" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/reaction/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Remover reação Envie `reaction: "remove"` para apagar uma reação previamente colocada na mensagem. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/reaction/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "messageId": "3EB08FCF27E532F1B0F5", "reaction": "remove", "fromMe": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/reaction/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", messageId: "3EB08FCF27E532F1B0F5", reaction: "remove", fromMe: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/reaction/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "messageId": "3EB08FCF27E532F1B0F5", "reaction": "remove", "fromMe": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "messageId": "3EB08FCF27E532F1B0F5", "reaction": "remove", "fromMe": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/reaction/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O `messageId` retornado é o da **própria reação** (não o da mensagem reagida, esse fica em `replyTo.messageId`). O `content` traz o emoji aplicado, ou string vazia quando a reação foi removida. ```json 200 OK { "success": true, "message": "Reaction sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1C2C2", "direction": "sent", "messageType": "reaction", "content": "👍", "source": "api", "timestamp": "2026-04-30T14:30:00Z", "chat": { "jid": "5511999999999@s.whatsapp.net", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" }, "replyTo": { "messageId": "3EB08FCF27E532F1B0F5" } } } ``` Quando a reação é removida (`reaction: "remove"`), a `message` retornada vira `"Reaction removed successfully"` e o `content` fica vazio. A reação aparece no destinatário ancorada à mensagem original, se você reagir novamente com outro emoji, o WhatsApp **substitui** a reação anterior. ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Chat onde a mensagem alvo está: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`, `@lid`, `@g.us`). ID da mensagem que receberá a reação. Emoji da reação (ex.: `"👍"`, `"❤️"`, `"😂"`, `"🔥"`) **ou** a string literal `"remove"` para apagar uma reação existente. `true` quando a mensagem original foi **enviada pela própria instância**; `false` quando ela foi recebida de outro contato/participante. O WhatsApp usa esse flag junto com `participant` para localizar o alvo. JID do autor da mensagem original (ex.: `5511888888888@s.whatsapp.net`). **Obrigatório em grupos quando `fromMe: false`**, sem ele o servidor retorna `400 missing_participant`. Em conversas 1-a-1 ou quando `fromMe: true`, é ignorado. Identificador de origem para rastreabilidade (ex.: `crm`, `bot-suporte`, `n8n`). Salvo no registro da mensagem e propagado para webhooks. ## Notas - Reações **não suportam** `delay`, `replyTo`, `replyPrivate`, `mention` nem `mentionAll`, apenas os campos listados acima. - Para **alterar** uma reação existente, basta enviar uma nova com outro emoji. O WhatsApp substitui automaticamente. - Em grupos, sem `participant` correto a reação cai em `missing_participant` mesmo que o `messageId` exista no banco. - `fromMe` precisa refletir o lado real da mensagem. Se invertido, o WhatsApp pode não localizar o alvo e a reação some silenciosamente no aplicativo do destinatário. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid request body: ` | | 400 |, | `Number is required` | | 400 |, | `MessageID is required` | | 400 |, | `Reaction is required` | | 400 | `invalid_number` | `Invalid phone number format: ` | | 400 | `invalid_message_id` | (motivo do messageId inválido) | | 400 | `missing_participant` | `Participant is required for group reactions when fromMe=false` | | 404 |, | `Instance not found` | | 500 | `send_failed` | `Failed to send reaction: ` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "Reaction is required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Publica um **status** (story) de **24 horas** no perfil da instância. Suporta quatro tipos: `text` (texto puro com cor de fundo e fonte), `image`, `video` e `audio`. Diferente dos outros endpoints, **não há campo `number`**, o status é publicado em `status@broadcast` e fica visível para todos os contatos que têm permissão (configuração do app). **Menções não são suportadas** neste endpoint. ## Exemplos ### Status de texto com cor e fonte Publica um status puramente textual com cor de fundo customizada e fonte. Não usa `mediaUrl`. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/status/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "type": "text", "message": "Estamos chegando em Maio com novidades!", "backgroundColor": "#FF6600", "font": "serif" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/status/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ type: "text", message: "Estamos chegando em Maio com novidades!", backgroundColor: "#FF6600", font: "serif" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/status/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "type": "text", "message": "Estamos chegando em Maio com novidades!", "backgroundColor": "#FF6600", "font": "serif" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "type": "text", "message": "Estamos chegando em Maio com novidades!", "backgroundColor": "#FF6600", "font": "serif" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/status/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Status de imagem Publica uma imagem como status. `mediaUrl` é obrigatório para tipos não-texto. `message` aparece como legenda. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/status/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "type": "image", "message": "Lançamento da nova versão!", "mediaUrl": "https://cdn.example.com/banner-lancamento.jpg" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/status/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ type: "image", message: "Lançamento da nova versão!", mediaUrl: "https://cdn.example.com/banner-lancamento.jpg" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/status/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "type": "image", "message": "Lançamento da nova versão!", "mediaUrl": "https://cdn.example.com/banner-lancamento.jpg" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "type": "image", "message": "Lançamento da nova versão!", "mediaUrl": "https://cdn.example.com/banner-lancamento.jpg" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/status/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Status de vídeo Publica um vídeo curto como status. WhatsApp limita stories de vídeo a **30 segundos**, se a mídia for maior, é cortada. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/status/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "type": "video", "message": "Bastidores da gravação desta semana", "mediaUrl": "https://cdn.example.com/teaser.mp4", "mimeType": "video/mp4" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/status/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ type: "video", message: "Bastidores da gravação desta semana", mediaUrl: "https://cdn.example.com/teaser.mp4", mimeType: "video/mp4" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/status/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "type": "video", "message": "Bastidores da gravação desta semana", "mediaUrl": "https://cdn.example.com/teaser.mp4", "mimeType": "video/mp4" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "type": "video", "message": "Bastidores da gravação desta semana", "mediaUrl": "https://cdn.example.com/teaser.mp4", "mimeType": "video/mp4" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/status/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Status de áudio (mensagem de voz) Publica um áudio como status. Por padrão é tratado como **PTT (voz)**. Use `isVoice: false` para tratar como áudio comum. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/message/status/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "type": "audio", "message": "Recado de hoje", "mediaUrl": "https://cdn.example.com/recado.ogg", "mimeType": "audio/ogg; codecs=opus", "isVoice": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/message/status/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ type: "audio", message: "Recado de hoje", mediaUrl: "https://cdn.example.com/recado.ogg", mimeType: "audio/ogg; codecs=opus", isVoice: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/message/status/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "type": "audio", "message": "Recado de hoje", "mediaUrl": "https://cdn.example.com/recado.ogg", "mimeType": "audio/ogg; codecs=opus", "isVoice": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "type": "audio", "message": "Recado de hoje", "mediaUrl": "https://cdn.example.com/recado.ogg", "mimeType": "audio/ogg; codecs=opus", "isVoice": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/status/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O `messageType` ecoa o `type` enviado (`text`, `image`, `video` ou `audio`) e o `chat.jid` é sempre `status@broadcast`. O `messageId` retornado pode ser usado para deletar a publicação antes das 24h via endpoint de apagar mensagem. ```json 200 OK { "success": true, "message": "Status sent successfully", "status": "sent", "data": { "messageId": "3EB08FCF27E532F1B0F5", "direction": "sent", "messageType": "text", "content": "Estamos chegando em Maio com novidades!", "source": "api", "timestamp": "2026-04-30T14:30:00Z", "chat": { "jid": "status@broadcast", "isGroup": false }, "sender": { "jid": "5511777777777@s.whatsapp.net", "instance": "minha-instancia" } } } ``` ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Tipo do status. Valores aceitos: `text`, `image`, `video`, `audio`. Conteúdo textual do status. Para `type=text`, é o próprio texto exibido. Para mídia (`image`, `video`, `audio`), funciona como legenda. URL pública do arquivo de mídia. **Obrigatório** quando `type` é `image`, `video` ou `audio`. Ignorado quando `type=text`. MIME type da mídia (ex.: `image/jpeg`, `video/mp4`, `audio/ogg; codecs=opus`). Opcional, auto-detectado quando omitido. Nome do arquivo. Opcional, raramente relevante para stories. **Apenas para `type=text`.** Cor de fundo do status em hex (ex.: `#FF0000`, `#00AAFF`). Quando omitido, o WhatsApp usa a cor padrão do tema. **Apenas para `type=text`.** Fonte do texto. Valores comuns: `system`, `serif`, `sans-serif`. **Apenas para `type=audio`.** Quando `true` (padrão), o áudio é publicado como **PTT (mensagem de voz)**. Quando `false`, vira áudio comum com player normal. **Apenas para `type=audio`.** Duração em segundos. Opcional, auto-detectado pela ferramenta de transcodificação. **Apenas para `type=audio`.** Forma de onda customizada (array de bytes). Opcional, auto-gerada se omitida. Identificador de origem para rastreabilidade (ex.: `crm`, `bot-marketing`, `n8n`). ## Notas - **Não há campo `number`**, stories vão sempre para `status@broadcast` e ficam visíveis pelas regras de privacidade configuradas no app (Configurações → Privacidade → Status). - **Menções não são suportadas** neste endpoint, `mention` e `mentionAll` não existem aqui (stories não suportam menções na API). - Áudios em formatos não-Opus (mp3, m4a, wav) são **convertidos automaticamente** pelo servidor via FFmpeg para `audio/ogg; codecs=opus` antes de publicar. O processo pode aumentar o tempo de resposta da request. - Para `type=video`, o WhatsApp limita stories a ~30 segundos. Vídeos maiores podem ser cortados ou rejeitados pelo servidor do WhatsApp. - Status duram **24 horas** e são apagados automaticamente. Para deletar antes, use o endpoint de apagar mensagem com o `messageId` retornado. - `backgroundColor` e `font` só fazem efeito em `type=text`. Em status de mídia, são ignorados silenciosamente. ## Erros | HTTP | Status interno | Mensagem | |------|----------------|----------| | 400 |, | `Instance name is required` | | 400 |, | `Invalid request body: ` | | 400 |, | `Type must be one of: text, image, video, audio` | | 400 |, | `Message is required` | | 400 |, | `MediaURL is required for type: ` | | 400 | `media_download_failed` | `Failed to download media from URL` | | 400 | `media_validation_failed` | (validação do arquivo de mídia) | | 400 | `unsupported_media_type` | (formato de mídia não suportado) | | 500 | `media_upload_failed` | `Failed to upload media to WhatsApp` | | 500 | `audio_conversion_failed` | (falha na conversão do áudio para Opus) | | 404 |, | `Instance not found` | | 500 | `send_failed` | `Failed to send status: ` | | 503 | `disconnected` | `Instance is not connected to WhatsApp` | Envelope de erro: ```json { "success": false, "error": { "message": "MediaURL is required for type: image" } } ``` ### Chamadas **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não O módulo `/api/call/*` cobre o disparo de **chamadas de voz** a partir da instância. Hoje há duas operações: a **chamada fake**, que faz o telefone tocar por alguns segundos e desliga sozinha (sem áudio), e a **chamada com áudio**, que liga e reproduz um arquivo quando o destinatário atende. Todas as rotas validam a ownership da instância e aceitam `TokenAccount` ou `TokenInstance`. ## Endpoints disponíveis | Método | Path | Tipo | |--------|------|------| | POST | `/api/call/fake/:instance` | [Chamada Fake](/pt/api/calls/fake) | | POST | `/api/call/audio/:instance` | [Chamada com Áudio](/pt/api/calls/audio) | ## Estrutura comum ### Destinatário (`number`) Ambos os endpoints recebem o destino no campo `number`, que aceita: - Número simples: `"5511999999999"` (preferido). - JID privado: `"5511999999999@s.whatsapp.net"`. As chamadas são sempre 1:1 — não há chamada para grupo via API. ### Resposta padrão (200) As duas rotas retornam um envelope com o `callId` atribuído pelo WhatsApp: ```json { "success": true, "message": "Audio call placed", "callId": "3EB08FCF27E532F1D3D3", "number": "5511999999999" } ``` A chamada fake inclui também o campo `duration` (segundos que ficou tocando). O `callId` identifica a chamada no WhatsApp. A chamada fake é encerrada automaticamente pelo servidor após `duration` segundos; a chamada com áudio só reproduz o arquivo se o destinatário **atender**. ## Erros comuns | HTTP | Mensagem | |------|----------| | 400 | `Instance name is required` | | 400 | `Invalid request body: ` | | 400 | `Number is required` | | 400 | `Duration must be between 1 and 60 seconds` (fake) | | 400 | `Provide exactly one of mediaUrl or mediaBase64` (áudio) | | 404 | `instance not found` | | 500 | `` | Envelope de erro: ```json { "success": false, "error": { "message": "Number is required" } } ``` ## Próximos passos Faz o telefone tocar por alguns segundos e desliga sozinha. Liga e reproduz um áudio (URL ou base64) quando atendem. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Inicia uma chamada de voz que **toca por alguns segundos no aparelho do destinatário e desliga sozinha**, sem áudio e sem conexão de mídia. Útil para chamar atenção, validar número ou fluxos de "toque e desligue". O campo `duration` controla por quantos segundos a chamada fica tocando antes do encerramento automático: padrão **8 segundos**, faixa permitida **1 a 60**. ## Exemplo ```bash cURL curl -X POST "https://ryzeapi.cloud/api/call/fake/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "duration": 15 }' ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Call placed successfully", "callId": "3EB08FCF27E532F1D3D3", "number": "5511999999999", "duration": 8 } ``` ## Request body Destino: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`). Segundos que a chamada fica tocando antes de desligar automaticamente. Faixa permitida: **1 a 60**. Omitido ou `0` usa o padrão `8`. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `Instance name is required` | | 400 | `Invalid request body: ` | | 400 | `Number is required` | | 400 | `Duration must be between 1 and 60 seconds` | | 404 | `instance not found` | | 500 | `` | **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Inicia uma chamada de voz e, quando o destinatário **atende**, reproduz um arquivo de áudio. O áudio é convertido automaticamente (via ffmpeg) para o formato exigido pela chamada de voz do WhatsApp. Informe a mídia em **exatamente um** campo: `mediaUrl` (aceita URL pública **ou** base64/data URI) **ou** `mediaBase64` (somente base64). Enviar os dois, ou nenhum, é erro. ## Exemplo ```bash cURL curl -X POST "https://ryzeapi.cloud/api/call/audio/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "mediaUrl": "https://exemplo.com/mensagem.mp3" }' ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Audio call placed", "callId": "3EB08FCF27E532F1D3D3", "number": "5511999999999" } ``` ## Request body Destino: telefone (`5511999999999`) ou JID (`@s.whatsapp.net`). Fonte do áudio: aceita uma **URL pública** (`http(s)://…`) **ou** o conteúdo do arquivo em **base64** — base64 puro ou data URI (`data:audio/mpeg;base64,…`). O servidor detecta automaticamente qual dos dois você enviou. Informe **isto ou** `mediaBase64`, nunca os dois. Conteúdo do arquivo de áudio em base64. Informe **isto ou** `mediaUrl`, nunca os dois. ## Notas - Informe **exatamente uma** fonte de mídia: `mediaUrl` **ou** `mediaBase64`. Enviar ambas (ou nenhuma) retorna `400`. - O servidor usa ffmpeg para converter o áudio para o codec exigido pela chamada de voz do WhatsApp; formatos comuns (mp3, ogg, wav, m4a) são aceitos. - O áudio só é reproduzido se o destinatário **atender** a chamada. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `Instance name is required` | | 400 | `Invalid request body: ` | | 400 | `Number is required` | | 400 | `Provide exactly one of mediaUrl or mediaBase64` | | 404 | `instance not found` | | 500 | `` | ### Grupos **Auth:** `TokenAccount` ou `TokenInstance` em todas as rotas. Cada chamada valida a ownership da instância. Esta seção cobre todas as rotas `/api/group/*`, criação, listagem, detalhes, gerenciamento de participantes, atualização de metadados, reset de link de convite, entrada e saída. Para comunidades (grupos pais), veja [Comunidades](/pt/api/communities/overview). ## Endpoints | Método | Path | Função | |--------|------|--------| | POST | `/api/group/create/:instance` | [Criar grupo](/pt/api/groups/create) | | GET | `/api/group/list/:instance` | [Listar grupos](/pt/api/groups/list) | | GET | `/api/group/info/:instance` | [Detalhes de um grupo](/pt/api/groups/info) | | PUT | `/api/group/update/:instance` | [Atualizar nome / descrição / foto / permissões](/pt/api/groups/update) | | POST | `/api/group/participants/:instance` | [Add / remove / promote / demote / approve / reject](/pt/api/groups/participants) | | POST | `/api/group/join/:instance` | [Entrar via código / link](/pt/api/groups/join) | | POST | `/api/group/resetLink/:instance` | [Revogar e gerar novo invite link](/pt/api/groups/reset-link) | | GET | `/api/group/requests/:instance` | [Listar solicitações pendentes](/pt/api/groups/requests) | | DELETE | `/api/group/leave/:instance` | [Sair do grupo](/pt/api/groups/leave) | ## Identifiers aceitos A maioria dos endpoints aceita um campo `identifier` que pode ser: | Forma | Exemplo | |-------|---------| | JID do grupo | `120363406289005073@g.us` | | Código de convite | `ABC123XYZ` | | Link completo | `https://chat.whatsapp.com/ABC123XYZ` | **Exceções:** - `POST /api/group/join` aceita **apenas** código ou link (não aceita JID). - `GET /api/community/listSubGroups` exige JID em query `?communityJid=`. ## Estruturas de dados ### `GroupInfo` Resposta padrão das rotas de criação e atualização. ```json { "name": "Time de Dev", "jid": "120363406289005073@g.us", "description": "Discussões técnicas", "inviteCode": "ABC123XYZ", "inviteLink": "https://chat.whatsapp.com/ABC123XYZ", "createdBy": "5511999999999@s.whatsapp.net", "participantCount": 3, "participants": [ { "jid": "5511999999999@s.whatsapp.net", "isAdmin": true, "isSuperAdmin": false } ], "groupSettings": { "membersCanEditInfo": true, "membersCanSendMessages": true, "membersCanAddOthers": false, "requireAdminApproval": false }, "isCommunity": false, "isParent": false, "linkedParentJid": null } ``` ### `GroupDetail` Resposta de [`GET /info`](/pt/api/groups/info). Inclui campos extras além do `GroupInfo`: `image`, `createdAt`, `metadata` (autor da última alteração de nome / descrição), `isEphemeral`, `isIncognito`, `isSuspended`, `isDefaultSubGroup`. ### `GroupPermissions` | Campo | Tipo | Significado WhatsApp | |-------|------|----------------------| | `membersCanEditInfo` | bool | Inverso de `IsLocked` | | `membersCanSendMessages` | bool | Inverso de `IsAnnounce` | | `membersCanAddOthers` | bool | `MemberAddMode == AllMember` | | `requireAdminApproval` | bool | `IsJoinApprovalRequired` | ## Envelope de erro Todas as rotas usam o envelope padrão da API: ```json { "success": false, "error": { "message": "Identifier is required" } } ``` ## Quadro de erros (resumo) | Categoria | Mensagem | |-----------|----------| | Auth | `Not authorized to view group requests (must be admin)` | | Auth | `Not authorized to perform this action (must be admin)` | | Auth | `Not authorized to update this group (must be admin)` | | Auth | `Not authorized to reset group invite link (must be admin)` | | Auth | `Not allowed to join this group` | | Auth | `Not allowed to leave this group` | | Validação | `Identifier is required` | | Validação | `At least one participant is required` | | Validação | `Invalid action. Must be one of: add, remove, promote, demote, approve, reject` | | Identifier | `failed to resolve group from identifier (not a valid JID, code, or link)` | | Identifier | `invalid group JID : ` | | Identifier | `not a group JID` | | Estado | `Instance is not connected to WhatsApp` | | Estado | `Group not found or you are not a member of this group` | | Convite | `Invite link has been revoked or expired` | | Convite | `Invalid invite link or code` | | Throttle | `rate limit exceeded (429): wait before creating again` | ## Próximo Cria um grupo novo com participantes iniciais. Retorna todos os grupos da instância. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Cria um grupo de WhatsApp e o vincula opcionalmente a uma comunidade existente (`communityJid`). Você pode definir nome, descrição, foto e permissões iniciais (`groupSettings`) na mesma chamada. A foto e a descrição são aplicadas em chamadas subsequentes após a criação do grupo. ## Exemplos ### Mínimo Cria um grupo "Time de Dev" com 2 participantes iniciais (`5511999999999`, `5521988888888`), sem descrição, foto, comunidade nem permissões customizadas. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/group/create/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "name": "Time de Dev", "participants": ["5511999999999", "5521988888888"] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/create/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ name: "Time de Dev", participants: ["5511999999999", "5521988888888"] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/group/create/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "name": "Time de Dev", "participants": ["5511999999999", "5521988888888"] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "Time de Dev", "participants": ["5511999999999", "5521988888888"] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/group/create/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com descrição e foto Cria o grupo já com a descrição "Discussões técnicas" e foto vinda de uma URL pública. Descrição e imagem são aplicadas em chamadas subsequentes após a criação do grupo. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/group/create/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "name": "Time de Dev", "description": "Discussões técnicas", "image": "https://exemplo.com/logo.png", "participants": ["5511999999999", "5521988888888"] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/create/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ name: "Time de Dev", description: "Discussões técnicas", image: "https://exemplo.com/logo.png", participants: ["5511999999999", "5521988888888"] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/group/create/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "name": "Time de Dev", "description": "Discussões técnicas", "image": "https://exemplo.com/logo.png", "participants": ["5511999999999", "5521988888888"] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "Time de Dev", "description": "Discussões técnicas", "image": "https://exemplo.com/logo.png", "participants": ["5511999999999", "5521988888888"] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/group/create/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com permissões Cria um grupo "Anúncios" com restrições iniciais: somente admins podem enviar mensagens (`membersCanSendMessages: false`) e novas entradas precisam de aprovação (`requireAdminApproval: true`). ```bash cURL curl -X POST "https://ryzeapi.cloud/api/group/create/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "name": "Anúncios", "participants": ["5511999999999"], "groupSettings": { "membersCanSendMessages": false, "requireAdminApproval": true } }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/create/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ name: "Anúncios", participants: ["5511999999999"], groupSettings: { membersCanSendMessages: false, requireAdminApproval: true } }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/group/create/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "name": "Anúncios", "participants": ["5511999999999"], "groupSettings": { "membersCanSendMessages": False, "requireAdminApproval": True } } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "Anúncios", "participants": ["5511999999999"], "groupSettings": { "membersCanSendMessages": false, "requireAdminApproval": true } }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/group/create/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Vinculado a comunidade Cria o grupo "Subgrupo Geral" já vinculado à comunidade `120363406289005073@g.us` via `communityJid`, evitando o passo extra de chamar `/community/link` após a criação. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/group/create/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "name": "Subgrupo Geral", "participants": ["5511999999999"], "communityJid": "120363406289005073@g.us" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/create/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ name: "Subgrupo Geral", participants: ["5511999999999"], communityJid: "120363406289005073@g.us" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/group/create/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "name": "Subgrupo Geral", "participants": ["5511999999999"], "communityJid": "120363406289005073@g.us" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "Subgrupo Geral", "participants": ["5511999999999"], "communityJid": "120363406289005073@g.us" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/group/create/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta inclui o `group.jid` recém-criado (use-o como `identifier` nas chamadas subsequentes), o `inviteCode` e `inviteLink` prontos para compartilhar e a lista de `participants` já marcada com `isAdmin`/`isSuperAdmin`. O criador entra automaticamente como super-admin. Os campos `groupSettings` refletem as permissões iniciais (defaults ou o que foi enviado). ```json 200 OK { "success": true, "message": "Group created successfully", "group": { "name": "Time de Dev", "jid": "120363406289005073@g.us", "description": "Discussões técnicas", "inviteCode": "ABC123XYZ", "inviteLink": "https://chat.whatsapp.com/ABC123XYZ", "createdBy": "5511999999999@s.whatsapp.net", "participantCount": 3, "participants": [ { "jid": "5511999999999@s.whatsapp.net", "isAdmin": true, "isSuperAdmin": false } ], "groupSettings": { "membersCanEditInfo": true, "membersCanSendMessages": true, "membersCanAddOthers": false, "requireAdminApproval": false }, "isCommunity": false, "isParent": false, "linkedParentJid": null } } ``` ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Nome do grupo. Máximo de **25 caracteres**. Lista de números (`5511999999999`) ou JIDs (`5511999999999@s.whatsapp.net`). Pelo menos **1** item. Descrição (tópico) do grupo. Aplicada via `SetGroupTopic` após a criação. URL pública ou data URI base64. A imagem é convertida para JPEG. Cria o grupo já vinculado a uma comunidade (parent group). Permissões iniciais. Subcampos: `membersCanEditInfo`, `membersCanSendMessages`, `membersCanAddOthers`, `requireAdminApproval`. ## Notas - O criador do grupo entra automaticamente como **super-admin**. - Números inválidos (não registrados no WhatsApp) são rejeitados; o erro indica qual participante falhou. - Se `image` for passada e o upload falhar, a criação do grupo segue normalmente, o erro de imagem não é fatal nesta rota. Use [`PUT /update`](/pt/api/groups/update) para reaplicar a foto. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `The 'name' field is required` | | 400 | `At least one participant is required` | | 400 | `group name must be 25 characters or less` | | 400 | `invalid participant : ` | | 400 | `Instance is not connected to WhatsApp` | | 429 | `rate limit exceeded (429): wait before creating again` | Envelope: ```json { "success": false, "error": { "message": "The 'name' field is required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Lista todos os grupos da instância. Por padrão, retorna apenas metadados leves (nome, JID, descrição, criador, contagem). Passe `includeMembers=true` na query para incluir a lista completa de membros, neste caso o timeout da operação é estendido para **60s**. ## Exemplos ### Listar (sem membros) Retorna todos os grupos da instância apenas com metadados leves (nome, JID, descrição, criador, contagem). É o modo padrão, mais rápido em contas com muitos grupos. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/group/list/$Instance_Name" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/list/${process.env.Instance_Name}`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/group/list/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/group/list/"+os.Getenv("Instance_Name"), nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Listar com membros Adiciona `includeMembers=true` para incluir a lista completa de participantes de cada grupo. O timeout interno é estendido para 60s, útil quando você precisa de uma snapshot completa em uma única chamada. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/group/list/$Instance_Name?includeMembers=true" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/list/${process.env.Instance_Name}?includeMembers=true`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/group/list/{os.environ['Instance_Name']}?includeMembers=true", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/group/list/"+os.Getenv("Instance_Name")+"?includeMembers=true", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Retorna todos os grupos da instância em `groups[]`, com `meta.total` indicando a quantidade. Cada item carrega metadados leves (`name`, `groupJid`, `description`, `creatorJid`, `memberCount`); o array `members` só vem populado quando `includeMembers=true`. Não há paginação, todos os grupos vêm em uma única resposta. ```json 200 OK { "success": true, "message": "2 Groups found", "groups": [ { "name": "Time de Dev", "groupJid": "120363406289005073@g.us", "description": "Discussões técnicas", "creatorJid": "5511999999999@s.whatsapp.net", "memberCount": 3, "members": [ { "jid": "5511999999999@s.whatsapp.net", "isAdmin": true, "isSuperAdmin": false } ] } ], "meta": { "total": 2 } } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Query Quando `true`, inclui a lista completa de membros de cada grupo. ## Notas - O array `members` só aparece quando `includeMembers=true`. - Para uma conta com muitos grupos, use `includeMembers=false` e busque os membros sob demanda via [`GET /info`](/pt/api/groups/info). - Não há paginação, todos os grupos vêm em uma única resposta. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `Instance is not connected to WhatsApp` | | 404 | `Instance not found` | Envelope: ```json { "success": false, "error": { "message": "Instance is not connected to WhatsApp" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna o `GroupDetail` completo: nome, descrição, foto, link de convite, criador, lista de participantes (com `lid`), permissões e metadata (quem alterou nome / descrição). Aceita `identifier` em qualquer formato, JID, código de convite ou link. ## Exemplos ### Por JID Busca os detalhes do grupo passando o JID `120363406289005073@g.us` em `identifier`. Por default, a resposta inclui a lista completa de participantes. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/group/info/$Instance_Name?identifier=120363406289005073@g.us" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/info/${process.env.Instance_Name}?identifier=120363406289005073@g.us`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/group/info/{os.environ['Instance_Name']}?identifier=120363406289005073@g.us", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/group/info/"+os.Getenv("Instance_Name")+"?identifier=120363406289005073@g.us", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Por link Resolve o grupo a partir do link de convite `https://chat.whatsapp.com/ABC123XYZ`. O serviço extrai o código, descobre o JID e retorna os mesmos dados que a busca por JID. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/group/info/$Instance_Name?identifier=https://chat.whatsapp.com/ABC123XYZ" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/info/${process.env.Instance_Name}?identifier=https://chat.whatsapp.com/ABC123XYZ`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/group/info/{os.environ['Instance_Name']}?identifier=https://chat.whatsapp.com/ABC123XYZ", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/group/info/"+os.Getenv("Instance_Name")+"?identifier=https://chat.whatsapp.com/ABC123XYZ", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Sem participantes Adiciona `participants=false` na query para receber apenas os metadados do grupo, sem a lista de membros. Útil quando você só precisa do nome / descrição / permissões em grupos grandes. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/group/info/$Instance_Name?identifier=ABC123XYZ&participants=false" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/info/${process.env.Instance_Name}?identifier=ABC123XYZ&participants=false`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/group/info/{os.environ['Instance_Name']}?identifier=ABC123XYZ&participants=false", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/group/info/"+os.Getenv("Instance_Name")+"?identifier=ABC123XYZ&participants=false", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Retorna o `GroupDetail` completo: metadados (`name`, `description`, `image`, `inviteLink`, `createdBy`, `createdAt`), lista de `participants` (presente apenas quando `participants=true`, com `lid` para correlação cross-device), `groupSettings` resolvidos e `metadata` com autoria das últimas alterações de nome/descrição. A URL em `image` é assinada pelo WhatsApp e expira em ~1h. ```json 200 OK { "success": true, "message": "Group information retrieved successfully", "group": { "jid": "120363406289005073@g.us", "name": "Time de Dev", "description": "Discussões técnicas", "image": "https://pps.whatsapp.net/...", "inviteCode": "ABC123XYZ", "inviteLink": "https://chat.whatsapp.com/ABC123XYZ", "createdBy": "5511999999999@s.whatsapp.net", "createdAt": "2026-01-15T10:30:00Z", "participantCount": 3, "participants": [ { "jid": "5511999999999@s.whatsapp.net", "lid": "199789077627112@lid", "isAdmin": true, "isSuperAdmin": false, "joinedAt": null } ], "groupSettings": { "membersCanEditInfo": true, "membersCanSendMessages": true, "membersCanAddOthers": false, "requireAdminApproval": false }, "metadata": { "nameSetAt": "2026-01-15T10:30:00Z", "nameSetBy": "5511999999999@s.whatsapp.net", "descriptionSetAt": "2026-02-20T14:00:00Z", "descriptionSetBy": "5521988888888@s.whatsapp.net" }, "isCommunity": false, "isParent": false, "isDefaultSubGroup": false, "isEphemeral": false, "isIncognito": false, "isSuspended": false } } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Query JID `@g.us`, código de convite (`ABC123XYZ`) ou link completo (`https://chat.whatsapp.com/ABC123XYZ`). Quando `false`, omite a lista de participantes (resposta mais leve). ## Notas - Quando o identifier é código / link, o serviço resolve para JID antes de buscar o detalhe, você precisa estar no grupo (ou fazer join antes) para ver os dados internos. - O campo `lid` corresponde ao identificador "lite" do whatsmeow para o usuário; útil para correlacionar mensagens enviadas a partir de outros aparelhos. - A URL em `image` é temporária (assinada pelo WhatsApp; vale ~1h). ## Erros | HTTP | Mensagem | |------|----------| | 400 | `The 'identifier' query parameter is required (can be group JID, invite code, or invite link)` | | 400 | `failed to resolve group from identifier (not a valid JID, code, or link)` | | 404 | `Group not found or you are not a member of this group` | Envelope: ```json { "success": false, "error": { "message": "Group not found or you are not a member of this group" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** parcial (mesmos valores não geram efeito) ## Descrição Atualiza qualquer subconjunto de campos do grupo: `name`, `description`, `image`, `groupSettings`. **Pelo menos um campo** além de `identifier` precisa ser enviado. A `message` da resposta lista os campos efetivamente atualizados. ## Exemplos ### Atualizar nome Renomeia o grupo `120363406289005073@g.us` para "Time Dev Updated", deixando descrição, foto e permissões inalteradas. ```bash cURL curl -X PUT "https://ryzeapi.cloud/api/group/update/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "identifier": "120363406289005073@g.us", "name": "Time Dev Updated" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/update/${process.env.Instance_Name}`, { method: "PUT", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ identifier: "120363406289005073@g.us", name: "Time Dev Updated" }) }); ``` ```python Python import os, requests requests.put( f"https://ryzeapi.cloud/api/group/update/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "identifier": "120363406289005073@g.us", "name": "Time Dev Updated" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "identifier": "120363406289005073@g.us", "name": "Time Dev Updated" }`) req, _ := http.NewRequest("PUT", "https://ryzeapi.cloud/api/group/update/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Atualizar vários campos Atualiza nome, descrição, foto e permissões de uma só vez: somente admins podem enviar mensagens (`membersCanSendMessages: false`) e novas entradas precisam de aprovação (`requireAdminApproval: true`). ```bash cURL curl -X PUT "https://ryzeapi.cloud/api/group/update/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "identifier": "120363406289005073@g.us", "name": "Time Dev", "description": "Nova descrição", "image": "https://exemplo.com/logo.png", "groupSettings": { "membersCanSendMessages": false, "requireAdminApproval": true } }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/update/${process.env.Instance_Name}`, { method: "PUT", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ identifier: "120363406289005073@g.us", name: "Time Dev", description: "Nova descrição", image: "https://exemplo.com/logo.png", groupSettings: { membersCanSendMessages: false, requireAdminApproval: true } }) }); ``` ```python Python import os, requests requests.put( f"https://ryzeapi.cloud/api/group/update/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "identifier": "120363406289005073@g.us", "name": "Time Dev", "description": "Nova descrição", "image": "https://exemplo.com/logo.png", "groupSettings": { "membersCanSendMessages": False, "requireAdminApproval": True } } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "identifier": "120363406289005073@g.us", "name": "Time Dev", "description": "Nova descrição", "image": "https://exemplo.com/logo.png", "groupSettings": { "membersCanSendMessages": false, "requireAdminApproval": true } }`) req, _ := http.NewRequest("PUT", "https://ryzeapi.cloud/api/group/update/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Remover foto Apaga a foto do grupo enviando `removeImage: true`. Esse flag tem precedência sobre `image`, útil para zerar a imagem sem precisar fornecer outra. ```bash cURL curl -X PUT "https://ryzeapi.cloud/api/group/update/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "identifier": "120363406289005073@g.us", "removeImage": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/update/${process.env.Instance_Name}`, { method: "PUT", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ identifier: "120363406289005073@g.us", removeImage: true }) }); ``` ```python Python import os, requests requests.put( f"https://ryzeapi.cloud/api/group/update/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "identifier": "120363406289005073@g.us", "removeImage": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "identifier": "120363406289005073@g.us", "removeImage": true }`) req, _ := http.NewRequest("PUT", "https://ryzeapi.cloud/api/group/update/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Limpar descrição Zera a descrição (tópico) do grupo enviando `description: ""`. String vazia é tratada explicitamente como remover, diferente de omitir o campo, que mantém a descrição atual. ```bash cURL curl -X PUT "https://ryzeapi.cloud/api/group/update/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "identifier": "120363406289005073@g.us", "description": "" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/update/${process.env.Instance_Name}`, { method: "PUT", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ identifier: "120363406289005073@g.us", description: "" }) }); ``` ```python Python import os, requests requests.put( f"https://ryzeapi.cloud/api/group/update/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "identifier": "120363406289005073@g.us", "description": "" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "identifier": "120363406289005073@g.us", "description": "" }`) req, _ := http.NewRequest("PUT", "https://ryzeapi.cloud/api/group/update/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A `message` lista somente os campos **efetivamente** alterados (útil para confirmar quando um valor enviado já era o atual). O objeto `group` traz o estado pós-update completo, incluindo `groupSettings` resultantes e o `inviteLink` corrente. ```json 200 OK { "success": true, "message": "Group name, description, announce setting updated successfully", "group": { "name": "Time Dev", "jid": "120363406289005073@g.us", "description": "Nova descrição", "inviteCode": "ABC123XYZ", "inviteLink": "https://chat.whatsapp.com/ABC123XYZ", "createdBy": "5511999999999@s.whatsapp.net", "participantCount": 3, "groupSettings": { "membersCanEditInfo": true, "membersCanSendMessages": false, "membersCanAddOthers": false, "requireAdminApproval": true }, "isCommunity": false, "isParent": false, "linkedParentJid": null } } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body JID, código ou link do grupo. Novo nome. Máximo de **25 caracteres**. Nova descrição (tópico). String vazia (`""`) **remove** a descrição. URL ou base64. A imagem é convertida para JPEG antes do upload. Quando `true`, **remove** a foto atual. Tem **precedência** sobre `image`. Atualização parcial das permissões. Subcampos: `membersCanEditInfo`, `membersCanSendMessages`, `membersCanAddOthers`, `requireAdminApproval`. ## Notas - `removeImage: true` ignora qualquer valor enviado em `image`. - A `message` da resposta lista somente os campos **efetivamente** alterados, útil para confirmar quando um valor enviado já era o atual. - A ordem interna de execução é: name -> description -> photo/removeImage -> settings. Se algum passo falha, os anteriores **já foram aplicados** e não são revertidos. - `description: ""` zera a descrição, não é equivalente a omitir o campo. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `At least one field must be provided to update` | | 400 | `group name must be 25 characters or less` | | 400 | `Identifier is required` | | 403 | `Not authorized to update this group (must be admin)` | | 404 | `Group not found or you are not a member of this group` | Envelope: ```json { "success": false, "error": { "message": "Not authorized to update this group (must be admin)" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Endpoint único para 6 ações sobre participantes, define a operação no campo `action`. Cada participante recebe um resultado individual (`success: bool`), então **operações parciais são possíveis**: a chamada pode retornar `200` com alguns membros falhando. ## Exemplos ### Adicionar ao Grupo Adiciona 2 números ao grupo `120363406289005073@g.us` em uma única chamada (`action: "add"`). Falhas por privacidade ou número sem WhatsApp ficam isoladas em `success: false` no array `participants`. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/group/participants/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "action": "add", "identifier": "120363406289005073@g.us", "participants": ["5511999999999", "5521988888888"] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/participants/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ action: "add", identifier: "120363406289005073@g.us", participants: ["5511999999999", "5521988888888"] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/group/participants/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "action": "add", "identifier": "120363406289005073@g.us", "participants": ["5511999999999", "5521988888888"] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "action": "add", "identifier": "120363406289005073@g.us", "participants": ["5511999999999", "5521988888888"] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/group/participants/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Promover a Admin Promove o número `5511999999999` a admin via `action: "promote"`. Requer que você seja super-admin do grupo (criador ou promovido por outro super-admin). ```bash cURL curl -X POST "https://ryzeapi.cloud/api/group/participants/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "action": "promote", "identifier": "120363406289005073@g.us", "participants": ["5511999999999"] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/participants/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ action: "promote", identifier: "120363406289005073@g.us", participants: ["5511999999999"] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/group/participants/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "action": "promote", "identifier": "120363406289005073@g.us", "participants": ["5511999999999"] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "action": "promote", "identifier": "120363406289005073@g.us", "participants": ["5511999999999"] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/group/participants/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Rebaixar a Membro Remove o cargo de admin do número `5511999999999` via `action: "demote"`, devolvendo-o ao status de membro comum. Também exige privilégio de super-admin. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/group/participants/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "action": "demote", "identifier": "120363406289005073@g.us", "participants": ["5511999999999"] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/participants/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ action: "demote", identifier: "120363406289005073@g.us", participants: ["5511999999999"] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/group/participants/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "action": "demote", "identifier": "120363406289005073@g.us", "participants": ["5511999999999"] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "action": "demote", "identifier": "120363406289005073@g.us", "participants": ["5511999999999"] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/group/participants/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Aprovar Entrada no Grupo (request pendente) Aprova um pedido de entrada pendente passando o LID `199789077627112@lid` em `participants` e `action: "approve"`. Funciona apenas em grupos com `requireAdminApproval=true`. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/group/participants/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "action": "approve", "identifier": "120363406289005073@g.us", "participants": ["199789077627112@lid"] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/participants/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ action: "approve", identifier: "120363406289005073@g.us", participants: ["199789077627112@lid"] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/group/participants/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "action": "approve", "identifier": "120363406289005073@g.us", "participants": ["199789077627112@lid"] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "action": "approve", "identifier": "120363406289005073@g.us", "participants": ["199789077627112@lid"] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/group/participants/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Rejeitar Entrada no Grupo (request pendente) Rejeita um pedido pendente do número `5511999999999` via `action: "reject"`. O serviço resolve automaticamente o LID equivalente quando você passa apenas o telefone. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/group/participants/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "action": "reject", "identifier": "120363406289005073@g.us", "participants": ["5511999999999"] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/participants/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ action: "reject", identifier: "120363406289005073@g.us", participants: ["5511999999999"] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/group/participants/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "action": "reject", "identifier": "120363406289005073@g.us", "participants": ["5511999999999"] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "action": "reject", "identifier": "120363406289005073@g.us", "participants": ["5511999999999"] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/group/participants/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Remover do Grupo Remove o número `5511999999999` do grupo via `action: "remove"`. O ex-membro pode reentrar pelo link de convite, a menos que você gere um novo link com [`/reset-link`](/pt/api/groups/reset-link). ```bash cURL curl -X POST "https://ryzeapi.cloud/api/group/participants/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "action": "remove", "identifier": "120363406289005073@g.us", "participants": ["5511999999999"] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/participants/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ action: "remove", identifier: "120363406289005073@g.us", participants: ["5511999999999"] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/group/participants/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "action": "remove", "identifier": "120363406289005073@g.us", "participants": ["5511999999999"] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "action": "remove", "identifier": "120363406289005073@g.us", "participants": ["5511999999999"] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/group/participants/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Cada participante recebe um resultado individual com `success: true|false` no array `participants`. **Operações parciais são possíveis**, a chamada pode retornar `200` mesmo com alguns membros falhando (número sem WhatsApp, privacidade impede add, já é membro, etc.). Sempre inspecione cada entrada para detectar erros isolados; o campo `error` traz o motivo quando `success=false`. ```json 200 OK (parcial) { "success": true, "message": "Successfully added 2 participant(s)", "groupJid": "120363406289005073@g.us", "action": "add", "participants": [ { "jid": "5511999999999@s.whatsapp.net", "isAdmin": false, "isSuperAdmin": false, "success": true }, { "jid": "5521988888888@s.whatsapp.net", "isAdmin": false, "isSuperAdmin": false, "success": false, "error": "user not on whatsapp" } ] } ``` Falhas individuais (`success: false`) **não** abortam o processamento dos demais participantes. Sempre inspecione cada entrada para detectar erros parciais (ex.: número sem WhatsApp, já membro, privacidade impede add). ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Uma de: `add`, `remove`, `promote`, `demote`, `approve`, `reject`. JID, código de convite ou link do grupo. Números ou JIDs alvo da operação. Pelo menos **1** item. ### Tabela de ações | `action` | Permissão | Timeout | Uso | |----------|-----------|---------|-----| | `add` | Admin | 60s | Adicionar números / JIDs | | `remove` | Admin | 60s | Remover membros | | `promote` | Super-admin | 60s | Tornar admin | | `demote` | Super-admin | 60s | Remover admin | | `approve` | Admin | 90s | Aprovar request pendente | | `reject` | Admin | 90s | Rejeitar request pendente | ## Notas - `promote` / `demote` exigem que você seja **super-admin** (criador do grupo ou promovido por um super-admin). - `approve` / `reject` só funcionam para grupos com `requireAdminApproval=true` e tem timeout maior (90s) por dependerem de respostas do WhatsApp. - Em `approve`/`reject`, prefira passar o **LID** retornado por [`/requests`](/pt/api/groups/requests), caso só tenha o telefone, o serviço tenta resolver o LID equivalente automaticamente. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `Invalid action. Must be one of: add, remove, promote, demote, approve, reject` | | 400 | `At least one participant is required` | | 400 | `Identifier is required` | | 403 | `Not authorized to perform this action (must be admin)` | | 404 | `Group not found or you are not a member of this group` | Envelope: ```json { "success": false, "error": { "message": "Invalid action. Must be one of: add, remove, promote, demote, approve, reject" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** parcial (chamadas repetidas em grupo aberto retornam o mesmo `groupJid`) ## Descrição Entra em um grupo a partir de um **código** (`ABC123XYZ`) ou **link** completo (`https://chat.whatsapp.com/ABC123XYZ`). Se o grupo tiver `requireAdminApproval=true`, a entrada fica pendente e retorna `requiresApproval: true`. Diferente das outras rotas, `/join` **não aceita JID** como identifier, apenas código ou link. ## Exemplos ### Por link Entra no grupo passando o link completo (`https://chat.whatsapp.com/ABC123XYZ`) em `identifier`. O serviço extrai o código automaticamente e tenta o ingresso. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/group/join/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "identifier": "https://chat.whatsapp.com/ABC123XYZ" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/join/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ identifier: "https://chat.whatsapp.com/ABC123XYZ" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/group/join/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "identifier": "https://chat.whatsapp.com/ABC123XYZ" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "identifier": "https://chat.whatsapp.com/ABC123XYZ" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/group/join/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Por código Entra no grupo passando apenas o código bruto (`ABC123XYZ`) em `identifier`, útil quando você já extraiu o código do link em outro fluxo. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/group/join/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "identifier": "ABC123XYZ" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/join/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ identifier: "ABC123XYZ" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/group/join/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "identifier": "ABC123XYZ" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "identifier": "ABC123XYZ" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/group/join/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O `groupJid` identifica o grupo que a instância passou a integrar (ou tentou integrar). Quando `requiresApproval: true`, a entrada **ainda não** foi efetivada, está aguardando aprovação manual de um admin, confira a fila pendente em [`/requests`](/pt/api/groups/requests). Quando `false`, a instância já é membro e pode enviar/receber mensagens. ```json 200 OK { "success": true, "message": "Successfully joined the group", "groupJid": "120363406289005073@g.us", "requiresApproval": false } ``` ```json 200 OK (com aprovação manual) { "success": true, "message": "Join request sent successfully. Waiting for admin approval.", "groupJid": "120363406289005073@g.us", "requiresApproval": true } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Código de convite (`ABC123XYZ`) ou link completo (`https://chat.whatsapp.com/ABC123XYZ`). ## Notas - `requiresApproval: true` significa que você **ainda não** entrou, sua entrada está na fila listada por [`/requests`](/pt/api/groups/requests) até que um admin aprove. - Quando o link foi revogado por um admin (via [`/resetLink`](/pt/api/groups/reset-link)), o código antigo deixa de funcionar imediatamente. - Para pré-visualizar o grupo sem entrar, use [`GET /info`](/pt/api/groups/info) com o link como `identifier`. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `Invalid invite link or code` | | 400 | `Invite link has been revoked or expired` | | 403 | `Not allowed to join this group` | | 404 | `Group not found` | Envelope: ```json { "success": false, "error": { "message": "Invite link has been revoked or expired" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não (cada chamada gera um código diferente) ## Descrição Revoga o link de convite atual do grupo e gera um novo. **Apenas admins** podem chamar. Use sempre que suspeitar que o link vazou ou ao remover um membro que você não quer que volte. ## Exemplos ### Resetar link Revoga o link de convite atual do grupo `120363406289005073@g.us` e gera um novo código. O link antigo para de funcionar imediatamente. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/group/resetLink/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "identifier": "120363406289005073@g.us" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/resetLink/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ identifier: "120363406289005073@g.us" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/group/resetLink/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "identifier": "120363406289005073@g.us" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "identifier": "120363406289005073@g.us" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/group/resetLink/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Retorna o novo `inviteCode` e o `inviteLink` recém-gerados, ambos já ativos. O link antigo deixa de funcionar imediatamente, substitua qualquer cópia em fluxos de onboarding, materiais de divulgação ou QR codes impressos. ```json 200 OK { "success": true, "message": "Group invite link reset successfully", "groupJid": "120363406289005073@g.us", "inviteCode": "NEW123XYZ", "inviteLink": "https://chat.whatsapp.com/NEW123XYZ" } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body JID, código de convite ou link do grupo. ## Notas - O link antigo deixa de funcionar imediatamente, quem ainda não entrou recebe `Invite link has been revoked or expired`. - Para apenas **consultar** o link atual sem revogá-lo, use [`GET /info`](/pt/api/groups/info) e leia `group.inviteLink`. - Para casos de comprometimento mais grave (admin comprometido), também revogue admin de quem não deveria ter através de [`/participants`](/pt/api/groups/participants) com `action=demote`. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `Identifier is required` | | 403 | `Not authorized to reset group invite link (must be admin)` | | 404 | `Group not found or you are not a member of this group` | Envelope: ```json { "success": false, "error": { "message": "Not authorized to reset group invite link (must be admin)" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Lista as solicitações pendentes de ingresso em um grupo que tem `requireAdminApproval=true`. Apenas **admins** do grupo podem visualizar essa fila. Para aceitar ou rejeitar, use [`POST /participants`](/pt/api/groups/participants) com `action=approve` ou `action=reject`. ## Exemplos ### Listar pedidos Retorna a fila de solicitações pendentes do grupo `120363406289005073@g.us`. Cada entrada traz o LID e (quando disponível) o telefone de quem pediu para entrar. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/group/requests/$Instance_Name?identifier=120363406289005073@g.us" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/requests/${process.env.Instance_Name}?identifier=120363406289005073@g.us`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/group/requests/{os.environ['Instance_Name']}?identifier=120363406289005073@g.us", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/group/requests/"+os.Getenv("Instance_Name")+"?identifier=120363406289005073@g.us", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Retorna a fila de solicitações pendentes em `requests[]`, com `meta.total` indicando o tamanho. Cada entrada traz o `jid` em formato **LID** (`@lid`, para preservar a privacidade de quem solicitou) e, quando disponível, o `phoneNumber` correspondente. Use esses identificadores em [`/participants`](/pt/api/groups/participants) com `action=approve` ou `action=reject` para resolver cada pedido. ```json 200 OK { "success": true, "message": "2 pending requests found", "groupJid": "120363406289005073@g.us", "requests": [ { "jid": "199789077627112@lid", "phoneNumber": "5511999999999@s.whatsapp.net", "requestedAt": "2026-04-20T14:00:00Z" } ], "meta": { "total": 2 } } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Query JID, código de convite ou link do grupo. ## Notas - O `jid` retornado vem em formato **LID** (`@lid`) para preservar a privacidade de quem solicitou, em alguns casos `phoneNumber` pode vir `null`. - Para aprovar ou rejeitar em lote, encaminhe esses JIDs (ou os PNs) para [`/participants`](/pt/api/groups/participants). ## Erros | HTTP | Mensagem | |------|----------| | 400 | `Identifier is required` | | 403 | `Not authorized to view group requests (must be admin)` | | 404 | `Group not found or you are not a member of this group` | Envelope: ```json { "success": false, "error": { "message": "Not authorized to view group requests (must be admin)" } } ``` ## Próximo Use `action=approve` ou `action=reject` em `/api/group/participants`. Ative ou desative `requireAdminApproval`. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim (sair de um grupo do qual você já saiu retorna erro 404) ## Descrição Faz a instância sair de um grupo. O identifier vai como **query string** (e não no body), por se tratar de `DELETE`. ## Exemplos ### Sair (por JID) Remove a instância do grupo `120363406289005073@g.us` informando o JID na query string. É o formato mais direto quando você já tem o JID em mãos. ```bash cURL curl -X DELETE "https://ryzeapi.cloud/api/group/leave/$Instance_Name?identifier=120363406289005073@g.us" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/group/leave/${process.env.Instance_Name}?identifier=120363406289005073@g.us`, { method: "DELETE", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.delete( f"https://ryzeapi.cloud/api/group/leave/{os.environ['Instance_Name']}?identifier=120363406289005073@g.us", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/group/leave/"+os.Getenv("Instance_Name")+"?identifier=120363406289005073@g.us", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Sair (por link) Sai do grupo passando o link de convite (`https://chat.whatsapp.com/ABC123XYZ`) em `identifier`. O serviço extrai o código, resolve o JID e executa o leave. ```bash cURL curl -X DELETE -G "https://ryzeapi.cloud/api/group/leave/$Instance_Name" \ --data-urlencode "identifier=https://chat.whatsapp.com/ABC123XYZ" \ -H "token: $Token_Instance" ``` ```javascript JavaScript const url = new URL(`https://ryzeapi.cloud/api/group/leave/${process.env.Instance_Name}`); url.searchParams.set("identifier", "https://chat.whatsapp.com/ABC123XYZ"); await fetch(url, { method: "DELETE", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.delete( f"https://ryzeapi.cloud/api/group/leave/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"] }, params={ "identifier": "https://chat.whatsapp.com/ABC123XYZ" } ) ``` ```go Go package main import ( "net/http" "net/url" "os" ) func main() { q := url.Values{} q.Set("identifier", "https://chat.whatsapp.com/ABC123XYZ") req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/group/leave/"+os.Getenv("Instance_Name")+"?"+q.Encode(), nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Confirma a saída devolvendo o `groupJid` do grupo abandonado. Sair de uma comunidade (parent group) também desvincula a instância dos subgrupos vinculados pela comunidade. Para voltar ao grupo, será necessário obter um novo convite. ```json 200 OK { "success": true, "message": "Successfully left the group", "groupJid": "120363406289005073@g.us" } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Query JID, código de convite ou link do grupo. ## Notas - Sair de uma comunidade (parent group) também desvincula a instância dos subgrupos vinculados pela comunidade. - Para voltar ao grupo, é preciso obter um novo convite. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `Identifier is required` | | 403 | `Not allowed to leave this group` | | 404 | `Group not found or you are not a member of this group` | Envelope: ```json { "success": false, "error": { "message": "Group not found or you are not a member of this group" } } ``` ### Comunidades **Auth:** `TokenAccount` ou `TokenInstance` em todas as rotas. Cada chamada valida a ownership da instância. Comunidades são "parent groups" do WhatsApp que agrupam outros grupos como subgrupos. Esta seção cobre as rotas `/api/community/*` para criar, vincular, desvincular e listar subgrupos. Para grupos comuns, veja [Grupos](/pt/api/groups/overview). ## Endpoints | Método | Path | Função | |--------|------|--------| | POST | `/api/community/create/:instance` | [Criar comunidade](/pt/api/communities/create) | | POST | `/api/community/link/:instance` | [Vincular grupos a comunidade](/pt/api/communities/link) | | POST | `/api/community/unlink/:instance` | [Desvincular grupos](/pt/api/communities/unlink) | | GET | `/api/community/listSubGroups/:instance` | [Listar subgrupos](/pt/api/communities/list-subgroups) | ## Como funciona Uma comunidade no WhatsApp é composta por: - **Grupo-pai** (a comunidade em si), onde você gerencia tudo - **Grupo de Anúncios**, criado automaticamente. Apenas admins postam, mas todos os membros de subgrupos recebem - **Subgrupos**, grupos comuns vinculados à comunidade Cada grupo pertence a **no máximo uma comunidade** por vez. Para mover um grupo de uma comunidade para outra, desvincule primeiro e depois vincule no destino. ## Identifiers - A maioria das rotas espera **JIDs `@g.us`** em `communityJid` e `groupJid`. - `GET /listSubGroups` exige `?communityJid=` na query string. ## Modelos de resposta ### Resposta de `/create` ```json { "success": true, "message": "Community created successfully", "linkedGroups": ["120363406289005074@g.us"], "failedGroups": [], "imageError": null, "group": { "name": "Comunidade Alpha", "jid": "120363406289005073@g.us", "isCommunity": true, "isParent": true, "linkedParentJid": null } } ``` ### Resposta de `/link` e `/unlink` ```json { "success": true, "message": "Linked 2 of 2 groups to community", "linked": ["120363406289005074@g.us", "120363406289005075@g.us"], "failed": [] } ``` No endpoint de `/unlink`, o campo se chama `linked` mas contém os JIDs **desvinculados** com sucesso (DTO compartilhado). Use a `message` (`"Unlinked N of M ..."`) para desambiguar. ### Resposta de `/listSubGroups` ```json { "success": true, "message": "2 subgroup(s) found", "communityJid": "120363406289005073@g.us", "subgroups": [ { "jid": "120363406289005074@g.us", "name": "Anúncios", "isDefaultSubGroup": true }, { "jid": "120363406289005075@g.us", "name": "Geral", "isDefaultSubGroup": false } ] } ``` `isDefaultSubGroup=true` indica o **Grupo de Anúncios** padrão da comunidade. ## Envelope de erro ```json { "success": false, "error": { "message": "community jid is required" } } ``` ## Próximo Cria o grupo-pai e vincula subgrupos iniciais. Retorna os grupos vinculados. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Cria uma comunidade (grupo-pai com Grupo de Anúncios automático). Aceita opcionalmente descrição, foto e uma lista de grupos existentes a vincular durante a criação. Cada vinculação tem **delay de 1s** entre chamadas para evitar throttling do servidor WhatsApp. Se o servidor WhatsApp rejeitar a criação como parent (`400`), o serviço faz **fallback automático** para grupo regular. A resposta vem com `isCommunity: false` na resposta e um aviso na `message`. ## Exemplos ### Mínimo Cria a comunidade apenas com o `name` obrigatório ("Comunidade Alpha"), sem descrição, foto nem subgrupos vinculados na chamada. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/community/create/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "name": "Comunidade Alpha" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/community/create/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ name: "Comunidade Alpha" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/community/create/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "name": "Comunidade Alpha" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "Comunidade Alpha" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/community/create/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com subgrupos Cria a comunidade "Empresa XYZ" com descrição, foto e já vincula 2 grupos existentes (`groupJid`) como subgrupos, além de definir `membershipApprovalMode: request_required` para que entradas precisem de aprovação. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/community/create/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "name": "Empresa XYZ", "description": "Comunidade oficial", "image": "https://exemplo.com/community.png", "groupJid": [ "120363406289005074@g.us", "120363406289005075@g.us" ], "membershipApprovalMode": "request_required" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/community/create/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ name: "Empresa XYZ", description: "Comunidade oficial", image: "https://exemplo.com/community.png", groupJid: [ "120363406289005074@g.us", "120363406289005075@g.us" ], membershipApprovalMode: "request_required" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/community/create/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "name": "Empresa XYZ", "description": "Comunidade oficial", "image": "https://exemplo.com/community.png", "groupJid": [ "120363406289005074@g.us", "120363406289005075@g.us" ], "membershipApprovalMode": "request_required" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "Empresa XYZ", "description": "Comunidade oficial", "image": "https://exemplo.com/community.png", "groupJid": [ "120363406289005074@g.us", "120363406289005075@g.us" ], "membershipApprovalMode": "request_required" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/community/create/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Devolve a comunidade recém-criada em `group`, onde `group.jid` é o JID da comunidade (use-o como `communityJid` em chamadas subsequentes) e `isCommunity: true` confirma que o WhatsApp aceitou a criação como parent group. Os grupos passados em `groupJid` são processados em ordem e separados em `linkedGroups` (sucesso) e `failedGroups` (falha). Se uma `image` foi enviada mas falhou ao aplicar, o motivo aparece em `imageError` sem abortar a criação. ```json 200 OK { "success": true, "message": "Community created successfully", "linkedGroups": ["120363406289005074@g.us"], "failedGroups": [], "imageError": "", "group": { "name": "Comunidade Alpha", "jid": "120363406289005073@g.us", "description": "Comunidade oficial", "inviteCode": "ABC123XYZ", "inviteLink": "https://chat.whatsapp.com/ABC123XYZ", "createdBy": "5511999999999@s.whatsapp.net", "participantCount": 1, "participants": [ { "jid": "5511999999999@s.whatsapp.net", "isAdmin": true, "isSuperAdmin": true } ], "isCommunity": true, "isParent": true } } ``` ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Nome da comunidade. Máximo de **25 caracteres**. Descrição da comunidade. URL ou base64. Convertida para JPEG. Falha de imagem **não** aborta a criação, vem reportada em `imageError`. Lista de JIDs `@g.us` de grupos existentes a vincular como subgrupos. `request_required` (subgrupos exigem aprovação por default) ou string vazia (open). ## Notas - A comunidade nasce **sem participantes**, membros entram via cada subgrupo. - O **Grupo de Anúncios** é criado automaticamente pelo WhatsApp. Você não controla nome / descrição dele aqui, use [`PUT /api/group/update`](/pt/api/groups/update) depois para ajustar. - Limite de **50 subgrupos** por comunidade (excedentes caem em `failedGroups`). - `imageError` é populado quando a foto falha mas a comunidade é criada normalmente. - Fallback silencioso para grupo regular: monitore `isCommunity` no cliente para alertar o usuário. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `community name is required` | | 400 | `Community name must be 25 characters or less` | | 400 | `Instance is not connected to WhatsApp` | | 429 | `rate limit exceeded (429): wait before creating again` | | 500 | `failed to create community: ` | Envelope: ```json { "success": false, "error": { "message": "community name is required" } } ``` ## Próximo Adicionar mais subgrupos depois da criação. Conferir os grupos atualmente vinculados. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Lista os subgrupos de uma comunidade. O **Grupo de Anúncios** é identificado pelo flag `isDefaultSubGroup: true`. Diferente dos demais endpoints, esta rota exige **JID** em `?communityJid=` (não aceita código / link). ## Exemplos ### Listar subgrupos Consulta os subgrupos da comunidade `120363406289005073@g.us` passando o JID completo (com sufixo `@g.us`) na query string. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/community/listSubGroups/$Instance_Name?communityJid=120363406289005073@g.us" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/community/listSubGroups/${process.env.Instance_Name}?communityJid=120363406289005073@g.us`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/community/listSubGroups/{os.environ['Instance_Name']}?communityJid=120363406289005073@g.us", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/community/listSubGroups/"+os.Getenv("Instance_Name")+"?communityJid=120363406289005073@g.us", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Sem sufixo @g.us Mesma consulta, mas passando apenas o ID numérico em `communityJid`. O serviço adiciona `@g.us` automaticamente antes de resolver a comunidade. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/community/listSubGroups/$Instance_Name?communityJid=120363406289005073" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/community/listSubGroups/${process.env.Instance_Name}?communityJid=120363406289005073`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/community/listSubGroups/{os.environ['Instance_Name']}?communityJid=120363406289005073", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/community/listSubGroups/"+os.Getenv("Instance_Name")+"?communityJid=120363406289005073", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Retorna `communityJid` (já normalizado com sufixo `@g.us`) e o array `subgroups` com cada grupo vinculado em que o bot participa. Cada item traz `jid`, `name` e o flag `isDefaultSubGroup` que identifica o **Grupo de Anúncios** da comunidade. O `message` traz a contagem (`"N subgroup(s) found"`) e o array vem vazio quando nenhum subgrupo é encontrado. ```json 200 OK { "success": true, "message": "2 subgroup(s) found", "communityJid": "120363406289005073@g.us", "subgroups": [ { "jid": "120363406289005074@g.us", "name": "Anúncios", "isDefaultSubGroup": true }, { "jid": "120363406289005075@g.us", "name": "Geral", "isDefaultSubGroup": false } ] } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Query JID `@g.us` da comunidade. Se omitir o sufixo, o serviço adiciona `@g.us` automaticamente. ## Notas - A listagem **só retorna subgrupos em que o bot está**, se o bot participa apenas do parent mas não de um subgrupo X, X não aparece na lista. - A propagação de novas vinculações pode levar 1-3s, um `GET` logo após `POST /link` pode retornar a lista sem o grupo recém-vinculado temporariamente. - O **Grupo de Anúncios** sempre aparece com `isDefaultSubGroup: true`. Filtre no cliente se precisar excluir-lo da listagem. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `communityJid query parameter is required` | | 400 | `invalid community JID: ` | | 400 | `Instance is not connected to WhatsApp` | | 500 | `failed to get groups: ` | Envelope: ```json { "success": false, "error": { "message": "communityJid query parameter is required" } } ``` ## Próximo Adicionar novos subgrupos. Remover subgrupos da comunidade. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Vincula um ou mais grupos existentes a uma comunidade. Cada vinculação tem **delay interno de 1s** para evitar throttling do servidor WhatsApp. Falhas individuais não abortam a operação, vão para o array `failed`. ## Exemplos ### Vincular 2 grupos Anexa em lote 2 grupos existentes à comunidade `120363406289005073@g.us` numa única chamada. O serviço aplica delay interno de 1s entre as vinculações para evitar throttling. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/community/link/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "communityJid": "120363406289005073@g.us", "groupJid": [ "120363406289005074@g.us", "120363406289005075@g.us" ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/community/link/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ communityJid: "120363406289005073@g.us", groupJid: [ "120363406289005074@g.us", "120363406289005075@g.us" ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/community/link/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "communityJid": "120363406289005073@g.us", "groupJid": [ "120363406289005074@g.us", "120363406289005075@g.us" ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "communityJid": "120363406289005073@g.us", "groupJid": [ "120363406289005074@g.us", "120363406289005075@g.us" ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/community/link/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Vincular 1 grupo Vincula um único grupo à comunidade. O array `groupJid` aceita 1 ou mais itens, útil quando você quer adicionar subgrupos um a um. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/community/link/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "communityJid": "120363406289005073@g.us", "groupJid": ["120363406289005074@g.us"] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/community/link/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ communityJid: "120363406289005073@g.us", groupJid: ["120363406289005074@g.us"] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/community/link/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "communityJid": "120363406289005073@g.us", "groupJid": ["120363406289005074@g.us"] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "communityJid": "120363406289005073@g.us", "groupJid": ["120363406289005074@g.us"] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/community/link/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Devolve a contagem em `message` (formato `"Linked N of M groups to community"`), o array `linked` com os JIDs vinculados com sucesso e `failed` com os que não foram processados (JID inválido, grupo já vinculado a outra comunidade ou bot sem permissão). `success` é `true` quando ao menos um grupo foi vinculado. ```json 200 OK { "success": true, "message": "Linked 2 of 2 groups to community", "linked": [ "120363406289005074@g.us", "120363406289005075@g.us" ], "failed": [] } ``` Sucesso parcial e falha total retornam **HTTP 200**. Sempre cheque `success` + `failed[]` no cliente para detectar erros por grupo. ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body JID `@g.us` da comunidade. Array de JIDs `@g.us` dos grupos a vincular. Pelo menos **1** item. ## Notas - Grupo já vinculado a **outra comunidade** cai em `failed[]`, desvincule antes via [`/unlink`](/pt/api/communities/unlink). - Limite de **50 subgrupos** por comunidade. Excedentes falham silenciosamente. - O **Grupo de Anúncios** já pertence à comunidade desde a criação e não pode ser vinculado. - Bot precisa ser admin tanto da comunidade quanto do grupo sendo vinculado. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `community jid is required` | | 400 | `group jid is required` | | 400 | `Instance is not connected to WhatsApp` | | 500 | `error parse community jid: ` | Envelope: ```json { "success": false, "error": { "message": "community jid is required" } } ``` ## Próximo Remover grupos da comunidade. Confirmar os subgrupos vinculados. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Desvincula grupos de uma comunidade. Os grupos **não são deletados**, apenas voltam a ser independentes (sem `linkedParentJid`). Mesmo body e response do `/link` (DTO compartilhado). O campo `linked` na resposta contém os JIDs **desvinculados** com sucesso (devido ao reuso do DTO `CommunityLinkResponse`). A `message` (`"Unlinked N of M ..."`) confirma a semântica. ## Exemplos ### Desvincular grupos Remove 2 subgrupos da comunidade `120363406289005073@g.us`. Os grupos voltam a ser independentes (sem `linkedParentJid`) mas continuam existindo com seus membros. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/community/unlink/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "communityJid": "120363406289005073@g.us", "groupJid": [ "120363406289005074@g.us", "120363406289005075@g.us" ] }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/community/unlink/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ communityJid: "120363406289005073@g.us", groupJid: [ "120363406289005074@g.us", "120363406289005075@g.us" ] }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/community/unlink/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "communityJid": "120363406289005073@g.us", "groupJid": [ "120363406289005074@g.us", "120363406289005075@g.us" ] } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "communityJid": "120363406289005073@g.us", "groupJid": [ "120363406289005074@g.us", "120363406289005075@g.us" ] }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/community/unlink/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Confirma a desvinculação devolvendo a contagem em `message` (formato `"Unlinked N of M groups from community"`). Por reuso do DTO compartilhado com `/link`, o array `linked` contém aqui os JIDs **desvinculados** com sucesso e `failed` os que falharam. `success` é `true` quando ao menos um grupo foi desvinculado, mesmo em sucesso parcial, sempre cheque `failed[]` para detectar erros por grupo. ```json 200 OK { "success": true, "message": "Unlinked 2 of 2 groups from community", "linked": [ "120363406289005074@g.us", "120363406289005075@g.us" ], "failed": [] } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body JID `@g.us` da comunidade. JIDs dos grupos a desvincular. Pelo menos **1** item. ## Notas - O Grupo de Anúncios da comunidade **não pode ser desvinculado**, para excluir o announcement, é preciso deletar a comunidade inteira (não exposto nesta API). - Os membros dos grupos continuam onde estavam, apenas a relação pai-filho é removida. - Para mover um grupo entre comunidades, desvincule daqui e use [`/link`](/pt/api/communities/link) na nova. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `community jid is required` | | 400 | `group jid is required` | | 400 | `Instance is not connected to WhatsApp` | | 500 | `error parse community jid: ` | Envelope: ```json { "success": false, "error": { "message": "community jid is required" } } ``` ## Próximo Reconectar a outra comunidade. Confirmar que o grupo foi removido. ### Newsletter **Auth:** `TokenAccount` ou `TokenInstance` em todas as rotas. Cada chamada valida a ownership da instância. Esta seção cobre as rotas `/api/newsletter/*` para criar canais, listar inscrições, obter detalhes, seguir e deixar de seguir. Canais têm JIDs terminando em `@newsletter` (diferente de grupos `@g.us`). ## Endpoints | Método | Path | Função | |--------|------|--------| | POST | `/api/newsletter/create/:instance` | [Criar canal](/pt/api/newsletter/create) | | GET | `/api/newsletter/list/:instance` | [Listar canais inscritos](/pt/api/newsletter/list) | | GET | `/api/newsletter/info/:instance` | [Info de um canal](/pt/api/newsletter/info) | | POST | `/api/newsletter/join/:instance` | [Inscrever-se](/pt/api/newsletter/join) | | DELETE | `/api/newsletter/leave/:instance` | [Cancelar inscrição](/pt/api/newsletter/leave) | ## Identifiers aceitos Os endpoints `info`, `join` e `leave` aceitam: | Forma | Exemplo | |-------|---------| | JID | `120363422585881117@newsletter` | | Link completo | `https://whatsapp.com/channel/120363422585881117` | | Código apenas | `120363422585881117` | ## Modelo `NewsletterChannel` | Campo | Tipo | Descrição | |-------|------|-----------| | `jid` | string | `@newsletter` | | `state` | string | `active`, `suspended`, `geosuspended` | | `name` | string | Nome do canal | | `description` | string | Pode ser vazia | | `inviteLink` | string? | `https://whatsapp.com/channel/` (apenas para admins / criador) | | `subscriberCount` | int | Pode ser `0` se desconhecido | | `pictureUrl` | string? | URL temporária do CDN do WhatsApp | ## Estados de canal | Estado | Significado | |--------|-------------| | `active` | Canal operacional, recebendo publicações e novos seguidores | | `suspended` | Canal suspenso (violação de políticas), visível mas sem interação | | `geosuspended` | Canal indisponível na sua região | ## Envelope de erro ```json { "success": false, "error": { "message": "newsletter not found" } } ``` ## Suporte do cliente WhatsMeow Algumas rotas dependem de funções do cliente WhatsMeow que podem não estar disponíveis em determinadas builds. Quando isso acontece, o servidor retorna **HTTP 501** com mensagens específicas: - `WhatsApp client does not support newsletter creation` - `WhatsApp client does not support listing newsletters` - `WhatsApp client does not support FollowNewsletter` - `WhatsApp client does not support UnfollowNewsletter` ## Quadro de erros (resumo) | HTTP | Mensagem | |------|----------| | 400 | `The 'name' field is required` | | 400 | `The 'identifier' query parameter is required (JID @newsletter or invite link/code)` | | 400 | `Invalid newsletter identifier (use JID @newsletter or invite link/code)` | | 400 | `Instance is not connected to WhatsApp` | | 404 | `newsletter not found` | | 500 | `failed to create newsletter: ` | | 500 | `failed to follow newsletter: ` | | 500 | `failed to leave newsletter: ` | | 501 | `WhatsApp client does not support ` | ## Próximo Cria um novo canal vinculado à conta. Retorna canais inscritos. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não (cada chamada cria um canal novo) ## Descrição Cria um novo canal. A conta criadora torna-se automaticamente admin / dono. O serviço aceita os termos de uso (TOS) automaticamente quando o WhatsApp os exigir, fazendo um retry transparente após `AcceptTOSNotice`. ## Exemplos ### Mínimo Cria um canal apenas com o `name` obrigatório. O canal nasce sem descrição nem foto, mas já recebe um `jid` permanente e um `inviteLink` no response. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/newsletter/create/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "name": "Notícias Importantes" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/newsletter/create/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ name: "Notícias Importantes" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/newsletter/create/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "name": "Notícias Importantes" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "Notícias Importantes" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/newsletter/create/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Completo Cria o canal já com `description` e `picture` (URL pública). O servidor baixa a imagem, converte para JPEG 640x640 e define como foto inicial do canal. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/newsletter/create/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "name": "Notícias Importantes", "description": "Atualizações diárias", "picture": "https://exemplo.com/logo.png" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/newsletter/create/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ name: "Notícias Importantes", description: "Atualizações diárias", picture: "https://exemplo.com/logo.png" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/newsletter/create/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "name": "Notícias Importantes", "description": "Atualizações diárias", "picture": "https://exemplo.com/logo.png" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "Notícias Importantes", "description": "Atualizações diárias", "picture": "https://exemplo.com/logo.png" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/newsletter/create/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com foto base64 Mesmo fluxo, mas a foto vai inline como `data:` URL com base64 em vez de URL externa. Útil quando a imagem é gerada localmente ou está atrás de autenticação. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/newsletter/create/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "name": "Canal de Testes", "picture": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/newsletter/create/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ name: "Canal de Testes", picture: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/newsletter/create/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "name": "Canal de Testes", "picture": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "name": "Canal de Testes", "picture": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/newsletter/create/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta inclui o `channel.jid` permanente do canal recém-criado (use-o como `identifier` nas chamadas subsequentes), o `inviteLink` pronto para compartilhar e o `state` atual. `subscriberCount` começa em `0` e `pictureUrl` vem `null` quando a foto ainda não foi processada ou não foi enviada. ```json 200 OK { "success": true, "message": "Newsletter created successfully", "channel": { "jid": "120363422585881117@newsletter", "state": "active", "name": "Notícias Importantes", "description": "Atualizações diárias", "inviteLink": "https://whatsapp.com/channel/120363422585881117", "subscriberCount": 0, "pictureUrl": null } } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Nome do canal. Não pode ser vazio. Descrição / bio do canal. URL ou base64. Convertida para JPEG (máx 640×640). Falha apenas loga warning, o canal é criado sem foto. ## Notas - Em alguns países, criar canal exige **conta WhatsApp Business verificada**. Se o servidor rejeitar, o erro do WhatsMeow é propagado. - Guarde o `channel.jid` retornado, links de convite podem ser revogados, mas o JID é permanente. - A criação **não é idempotente**: retry automático em timeout de rede pode duplicar o canal. - A foto é redimensionada para 640x640 mantendo aspect ratio (formatos aceitos: JPEG, PNG, WebP, GIF). ## Erros | HTTP | Mensagem | |------|----------| | 400 | `The 'name' field is required` | | 400 | `Instance is not connected to WhatsApp` | | 500 | `failed to create newsletter: ` | | 501 | `WhatsApp client does not support newsletter creation (CreateNewsletter not available)` | | 501 | `failed to create newsletter (terms may need acceptance)` | Envelope: ```json { "success": false, "error": { "message": "The 'name' field is required" } } ``` ## Próximo Confirmar dados após a criação. Ver os canais inscritos. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim (read-only) ## Descrição Retorna **todos** os canais aos quais a conta está inscrita, canais que ela segue **e** canais dos quais é admin / dono. Sem paginação: a lista vem completa em uma única resposta. ## Exemplos ### Listar Retorna em uma única resposta todos os canais que a conta segue ou administra. Sem filtros nem paginação, o cliente recebe a lista completa em `newsletters[]` com metadados de cada canal. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/newsletter/list/$Instance_Name" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/newsletter/list/${process.env.Instance_Name}`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/newsletter/list/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/newsletter/list/"+os.Getenv("Instance_Name"), nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta traz `newsletters[]` com cada canal seguido ou administrado pela conta e `meta.total` com a contagem (igual a `newsletters.length`). Cada item segue o shape `NewsletterChannel` (mesmo de [`/info`](/pt/api/newsletter/info)). Quando a conta não segue nenhum canal, o array vem vazio e `meta.total = 0`. ```json 200 OK { "success": true, "message": "2 newsletter(s) found", "newsletters": [ { "jid": "120363422585881117@newsletter", "state": "active", "name": "Notícias", "description": "Atualizações diárias", "inviteLink": "https://whatsapp.com/channel/120363422585881117", "subscriberCount": 150, "pictureUrl": null }, { "jid": "120363499999999999@newsletter", "state": "active", "name": "Tech News", "description": "Latest tech updates", "subscriberCount": 500, "pictureUrl": "https://exemplo.com/tech.jpg" } ], "meta": { "total": 2 } } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Notas - A lista mistura canais **seguidos** + **administrados**. Não há flag distintiva, para diferenciar, use [`GET /info`](/pt/api/newsletter/info) para inspecionar role. - Canais `suspended` / `geosuspended` aparecem na lista, filtre no cliente se quiser apenas operacionais. - `inviteLink` e `pictureUrl` são `omitempty`, canais que você só segue normalmente não expõem o link de convite. - Sem paginação: contas com 100+ canais podem ter respostas grandes. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `Instance is not connected to WhatsApp` | | 404 | `Instance not found` | | 500 | `failed to get newsletters: ` | | 501 | `WhatsApp client does not support listing newsletters (GetSubscribedNewsletters not available)` | Envelope: ```json { "success": false, "error": { "message": "Instance is not connected to WhatsApp" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna o `NewsletterChannel` completo. Útil para preview antes de seguir um canal, funciona mesmo que você **não esteja inscrito**. ## Exemplos ### Por JID Consulta os metadados do canal pelo JID canônico (`@newsletter`). Funciona mesmo que a conta não esteja inscrita, útil para preview antes de chamar `/join`. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/newsletter/info/$Instance_Name?identifier=120363422585881117@newsletter" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/newsletter/info/${process.env.Instance_Name}?identifier=120363422585881117@newsletter`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/newsletter/info/{os.environ['Instance_Name']}", params={"identifier": "120363422585881117@newsletter"}, headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/newsletter/info/"+os.Getenv("Instance_Name")+"?identifier=120363422585881117@newsletter", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Por link Consulta o canal pelo link de convite completo. O servidor faz o url-decode e resolve o código antes de buscar os metadados, retornando o mesmo `NewsletterChannel`. ```bash cURL curl -G "https://ryzeapi.cloud/api/newsletter/info/$Instance_Name" \ --data-urlencode "identifier=https://whatsapp.com/channel/120363422585881117" \ -H "token: $Token_Instance" ``` ```javascript JavaScript const params = new URLSearchParams({ identifier: "https://whatsapp.com/channel/120363422585881117" }); await fetch(`https://ryzeapi.cloud/api/newsletter/info/${process.env.Instance_Name}?${params}`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/newsletter/info/{os.environ['Instance_Name']}", params={"identifier": "https://whatsapp.com/channel/120363422585881117"}, headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "net/url" "os" ) func main() { q := url.Values{} q.Set("identifier", "https://whatsapp.com/channel/120363422585881117") req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/newsletter/info/"+os.Getenv("Instance_Name")+"?"+q.Encode(), nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Por código Consulta o canal apenas com o código de convite (sufixo do link). Mesmo resultado, mais conciso quando você já tem o código extraído. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/newsletter/info/$Instance_Name?identifier=120363422585881117" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/newsletter/info/${process.env.Instance_Name}?identifier=120363422585881117`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/newsletter/info/{os.environ['Instance_Name']}", params={"identifier": "120363422585881117"}, headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/newsletter/info/"+os.Getenv("Instance_Name")+"?identifier=120363422585881117", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta retorna o `NewsletterChannel` completo, com `channel.jid` canônico, `state` atual (`active`, `suspended`, etc.), nome, descrição, `inviteLink` (quando disponível), `subscriberCount` e `pictureUrl`. Os campos `inviteLink` e `pictureUrl` são opcionais e podem vir ausentes quando a conta não tem permissão para enxergá-los. ```json 200 OK { "success": true, "message": "Newsletter info retrieved", "channel": { "jid": "120363422585881117@newsletter", "state": "active", "name": "Tech News", "description": "Latest tech updates", "inviteLink": "https://whatsapp.com/channel/120363422585881117", "subscriberCount": 500, "pictureUrl": "https://exemplo.com/tech.jpg" } } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Query JID `@newsletter`, link completo (`https://whatsapp.com/channel/`) ou apenas o código (``). ## Notas - É a forma certa de **preview** antes de chamar [`/join`](/pt/api/newsletter/join). - `subscriberCount` pode vir defasado (cache do WhatsApp). - Códigos são **case-sensitive**: `ABC123` não é o mesmo que `abc123`. - `inviteLink` só vem se a conta tiver permissão para enxergá-lo. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `The 'identifier' query parameter is required (JID @newsletter or invite link/code)` | | 400 | `Invalid newsletter identifier (use JID @newsletter or invite link/code)` | | 400 | `newsletter not found or no metadata returned` | | 500 | `failed to get newsletter info: ` | | 501 | `WhatsApp client does not support GetNewsletterInfo` | Envelope: ```json { "success": false, "error": { "message": "Invalid newsletter identifier (use JID @newsletter or invite link/code)" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** parcial (canal já seguido normalmente é no-op silencioso) ## Descrição Inscreve a conta (follow) em um canal. Após o `join`, o canal aparece em [`GET /list`](/pt/api/newsletter/list) e mensagens futuras chegam como `message.exchange` com `chat.type = "newsletter"`. ## Exemplos ### Por JID Inscreve a conta passando o JID canônico do canal (`@newsletter`). É o formato mais direto, sem precisar resolver link ou código antes. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/newsletter/join/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "identifier": "120363422585881117@newsletter" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/newsletter/join/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ identifier: "120363422585881117@newsletter" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/newsletter/join/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "identifier": "120363422585881117@newsletter" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "identifier": "120363422585881117@newsletter" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/newsletter/join/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Por link Inscreve a conta passando o link de convite completo (`https://whatsapp.com/channel/...`). O servidor extrai o código do final da URL e resolve o JID antes do follow. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/newsletter/join/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "identifier": "https://whatsapp.com/channel/120363422585881117" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/newsletter/join/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ identifier: "https://whatsapp.com/channel/120363422585881117" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/newsletter/join/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "identifier": "https://whatsapp.com/channel/120363422585881117" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "identifier": "https://whatsapp.com/channel/120363422585881117" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/newsletter/join/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Por código Inscreve a conta passando apenas o código de convite (sufixo do link, sem o domínio). Atalho para quem já extraiu o código previamente. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/newsletter/join/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "identifier": "120363422585881117" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/newsletter/join/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ identifier: "120363422585881117" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/newsletter/join/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "identifier": "120363422585881117" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "identifier": "120363422585881117" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/newsletter/join/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta sempre traz o JID canônico do canal em `channelJid`, mesmo quando a entrada foi feita por link ou código de convite. Use esse valor como `identifier` em chamadas subsequentes (`/info`, `/leave`). ```json 200 OK { "success": true, "message": "Successfully joined newsletter", "channelJid": "120363422585881117@newsletter" } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body JID `@newsletter`, link completo ou código de convite. ## Notas - O response sempre traz o **JID canônico** em `channelJid`, útil quando o input foi link / código. - Após `join`, a propagação para `GET /list` pode levar alguns segundos. - Em raros casos, canais privados exigem aprovação do dono, o `join` retorna sucesso mas o canal só aparece em `list` após a aprovação. - Links de convite podem ser revogados pelo dono, códigos antigos passam a falhar com `newsletter not found`. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `The 'identifier' field is required (JID @newsletter or invite link/code)` | | 400 | `Invalid newsletter identifier (use JID @newsletter or invite link/code)` | | 400 | `newsletter not found` | | 500 | `failed to follow newsletter: ` | | 501 | `WhatsApp client does not support FollowNewsletter` | Envelope: ```json { "success": false, "error": { "message": "newsletter not found" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** parcial (sair de um canal não seguido pode retornar `not found`) ## Descrição Cancela a inscrição (unfollow). Mensagens futuras param de chegar e o canal some de [`GET /list`](/pt/api/newsletter/list). O identifier vai como **query string** (e não no body), por se tratar de `DELETE`. ## Exemplos ### Por JID Cancela a inscrição passando o JID canônico do canal via query string. Mensagens futuras param de chegar e o canal some de `GET /list`. ```bash cURL curl -X DELETE "https://ryzeapi.cloud/api/newsletter/leave/$Instance_Name?identifier=120363422585881117@newsletter" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/newsletter/leave/${process.env.Instance_Name}?identifier=120363422585881117@newsletter`, { method: "DELETE", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.delete( f"https://ryzeapi.cloud/api/newsletter/leave/{os.environ['Instance_Name']}", params={"identifier": "120363422585881117@newsletter"}, headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/newsletter/leave/"+os.Getenv("Instance_Name")+"?identifier=120363422585881117@newsletter", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Por link Cancela a inscrição passando o link de convite completo. O servidor extrai o código, resolve o JID e executa o unfollow. ```bash cURL curl -X DELETE -G "https://ryzeapi.cloud/api/newsletter/leave/$Instance_Name" \ --data-urlencode "identifier=https://whatsapp.com/channel/120363422585881117" \ -H "token: $Token_Instance" ``` ```javascript JavaScript const params = new URLSearchParams({ identifier: "https://whatsapp.com/channel/120363422585881117" }); await fetch(`https://ryzeapi.cloud/api/newsletter/leave/${process.env.Instance_Name}?${params}`, { method: "DELETE", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.delete( f"https://ryzeapi.cloud/api/newsletter/leave/{os.environ['Instance_Name']}", params={"identifier": "https://whatsapp.com/channel/120363422585881117"}, headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "net/url" "os" ) func main() { q := url.Values{} q.Set("identifier", "https://whatsapp.com/channel/120363422585881117") req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/newsletter/leave/"+os.Getenv("Instance_Name")+"?"+q.Encode(), nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Por código Cancela a inscrição passando apenas o código de convite. Atalho equivalente ao link, sem o prefixo do domínio. ```bash cURL curl -X DELETE "https://ryzeapi.cloud/api/newsletter/leave/$Instance_Name?identifier=120363422585881117" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/newsletter/leave/${process.env.Instance_Name}?identifier=120363422585881117`, { method: "DELETE", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.delete( f"https://ryzeapi.cloud/api/newsletter/leave/{os.environ['Instance_Name']}", params={"identifier": "120363422585881117"}, headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/newsletter/leave/"+os.Getenv("Instance_Name")+"?identifier=120363422585881117", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta traz o JID canônico do canal em `channelJid` (resolvido a partir do `identifier` passado na query, mesmo se foi link ou código). Após esse retorno, o canal é removido de [`/list`](/pt/api/newsletter/list) e mensagens futuras param de chegar. ```json 200 OK { "success": true, "message": "Successfully left newsletter", "channelJid": "120363422585881117@newsletter" } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Query JID `@newsletter`, link completo ou código de convite. ## Notas - Se você é o **dono / admin** do canal, `leave` apenas te desinscreve, o canal continua existindo. Para deletar o canal, use a interface oficial do WhatsApp. - `DELETE` sem body é a convenção, body JSON é ignorado. - Sem evento dedicado de "unfollowed" no webhook; mensagens do canal simplesmente param de chegar. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `The 'identifier' query parameter is required (JID @newsletter or invite link/code)` | | 400 | `Invalid newsletter identifier (use JID @newsletter or invite link/code)` | | 500 | `failed to leave newsletter: ` | | 501 | `WhatsApp client does not support UnfollowNewsletter` | Envelope: ```json { "success": false, "error": { "message": "Invalid newsletter identifier (use JID @newsletter or invite link/code)" } } ``` ## Próximo Confirmar que o canal foi removido. Reinscrever-se em outro canal. ### Perfil **Auth:** `TokenAccount` ou `TokenInstance` em todas as rotas. Cada chamada valida a ownership da instância. Esta seção cobre as rotas `/api/profile/*` para atualizar o próprio perfil, consultar perfis de terceiros e ler / atualizar configurações de privacidade do WhatsApp. ## Endpoints | Método | Path | Função | |--------|------|--------| | POST | `/api/profile/account/:instance` | [Atualizar foto / nome / status](/pt/api/profile/update-account) | | GET | `/api/profile/getAccount/:instance` | [Obter perfil próprio ou de terceiro](/pt/api/profile/read-account) | | GET | `/api/profile/getPrivacy/:instance` | [Obter privacidade](/pt/api/profile/read-privacy) | | GET | `/api/profile/privacy/:instance` | [Alias de `/getPrivacy`](/pt/api/profile/read-privacy-alias) | | POST | `/api/profile/privacy/:instance` | [Atualizar privacidade](/pt/api/profile/update-privacy) | ## Estrutura de privacidade A API agrupa as configurações em três subobjetos: `visibility`, `privacy` e `permissions`. ```json { "visibility": { "lastSeen": "contacts", "status": "all", "profile": "contacts", "online": "match_last_seen" }, "privacy": { "readReceipts": "all" }, "permissions": { "callAdd": "all", "groupAdd": "contacts" } } ``` ### Valores aceitos por campo | Campo | Valores | |-------|---------| | `lastSeen` / `status` / `profile` | `all` / `contacts` / `contact_blacklist` / `none` | | `online` | `all` / `match_last_seen` | | `readReceipts` | `all` / `none` | | `callAdd` | `all` / `known` | | `groupAdd` | `all` / `contacts` / `contact_blacklist` | ## Modelo `AccountProfileData` Resposta de [`GET /getAccount`](/pt/api/profile/read-account): ```json { "profilePicture": "https://pps.whatsapp.net/...", "profileName": "João Silva", "profileStatus": "Disponivel", "phoneNumber": "5511999999999", "jid": "5511999999999@s.whatsapp.net", "lid": "199789077627112@lid" } ``` ## Envelope de erro ```json { "success": false, "error": { "message": "At least one field must be provided (profilePicture, profileName, or profileStatus)" } } ``` ## Quadro de erros (resumo) | Categoria | Mensagem | |-----------|----------| | Validação | `At least one field must be provided (profilePicture, profileName, or profileStatus)` | | Validação | `At least one privacy setting must be provided` | | Validação | `Invalid value: . Valid values: ...` | | Estado | `Instance is not connected to WhatsApp` | | Número | `Number not found or not registered on WhatsApp` | | Número | `invalid LID format` | | Falha | `failed to update privacy: ` | ## Próximo Mudar foto, nome de exibição ou status. Ajustar quem vê o que. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim (setar o mesmo valor de novo e no-op) ## Descrição Atualiza um ou mais campos do perfil da conta conectada: **foto**, **nome de exibição** (push name) e / ou **status** (about). Update parcial, pelo menos um campo deve ser enviado. A `updatedFields` na resposta lista o que foi efetivamente alterado. ## Exemplos ### Tudo Atualiza os três campos do perfil em uma única chamada: foto (URL pública), nome de exibição ("João Silva") e status ("Disponível"). A resposta lista quais foram efetivamente alterados em `updatedFields`. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/profile/account/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "profilePicture": "https://exemplo.com/foto.jpg", "profileName": "João Silva", "profileStatus": "Disponível" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/profile/account/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ profilePicture: "https://exemplo.com/foto.jpg", profileName: "João Silva", profileStatus: "Disponível" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/profile/account/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "profilePicture": "https://exemplo.com/foto.jpg", "profileName": "João Silva", "profileStatus": "Disponível" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "profilePicture": "https://exemplo.com/foto.jpg", "profileName": "João Silva", "profileStatus": "Disponível" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/profile/account/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Só nome Atualiza apenas o `profileName` (push name) para "Vendas Empresa". Foto e status atuais ficam inalterados, o handler aceita update parcial. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/profile/account/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "profileName": "Vendas Empresa" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/profile/account/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ profileName: "Vendas Empresa" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/profile/account/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "profileName": "Vendas Empresa" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "profileName": "Vendas Empresa" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/profile/account/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Foto base64 Atualiza apenas a foto, enviando a imagem inline como `data:` URL com base64. O servidor decodifica, converte para JPEG 640x640 e aplica como nova foto de perfil. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/profile/account/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "profilePicture": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/profile/account/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ profilePicture: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/profile/account/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "profilePicture": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "profilePicture": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/profile/account/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Remover foto Envia `profilePicture: ""` (string vazia) para apagar a foto atual. O perfil fica sem imagem, exibindo o avatar padrão do WhatsApp. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/profile/account/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "profilePicture": "" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/profile/account/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ profilePicture: "" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/profile/account/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "profilePicture": "" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "profilePicture": "" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/profile/account/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Retorna um envelope simples com `success`, `message` fixa (`Profile updated successfully`) e `updatedFields`, um array com os nomes dos campos efetivamente alterados (`profilePicture`, `profileName`, `profileStatus`). Use esse array para confirmar quais updates passaram, campos não enviados ou ignorados (ex.: `profileName` vazio é no-op) ficam de fora da lista. ```json 200 OK { "success": true, "message": "Profile updated successfully", "updatedFields": ["profileName", "profileStatus"] } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Pelo menos **um** campo obrigatório. URL ou base64. String vazia (`""`) **remove** a foto atual. Convertida para JPEG (máx 640×640). Push name, nome exibido aos outros contatos. Texto da seção "Recados" (about). Pode ser string vazia para limpar. ## Notas - `profilePicture: ""` **remove** a foto. `profileName: ""` é tratado como **no-op**. `profileStatus: ""` **limpa** o status. - URLs de foto devem ser públicas, o servidor bloqueia downloads de redes internas (RFC1918, link-local) por SSRF guard. - Formatos aceitos: JPEG, PNG, WebP, GIF (primeiro frame). Tudo vira JPEG Q90 antes de subir. - O cache local (`profile_name`, `profile_picture_url`) é atualizado em background, pode haver alguns segundos de defasagem. - Mudanças frequentes podem ser rate-limitadas pelo WhatsApp. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `At least one field must be provided (profilePicture, profileName, or profileStatus)` | | 400 | `Instance is not connected to WhatsApp` | | 500 | `failed to process profile picture: ` | | 500 | `failed to set profile picture: ` | | 500 | `failed to set profile name: ` | | 500 | `failed to set profile status: ` | Envelope: ```json { "success": false, "error": { "message": "At least one field must be provided (profilePicture, profileName, or profileStatus)" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim (read-only) ## Descrição Retorna os dados do perfil da própria conta conectada (foto, nome, status, JID, LID). Opcionalmente, consulta o perfil de **outro número** via `?number=`. Sem o parâmetro, retorna o perfil da própria instância. ## Exemplos ### Própria conta Sem query param: retorna o perfil da própria instância conectada (foto, nome, status, JID e LID). Forma direta de validar quem está conectado naquela sessão. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/profile/getAccount/$Instance_Name" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/profile/getAccount/${process.env.Instance_Name}`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/profile/getAccount/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/profile/getAccount/"+os.Getenv("Instance_Name"), nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Outro número Consulta o perfil público de um terceiro pelo número (`?number=5511988887777`). Útil para verificar se o número existe no WhatsApp e obter foto / nome / status visíveis ao público. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/profile/getAccount/$Instance_Name?number=5511988887777" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/profile/getAccount/${process.env.Instance_Name}?number=5511988887777`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/profile/getAccount/{os.environ['Instance_Name']}?number=5511988887777", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/profile/getAccount/"+os.Getenv("Instance_Name")+"?number=5511988887777", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Por LID Consulta o perfil usando um LID (`@lid`) em vez de número. Útil quando o evento de origem expõe apenas o LID anônimo (em comunidades / canais novos do WhatsApp), sem o telefone correspondente. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/profile/getAccount/$Instance_Name?number=52399087550579@lid" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/profile/getAccount/${process.env.Instance_Name}?number=52399087550579@lid`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/profile/getAccount/{os.environ['Instance_Name']}?number=52399087550579@lid", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/profile/getAccount/"+os.Getenv("Instance_Name")+"?number=52399087550579@lid", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Devolve `profile` com os dados do alvo: `profilePicture` (URL CDN do WhatsApp, **sempre** presente, vira `null` se a conta não tem foto ou o lookup deu timeout), `profileName` (push name ou business name), `profileStatus` (texto "Recados"), `phoneNumber` (só dígitos), `jid` (`@s.whatsapp.net`) e `lid` (formato `@lid`, quando disponível). Os campos exceto `profilePicture` usam `omitempty`, podem não aparecer se o WhatsApp não devolveu o dado. ```json 200 OK { "success": true, "message": "Profile information retrieved successfully", "profile": { "profilePicture": "https://pps.whatsapp.net/...", "profileName": "João Silva", "profileStatus": "Disponível", "phoneNumber": "5511999999999", "jid": "5511999999999@s.whatsapp.net", "lid": "199789077627112@lid" } } ``` ```json 200 OK (sem foto) { "success": true, "message": "Profile information retrieved successfully", "profile": { "profilePicture": null, "profileName": "Cliente Teste", "phoneNumber": "5511988887777", "jid": "5511988887777@s.whatsapp.net" } } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Query Número (`5511999999999`, `+5511999999999`, `5511999999999@s.whatsapp.net`) ou LID (`52399087550579@lid`). Se omitido, retorna o perfil da própria instância. ## Notas - Para números BR (`55...`), o serviço tenta automaticamente variações com e sem o 9º dígito. - Sanitização automática de `+`, `-`, `(`, `)` e espaços: `(11) 99999-9999` vira `11999999999`. - `profilePicture` é o único campo que sempre aparece (pode ser `null`); os demais usam `omitempty`. - A URL da foto é temporária (CDN do WhatsApp). Se o lookup ultrapassar 10s, o campo vem `null`. - Para a própria conta, prefira `number=""`, passar o próprio número retorna dados "como outros te veem". ## Erros | HTTP | Mensagem | |------|----------| | 400 | `Number not found or not registered on WhatsApp` | | 400 | `invalid LID format` | | 400 | `Instance is not connected to WhatsApp` | | 500 | `timeout ao buscar foto (>10s)` | Envelope: ```json { "success": false, "error": { "message": "Number not found or not registered on WhatsApp" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim (setar o mesmo valor e no-op) ## Descrição Atualiza uma ou mais configurações de privacidade. Update **parcial**, apenas os campos enviados são alterados. Pelo menos uma das três subseções (`visibility`, `privacy`, `permissions`) precisa ser enviada. A resposta retorna as configurações **completas** após o update. ## Exemplos ### Tudo restritivo Aplica um perfil de privacidade fechado em uma única chamada: esconde `lastSeen`, restringe status / foto a contatos, desliga read receipts e limita chamadas a contatos conhecidos. Cada subseção envia um campo, totalizando várias stanzas no WhatsApp. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/profile/privacy/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "visibility": { "lastSeen": "none", "status": "contacts", "profile": "contacts", "online": "match_last_seen" }, "privacy": { "readReceipts": "none" }, "permissions": { "callAdd": "known", "groupAdd": "contacts" } }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/profile/privacy/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ visibility: { lastSeen: "none", status: "contacts", profile: "contacts", online: "match_last_seen" }, privacy: { readReceipts: "none" }, permissions: { callAdd: "known", groupAdd: "contacts" } }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/profile/privacy/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "visibility": { "lastSeen": "none", "status": "contacts", "profile": "contacts", "online": "match_last_seen" }, "privacy": { "readReceipts": "none" }, "permissions": { "callAdd": "known", "groupAdd": "contacts" } } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "visibility": { "lastSeen": "none", "status": "contacts", "profile": "contacts", "online": "match_last_seen" }, "privacy": { "readReceipts": "none" }, "permissions": { "callAdd": "known", "groupAdd": "contacts" } }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/profile/privacy/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Só groupAdd Atualiza somente `permissions.groupAdd` para `contacts`, impedindo que desconhecidos adicionem a conta a grupos. As demais configurações ficam inalteradas. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/profile/privacy/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "permissions": { "groupAdd": "contacts" } }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/profile/privacy/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ permissions: { groupAdd: "contacts" } }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/profile/privacy/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "permissions": {"groupAdd": "contacts"} } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "permissions": { "groupAdd": "contacts" } }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/profile/privacy/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Desligar read receipts Define `privacy.readReceipts` como `none` para parar de enviar o "duplo check azul". A conta deixa de confirmar leitura, e também deixa de ver a confirmação dos outros (efeito recíproco do WhatsApp). ```bash cURL curl -X POST "https://ryzeapi.cloud/api/profile/privacy/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "privacy": { "readReceipts": "none" } }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/profile/privacy/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ privacy: { readReceipts: "none" } }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/profile/privacy/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "privacy": {"readReceipts": "none"} } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "privacy": { "readReceipts": "none" } }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/profile/privacy/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Apenas lastSeen e online Esconde o `lastSeen` e amarra o `online` ao mesmo nível (`match_last_seen`). Resultado: ninguém vê quando a conta esteve online pela última vez nem se ela está ativa agora. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/profile/privacy/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "visibility": { "lastSeen": "none", "online": "match_last_seen" } }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/profile/privacy/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ visibility: { lastSeen: "none", online: "match_last_seen" } }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/profile/privacy/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "visibility": { "lastSeen": "none", "online": "match_last_seen" } } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "visibility": { "lastSeen": "none", "online": "match_last_seen" } }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/profile/privacy/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Após aplicar os updates, o handler refaz um `GetPrivacySettings` e devolve o snapshot **completo** atual em `settings`, agrupado em `visibility` (`lastSeen`, `status`, `profile`, `online`), `privacy` (`readReceipts`) e `permissions` (`callAdd`, `groupAdd`). Use o response como fonte da verdade do estado pós-update, é o que o WhatsApp confirmou, não apenas o que você enviou. ```json 200 OK { "success": true, "message": "Privacy settings updated successfully", "settings": { "visibility": { "lastSeen": "none", "status": "contacts", "profile": "contacts", "online": "match_last_seen" }, "privacy": { "readReceipts": "none" }, "permissions": { "callAdd": "known", "groupAdd": "contacts" } } } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Cada subseção é opcional, mas pelo menos uma deve estar presente. Subcampos: `lastSeen`, `status`, `profile`, `online`. Subcampos: `readReceipts`. Subcampos: `callAdd`, `groupAdd`. ### Valores aceitos por campo | Campo | Valores | |-------|---------| | `lastSeen` / `status` / `profile` | `all` / `contacts` / `contact_blacklist` / `none` | | `online` | `all` / `match_last_seen` | | `readReceipts` | `all` / `none` | | `callAdd` | `all` / `known` | | `groupAdd` | `all` / `contacts` / `contact_blacklist` | ## Notas **Validação para antes do primeiro erro.** Se você envia `visibility.lastSeen = "X"` (inválido) + `visibility.status = "contacts"` (válido), **nada** é aplicado, o handler aborta no primeiro campo inválido. Valide enums no cliente antes de chamar. - Operações não são **transacionais**: se o terceiro `SetPrivacySetting` falhar, os dois primeiros **já foram aplicados**, o cliente recebe `500` mas o estado parcial persiste. Verifique via `GET` após erros. - Cada campo dispara uma stanza separada, um update com 7 campos faz 7 chamadas + 1 `GetPrivacySettings` final = **8 stanzas**. Pode somar latência. - O response sempre traz as configurações **completas** atuais (não só os campos alterados). ## Erros | HTTP | Mensagem | |------|----------| | 400 | `At least one privacy setting must be provided` | | 400 | `Invalid lastSeen value: . Valid values: all, contacts, contact_blacklist, none` | | 400 | `Invalid status value: . Valid values: all, contacts, contact_blacklist, none` | | 400 | `Invalid profile value: . Valid values: all, contacts, contact_blacklist, none` | | 400 | `Invalid online value: . Valid values: all, match_last_seen` | | 400 | `Invalid readReceipts value: . Valid values: all, none` | | 400 | `Invalid callAdd value: . Valid values: all, known` | | 400 | `Invalid groupAdd value: . Valid values: all, contacts, contact_blacklist` | | 400 | `Instance is not connected to WhatsApp` | | 500 | `failed to update privacy: ` | Envelope: ```json { "success": false, "error": { "message": "Invalid lastSeen value: everyone. Valid values: all, contacts, contact_blacklist, none" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim (read-only) ## Descrição Retorna **todas** as configurações de privacidade da conta conectada agrupadas em três subobjetos: `visibility`, `privacy` e `permissions`. ## Exemplos ### Padrão Lê o snapshot completo de privacidade da conta agrupado em `visibility`, `privacy` e `permissions`. Sem query params nem body, leitura simples para conferir o estado atual antes de chamar o `POST` correspondente. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/profile/getPrivacy/$Instance_Name" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/profile/getPrivacy/${process.env.Instance_Name}`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/profile/getPrivacy/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/profile/getPrivacy/"+os.Getenv("Instance_Name"), nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Retorna o snapshot completo de privacidade em `settings`, agrupado em três subobjetos: `visibility` (`lastSeen`, `status`, `profile`, `online`), `privacy` (`readReceipts`) e `permissions` (`callAdd`, `groupAdd`). Os valores vêm do store local sincronizado via appstate, alterações feitas no app oficial podem demorar alguns segundos para refletir. ```json 200 OK { "success": true, "message": "Privacy settings retrieved successfully", "settings": { "visibility": { "lastSeen": "contacts", "status": "all", "profile": "contacts", "online": "match_last_seen" }, "privacy": { "readReceipts": "all" }, "permissions": { "callAdd": "all", "groupAdd": "contacts" } } } ``` ## Parâmetros de rota Nome da instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Valores aceitos | Campo | Valores | |-------|---------| | `lastSeen` / `status` / `profile` | `all` / `contacts` / `contact_blacklist` / `none` | | `online` | `all` / `match_last_seen` | | `readReceipts` | `all` / `none` | | `callAdd` | `all` / `known` | | `groupAdd` | `all` / `contacts` / `contact_blacklist` | ## Notas - Os valores vêm primariamente do store local do whatsmeow, sincronizado via appstate. Alterações feitas pelo app oficial aparecem aqui com latência de segundos. - `online: match_last_seen` significa que sua presença online segue a regra de `lastSeen`, se `lastSeen=none`, ninguém te vê online também. - Defaults para contas recém-criadas: tudo em `all`. ## Erros | HTTP | Mensagem | |------|----------| | 400 | `Instance is not connected to WhatsApp` | | 404 | `Instance not found` | Envelope: ```json { "success": false, "error": { "message": "Instance is not connected to WhatsApp" } } ``` ### Chat O módulo **Chat** agrupa tudo o que acontece **depois** que uma mensagem entra ou sai, gestão de contatos, organização com etiquetas, controle do estado dos chats (arquivar, fixar, silenciar, bloquear) e ações sobre mensagens individuais (editar, apagar, encaminhar, favoritar). É o módulo mais amplo da API: vai de uma chamada simples como "listar contatos" até fluxos completos como "criar uma etiqueta, atribuir a vários chats e depois listar tudo que tem essa etiqueta". Todas as rotas aceitam **TokenAccount** ou **TokenInstance** e validam ownership da instância antes de operar. ## Endpoints | Método | Path | Função | |--------|------|--------| | GET | `/api/chat/contacts/:instance` | Listar/buscar contatos | | GET | `/api/chat/contactsByLabel/:instance` | Chats filtrados por etiqueta | | GET | `/api/chat/getMessage/:instance` | Buscar mensagem por ID | | GET | `/api/chat/tag/:instance` | Listar etiquetas | | POST | `/api/chat/tag/:instance` | Criar etiqueta | | DELETE | `/api/chat/tag/:instance` | Deletar etiqueta | | POST | `/api/chat/assignTag/:instance` | Aplicar etiqueta a chat | | DELETE | `/api/chat/assignTag/:instance` | Remover etiqueta de chat | | POST | `/api/chat/archive/:instance` | Arquivar/desarquivar | | POST | `/api/chat/markRead/:instance` | Marcar mensagem como lida | | POST | `/api/chat/markChatRead/:instance` | Marcar chat inteiro como lido | | POST | `/api/chat/pinChat/:instance` | Fixar/desafixar chat | | POST | `/api/chat/pinMessage/:instance` | Fixar/desafixar mensagem | | POST | `/api/chat/favorite/:instance` | Favoritar chat ou mensagem | | POST | `/api/chat/mute/:instance` | Silenciar | | POST | `/api/chat/block/:instance` | Bloquear/desbloquear contato | | POST | `/api/chat/presence/:instance` | Enviar presença (typing/recording) | | POST | `/api/chat/history/:instance` | Histórico do chat | | GET | `/api/chat/base64/:instance` | Mídia em base64 | | GET | `/api/chat/status/:instance` | Status de entrega de mensagem | | GET | `/api/chat/poll/:instance` | Votos de uma enquete | | POST | `/api/chat/forward/:instance` | Encaminhar mensagem | | POST | `/api/chat/edit/:instance` | Editar mensagem | | DELETE | `/api/chat/delete/:instance` | Apagar mensagem | | DELETE | `/api/chat/deleteChat/:instance` | Apagar chat inteiro | ## Contatos Consulta de contatos sincronizados e filtragem por etiquetas. `GET /api/chat/contacts/:instance` `GET /api/chat/contactsByLabel/:instance` ## Etiquetas (tags / labels) Crie etiquetas, aplique aos chats e use-as como filtro, exatamente como o WhatsApp Business permite, mas via API. `GET /api/chat/tag/:instance` `POST /api/chat/tag/:instance` `DELETE /api/chat/tag/:instance` `POST /api/chat/assignTag/:instance` `DELETE /api/chat/assignTag/:instance` ## Estado do chat Controle como cada conversa aparece para o usuário do WhatsApp, arquivar, fixar, silenciar, favoritar, bloquear. `POST /api/chat/archive/:instance` `POST /api/chat/pinChat/:instance` `POST /api/chat/pinMessage/:instance` `POST /api/chat/mute/:instance` `POST /api/chat/favorite/:instance` `POST /api/chat/block/:instance` ## Leitura `POST /api/chat/markRead/:instance` `POST /api/chat/markChatRead/:instance` ## Presença `POST /api/chat/presence/:instance`, mostra `typing` ou `recording` para o contato. ## Histórico `POST /api/chat/history/:instance`, mensagens armazenadas com filtros opcionais por data. ## Mensagens Ações sobre mensagens específicas, além de utilitários para baixar mídia, ler enquetes e checar status de entrega. `GET /api/chat/getMessage/:instance` `POST /api/chat/forward/:instance` `POST /api/chat/edit/:instance` `DELETE /api/chat/delete/:instance` `DELETE /api/chat/deleteChat/:instance` `GET /api/chat/base64/:instance` `GET /api/chat/status/:instance` `GET /api/chat/poll/:instance` ## Identificadores aceitos A maioria dos endpoints aceita os mesmos formatos para identificar chat ou destino. A tabela abaixo resume: | Endpoint | Aceita | |----------|--------| | Maioria (`number`) | Número (`5511...`), JID privado (`...@s.whatsapp.net` ou `...@lid`), JID grupo (`...@g.us`), JID newsletter | | `markRead` em grupo | Exige `sender` (JID do autor da mensagem, `...@s.whatsapp.net` ou `...@lid`) | | `forward` (`to`) | Mesmo conjunto que `number` | **Sobre `@lid` (LinkedID):** identificador alternativo que o WhatsApp usa para usuários individuais quando o número de telefone não está exposto (privacidade em comunidades, grupos grandes, etc.). É equivalente ao `...@s.whatsapp.net` para fins de roteamento, qualquer endpoint que aceite JID privado também aceita `@lid`. Use o JID exato vindo de webhook ou de respostas anteriores da API; não tente converter `@lid` em número. ## Janelas do WhatsApp | Ação | Limite | |------|--------| | Editar mensagem | ~15 minutos após envio | | Apagar para todos (`deleteForEveryone: true`) | ~2 dias e 12 h após envio | | Após a janela | Apenas exclusão local (`delete_for_me`) | ## Padrões de uso ### Workflow de etiquetas ```bash # 1. Criar etiqueta curl -X POST "https://ryzeapi.cloud/api/chat/tag/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"name":"VIP","color":3}' # 2. Aplicar a um chat curl -X POST "https://ryzeapi.cloud/api/chat/assignTag/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"number":"5511999999999","tagId":"1"}' # 3. Listar chats com a etiqueta curl "https://ryzeapi.cloud/api/chat/contactsByLabel/$Instance_Name?labelIds=1" \ -H "token: $Token_Instance" # 4. Remover etiqueta do chat curl -X DELETE "https://ryzeapi.cloud/api/chat/assignTag/$Instance_Name?number=5511999999999&tagId=1" \ -H "token: $Token_Instance" ``` ### Workflow de moderação ```bash # 1. Marcar como lido curl -X POST "https://ryzeapi.cloud/api/chat/markChatRead/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"number":"5511999999999","read":true}' # 2. Silenciar 8h curl -X POST "https://ryzeapi.cloud/api/chat/mute/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"number":"5511999999999","mute":true,"duration":"8h"}' # 3. Arquivar curl -X POST "https://ryzeapi.cloud/api/chat/archive/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"number":"5511999999999","archive":true}' # 4. Bloquear (último recurso) curl -X POST "https://ryzeapi.cloud/api/chat/block/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"number":"5511999999999","block":true}' ``` ## Relacionados Enviar conteúdo antes de gerenciar. Webhooks `message.exchange`, `message.status` e `label.update`. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna as mensagens armazenadas de um chat específico, ordenadas das mais novas para as mais antigas. Você pode controlar a quantidade com `count` e filtrar por janela de datas com `from`/`to`. **Não há cursor de paginação.** Para paginar, ajuste os filtros `from` e `to`. O campo `hasMore` é uma heurística: vale `true` quando o número de mensagens retornadas == `count` (provavelmente há mais). ## Exemplos ### Últimas 50 Forma mínima: passa apenas `number` e usa o `count` padrão de 50 mensagens, retornando as mais recentes do chat ordenadas da mais nova para a mais antiga. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/history/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"number":"5511999999999"}' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/history/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/history/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={"number": "5511999999999"} ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{"number":"5511999999999"}`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/history/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com janela de datas Recupera até 200 mensagens enviadas entre 20 e 28 de abril de 2026 (`from`/`to` em ISO 8601). Útil para extrair o histórico de um intervalo específico ou paginar usando o `to` como cursor. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/history/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "count": 200, "from": "2026-04-20T00:00:00Z", "to": "2026-04-28T23:59:59Z" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/history/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", count: 200, from: "2026-04-20T00:00:00Z", to: "2026-04-28T23:59:59Z" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/history/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "count": 200, "from": "2026-04-20T00:00:00Z", "to": "2026-04-28T23:59:59Z" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "count": 200, "from": "2026-04-20T00:00:00Z", "to": "2026-04-28T23:59:59Z" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/history/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Grupo Mesma lógica, mas com `number` apontando para um JID de grupo (`@g.us`) e `count` de 100. Cada item em `messages[]` traz `senderJid` preenchido com o autor da mensagem dentro do grupo. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/history/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "120363123456789@g.us", "count": 100 }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/history/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "120363123456789@g.us", count: 100 }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/history/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "120363123456789@g.us", "count": 100 } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "120363123456789@g.us", "count": 100 }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/history/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso `messages` traz as mensagens em ordem cronológica decrescente (mais recente primeiro). `count` indica quantos itens vieram nesta página e `hasMore` é `true` quando você atingiu exatamente o `count` solicitado, sinalizando que pode haver mais mensagens, paginar usando o `from`/`to` da última mensagem retornada. `chat_jid` é o JID resolvido do chat solicitado. ```json 200 OK { "success": true, "message": "Retrieved 2 messages from chat history", "chat_jid": "5511999999999@s.whatsapp.net", "messages": [ { "id": "3EB08FCF27E532F1B0F5", "fromMe": true, "timestamp": "2026-04-28T14:30:00Z", "content": "Ola", "type": "text", "chatJid": "5511999999999@s.whatsapp.net", "senderJid": "" }, { "id": "3EB08FCF27E532F1B0F4", "fromMe": false, "timestamp": "2026-04-28T14:29:55Z", "content": "Bom dia!", "type": "text", "chatJid": "5511999999999@s.whatsapp.net", "senderJid": "5511999999999@s.whatsapp.net" } ], "count": 2, "hasMore": false } ``` ## Parâmetros de rota Nome da instância. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `Content-Type` | sim | `application/json` |, | | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Request body Telefone, JID privado (`...@s.whatsapp.net` ou `...@lid`), JID de grupo (`...@g.us`) ou newsletter. Quantidade máxima de mensagens a retornar. Sem limite superior interno. ISO 8601 / RFC3339. Mensagens **a partir** desta data (inclusivo). ISO 8601 / RFC3339. Mensagens **até** esta data (inclusivo). ## Notas e gotchas - Funciona **mesmo com a instância desconectada**, lê direto do banco da ingestão. - Para paginar com segurança, use `to = timestamp da mensagem mais antiga já recebida` na chamada anterior. - `hasMore=true` não garante 100% que existam mais mensagens, é apenas uma heurística baseada no count solicitado. ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` |, | | 400 | `Invalid request body: <...>` | JSON malformado. | | 400 | `Number is required` |, | | 400 | `invalid 'from' date format. Use ISO 8601 format (e.g., '2026-02-16T18:32:39Z')` |, | | 400 | `invalid 'to' date format. Use ISO 8601 format (e.g., '2026-02-16T18:32:39Z')` |, | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | ```json Erro 400 { "success": false, "error": { "message": "Number is required" } } ``` ## Relacionados Recuperar uma mensagem específica do histórico. Baixar uma mídia citada no histórico. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Marca **todas** as mensagens não-lidas de um chat como lidas (`read: true`) ou desfaz a marcação (`read: false`). É equivalente a abrir o chat no celular: o badge de mensagens novas zera. Para marcar uma única mensagem (sem zerar o badge), use [`markRead`](/pt/api/chat/mark-read). ## Exemplos ### Marcar como lido Com `read: true`, marca todas as mensagens não-lidas do chat como lidas e zera o badge, equivalente a abrir a conversa no celular. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/markChatRead/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "read": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/markChatRead/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", read: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/markChatRead/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "read": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "read": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/markChatRead/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Marcar como não-lido Com `read: false`, restaura o estado de "não-lido" no chat para que ele volte a aparecer destacado na lista, útil para revisitar uma conversa mais tarde. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/markChatRead/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "read": false }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/markChatRead/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", read: false }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/markChatRead/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "read": False } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "read": false }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/markChatRead/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta confirma a operação com `chat_jid` (JID resolvido a partir do `number`) e `read` refletindo o estado final. A `message` muda conforme o valor de `read`: `"Chat marked as read successfully"` ou `"Chat marked as unread successfully"`. ```json 200 OK { "success": true, "message": "Chat marked as read successfully", "chat_jid": "5511999999999@s.whatsapp.net", "read": true } ``` ## Parâmetros de rota Nome da instância. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `Content-Type` | sim | `application/json` |, | | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Request body Telefone, JID privado (`...@s.whatsapp.net` ou `...@lid`), JID de grupo (`...@g.us`) ou newsletter. `true` marca como lido, `false` marca como não-lido. ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` |, | | 400 | `Invalid request body: <...>` | JSON malformado. | | 400 | `Number is required` |, | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 503 | `Instance is not connected to WhatsApp` |, | ```json Erro 400 { "success": false, "error": { "message": "Number is required" } } ``` ## Relacionados Marcar uma única mensagem. Após zerar o badge, arquivar. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Envia o ACK de leitura para uma mensagem **específica**, o efeito visível para o remetente é o "check azul". Para marcar o chat inteiro de uma vez, use [`markChatRead`](/pt/api/chat/mark-chat-read). Em **grupos**, o campo `sender` (JID do autor da mensagem) é **obrigatório**. Em conversas 1-a-1, é opcional, pode ser omitido. ## Exemplos ### DM Em conversa 1-a-1, basta `messageId` e `number` do contato. O `sender` é dispensado, o servidor infere o autor a partir do JID do chat. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/markRead/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "messageId": "3EB08FCF27E532F1B0F5", "number": "5511999999999" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/markRead/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ messageId: "3EB08FCF27E532F1B0F5", number: "5511999999999" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/markRead/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "messageId": "3EB08FCF27E532F1B0F5", "number": "5511999999999" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "messageId": "3EB08FCF27E532F1B0F5", "number": "5511999999999" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/markRead/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Grupo (com sender) Em grupo (`@g.us`), o campo `sender` com o JID do autor da mensagem é obrigatório, sem ele o WhatsApp não consegue rotear o ACK de leitura corretamente. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/markRead/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "messageId": "3EB08FCF27E532F1B0F5", "number": "120363123456789@g.us", "sender": "5511999999999@s.whatsapp.net" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/markRead/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ messageId: "3EB08FCF27E532F1B0F5", number: "120363123456789@g.us", sender: "5511999999999@s.whatsapp.net" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/markRead/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "messageId": "3EB08FCF27E532F1B0F5", "number": "120363123456789@g.us", "sender": "5511999999999@s.whatsapp.net" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "messageId": "3EB08FCF27E532F1B0F5", "number": "120363123456789@g.us", "sender": "5511999999999@s.whatsapp.net" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/markRead/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta ecoa o `message_id` marcado e o `chat_jid` resolvido. Útil para auditar quais mensagens foram marcadas como lidas, especialmente em fluxos automáticos que confirmam recibo após processar a mensagem. ```json 200 OK { "success": true, "message": "Message marked as read successfully", "message_id": "3EB08FCF27E532F1B0F5", "chat_jid": "5511999999999@s.whatsapp.net" } ``` ## Parâmetros de rota Nome da instância. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `Content-Type` | sim | `application/json` |, | | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Request body ID da mensagem que será marcada como lida. JID do chat: telefone, JID privado (`...@s.whatsapp.net` ou `...@lid`), grupo (`...@g.us`) ou newsletter. JID do autor da mensagem (`...@s.whatsapp.net` ou `...@lid`). **Obrigatório em grupos.** Em DM, opcional. ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` |, | | 400 | `Invalid request body: <...>` | JSON malformado. | | 400 | `messageId is required` |, | | 400 | `Number is required` |, | | 400 | `sender is required for group messages` | Faltou `sender` em grupo. | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 503 | `Instance is not connected to WhatsApp` |, | ```json Erro 400 { "success": false, "error": { "message": "sender is required for group messages" } } ``` ## Relacionados Marca o chat inteiro de uma vez. Conferir o status atual da mensagem. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Marca um chat como fixado (`pin: true`) ou remove a fixação (`pin: false`). O WhatsApp limita a **3 chats fixados** simultaneamente. Tentar fixar um quarto retorna erro do WhatsMeow propagado para a resposta. ## Exemplos ### Fixar Com `pin: true`, o chat é movido para o topo da lista. Lembre que o WhatsApp permite no máximo 3 chats fixados ao mesmo tempo, tentar um quarto retorna erro. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/pinChat/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "pin": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/pinChat/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", pin: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/pinChat/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "pin": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "pin": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/pinChat/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Desfixar Com `pin: false`, remove a fixação e o chat volta a ordenar normalmente por atividade. Libera espaço no limite de 3 fixados. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/pinChat/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "pin": false }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/pinChat/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", pin: false }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/pinChat/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "pin": False } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "pin": false }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/pinChat/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta confirma a operação com `chat_jid` (JID resolvido a partir do `number`) e `pinned` refletindo o estado final. A `message` muda conforme o valor de `pin`: `"Chat pinned successfully"` ou `"Chat unpinned successfully"`. ```json 200 OK { "success": true, "message": "Chat pinned successfully", "chat_jid": "5511999999999@s.whatsapp.net", "pinned": true } ``` ## Parâmetros de rota Nome da instância. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `Content-Type` | sim | `application/json` |, | | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Request body Telefone, JID privado (`...@s.whatsapp.net` ou `...@lid`), JID de grupo (`...@g.us`) ou newsletter. `true` fixa, `false` desfixa. ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` |, | | 400 | `Invalid request body: <...>` | JSON malformado. | | 400 | `Number is required` |, | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 503 | `Instance is not connected to WhatsApp` |, | ```json Erro 400 { "success": false, "error": { "message": "Number is required" } } ``` ## Relacionados `POST /api/chat/archive/:instance` `POST /api/chat/favorite/:instance` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Fixa (`pin: true`) ou desfixa (`pin: false`) uma mensagem **dentro** de um chat. O chat, o autor da mensagem e a direção (`fromMe`) são resolvidos automaticamente a partir do `messageId`, você só precisa informar a mensagem. Ao contrário de **favoritar** (que é privado e silencioso), fixar uma mensagem é uma ação **visível para todos** os participantes da conversa: o WhatsApp exibe "fixou uma mensagem". O protocolo só oferece "fixar para todos", não existe fixar apenas para você. A mensagem precisa existir no banco da instância (ter sido recebida/enviada por ela). Caso contrário a API retorna `message with ID ... not found`. ## Exemplos ### Fixar Com `pin: true`, a mensagem é fixada no topo do chat pelo tempo definido em `duration`. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/pinMessage/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "pin": true, "messageId": "3EB0XXXXXXXXXXXXXXXX", "duration": "7d" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/pinMessage/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ pin: true, messageId: "3EB0XXXXXXXXXXXXXXXX", duration: "7d" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/pinMessage/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "pin": True, "messageId": "3EB0XXXXXXXXXXXXXXXX", "duration": "7d" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "pin": true, "messageId": "3EB0XXXXXXXXXXXXXXXX", "duration": "7d" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/pinMessage/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Desfixar Com `pin: false`, a mensagem é desfixada. O campo `duration` é ignorado. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/pinMessage/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "pin": false, "messageId": "3EB0XXXXXXXXXXXXXXXX" }' ``` ## Resposta de sucesso `chat_jid` é resolvido a partir do `messageId`, `pinned` reflete o estado final e `duration` é ecoado apenas quando se fixa. A `message` muda conforme o `pin`: `"Message pinned successfully"` ou `"Message unpinned successfully"`. ```json 200 OK { "success": true, "message": "Message pinned successfully", "chat_jid": "5511999999999@s.whatsapp.net", "message_id": "3EB0XXXXXXXXXXXXXXXX", "pinned": true, "duration": "7d" } ``` ## Parâmetros de rota Nome da instância. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `Content-Type` | sim | `application/json` |, | | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Request body `true` fixa, `false` desfixa. ID da mensagem a fixar/desfixar. O chat e o autor são resolvidos automaticamente a partir dele. Por quanto tempo a mensagem fica fixada: `"24h"`, `"7d"` ou `"30d"`. Usado apenas quando `pin: true`; ignorado ao desfixar. ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` |, | | 400 | `Invalid request body: <...>` | JSON malformado. | | 400 | `messageId is required` |, | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 500 | `message with ID <...> not found` | Mensagem não existe no banco da instância. | | 500 | `invalid duration "<...>": use "24h", "7d" or "30d"` | Valor de `duration` inválido. | | 500 | `WhatsApp client is not connected` | Instância desconectada. | ```json Erro 400 { "success": false, "error": { "message": "messageId is required" } } ``` ## Relacionados `POST /api/chat/pinChat/:instance` `POST /api/chat/favorite/:instance` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Marca como favorito **um chat** (informando `number`) **ou** **uma mensagem específica** (informando `messageId`). Pelo menos um dos dois deve estar presente. Quando ambos são informados, **`messageId` tem prioridade**, o favorito será registrado na mensagem. ## Exemplos ### Favoritar chat Marca a conversa inteira como favorita informando apenas `number`. Usa-se para destacar contatos importantes na lista, sem amarrar o favorito a uma mensagem específica. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/favorite/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "favorite": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/favorite/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", favorite: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/favorite/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "favorite": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "favorite": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/favorite/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Favoritar mensagem Favorita uma mensagem específica via `messageId`. Quando ambos os campos são enviados, `messageId` tem prioridade e o favorito é registrado na mensagem em vez do chat. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/favorite/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "messageId": "3EB08FCF27E532F1B0F5", "favorite": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/favorite/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ messageId: "3EB08FCF27E532F1B0F5", favorite: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/favorite/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "messageId": "3EB08FCF27E532F1B0F5", "favorite": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "messageId": "3EB08FCF27E532F1B0F5", "favorite": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/favorite/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Remover favorito Com `favorite: false`, desfaz o favorito previamente registrado no chat ou na mensagem (a depender de qual campo foi informado, mesmo padrão de prioridade dos exemplos anteriores). ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/favorite/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "messageId": "3EB08FCF27E532F1B0F5", "favorite": false }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/favorite/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ messageId: "3EB08FCF27E532F1B0F5", favorite: false }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/favorite/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "messageId": "3EB08FCF27E532F1B0F5", "favorite": False } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "messageId": "3EB08FCF27E532F1B0F5", "favorite": false }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/favorite/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O campo `type` indica o que foi marcado: `"chat"` quando você favoritou a conversa inteira, ou `"message"` quando passou um `messageId` (nesse caso `message_id` aparece preenchido). `chat_jid` é sempre devolvido. `favorite` reflete o estado final. ```json 200 OK { "success": true, "message": "Message favoritada successfully", "chat_jid": "5511999999999@s.whatsapp.net", "message_id": "3EB08FCF27E532F1B0F5", "favorite": true, "type": "message" } ``` ## Parâmetros de rota Nome da instância. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `Content-Type` | sim | `application/json` |, | | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Request body Telefone ou JID. Use para favoritar o **chat inteiro**. ID da mensagem. Use para favoritar uma **mensagem específica**. Tem prioridade sobre `number`. `true` favorita, `false` remove o favorito. É obrigatório enviar **pelo menos um** entre `number` e `messageId`. ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` |, | | 400 | `Invalid request body: <...>` | JSON malformado. | | 400 | `Either number or messageId is required` | Nenhum dos dois informado. | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 404 | `Message not found` | `messageId` inexistente. | | 503 | `Instance is not connected to WhatsApp` |, | ```json Erro 400 { "success": false, "error": { "message": "Either number or messageId is required" } } ``` ## Relacionados `POST /api/chat/pinChat/:instance` Recuperar uma mensagem favoritada. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Silencia (`mute: true`) ou desativa o silenciamento (`mute: false`) de um chat. O parâmetro `duration` aceita vários formatos legíveis para configurar a janela. ## Exemplos ### Silenciar 8h Silencia o chat por uma janela de 8 horas com `duration: "8h"`. Ao fim do período, as notificações voltam automaticamente sem necessidade de outra chamada. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/mute/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "mute": true, "duration": "8h" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/mute/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", mute: true, duration: "8h" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/mute/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "mute": True, "duration": "8h" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "mute": true, "duration": "8h" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/mute/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Silenciar permanente Com `duration: "always"` (aceita `"forever"` e `"permanent"` também), o chat fica silenciado indefinidamente, até que você envie outra chamada com `mute: false`. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/mute/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "mute": true, "duration": "always" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/mute/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", mute: true, duration: "always" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/mute/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "mute": True, "duration": "always" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "mute": true, "duration": "always" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/mute/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Desativar Com `mute: false`, remove qualquer silenciamento ativo no chat e volta a entregar notificações normalmente. O campo `duration` é ignorado nesta variante. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/mute/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "mute": false }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/mute/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", mute: false }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/mute/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "mute": False } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "mute": false }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/mute/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso `muted` indica o estado final e `duration` vem em segundos (`28800` = 8h, `604800` = 1 semana, `0` = permanente ou desativado). A `message` muda conforme a operação: `"Chat muted permanently"`, `"Chat muted for 8 hours"`, `"Chat muted for 1 week"` ou `"Chat notifications unmuted successfully"`. ```json 200 OK { "success": true, "message": "Chat muted for 8 hours", "chat_jid": "5511999999999@s.whatsapp.net", "muted": true, "duration": 28800 } ``` ## Parâmetros de rota Nome da instância. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `Content-Type` | sim | `application/json` |, | | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Request body Telefone, JID privado (`...@s.whatsapp.net` ou `...@lid`), JID de grupo (`...@g.us`) ou newsletter. `true` silencia, `false` desfaz. Tempo de silenciamento. Aceita os formatos da tabela abaixo. Ignorado quando `mute=false`. ### Valores aceitos para `duration` | Valor | Significado | |-------|-------------| | `"8h"` ou `"8 hours"` | 8 horas | | `"1w"`, `"7d"` ou `"1 week"` | 1 semana | | `"always"`, `"forever"`, `"permanent"` | Sem expiração | | (vazio) | Sem expiração | ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` |, | | 400 | `Invalid request body: <...>` | JSON malformado. | | 400 | `Number is required` |, | | 400 | `Invalid duration: <...>` | Formato não reconhecido. | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 503 | `Instance is not connected to WhatsApp` |, | ```json Erro 400 { "success": false, "error": { "message": "Invalid duration: 5y" } } ``` ## Relacionados `POST /api/chat/archive/:instance` `POST /api/chat/block/:instance` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Arquiva (`archive: true`) ou desarquiva (`archive: false`) um chat. A operação é propagada para o WhatsApp via app state, pode levar segundos até refletir nos demais dispositivos vinculados. ## Exemplos ### Arquivar Move o chat para a pasta de arquivados (`archive: true`), removendo-o da lista principal sem apagar o histórico. Sincroniza com os demais dispositivos vinculados via app state. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/archive/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "archive": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/archive/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", archive: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/archive/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "archive": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "archive": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/archive/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Desarquivar Tira o chat da pasta de arquivados (`archive: false`) e o traz de volta para a lista principal. A operação é propagada para todos os dispositivos vinculados. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/archive/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "archive": false }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/archive/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", archive: false }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/archive/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "archive": False } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "archive": false }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/archive/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta confirma a operação com `chat_jid` (JID resolvido a partir do `number`) e `archived` refletindo o estado final. A `message` muda conforme o valor de `archive`: `"Chat archived successfully"` ou `"Chat unarchived successfully"`. ```json 200 OK { "success": true, "message": "Chat archived successfully", "chat_jid": "5511999999999@s.whatsapp.net", "archived": true } ``` ## Parâmetros de rota Nome da instância. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `Content-Type` | sim | `application/json` |, | | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Request body Telefone, JID privado (`...@s.whatsapp.net` ou `...@lid`), JID de grupo (`...@g.us`) ou newsletter. `true` arquiva, `false` desarquiva. ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` |, | | 400 | `Invalid request body: <...>` | JSON malformado. | | 400 | `Number is required` |, | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 503 | `Instance is not connected to WhatsApp` |, | ```json Erro 400 { "success": false, "error": { "message": "Number is required" } } ``` ## Relacionados `POST /api/chat/pinChat/:instance` `POST /api/chat/pinMessage/:instance` `POST /api/chat/mute/:instance` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim Remove um chat (conversa) da sua lista no WhatsApp. Opcionalmente apaga também os arquivos de mídia salvos localmente. **Operação local apenas.** Outros participantes (em grupos) ou o destinatário (em DMs) continuam vendo o histórico do lado deles. Para sair de um grupo, use [`DELETE /api/group/leave`](/pt/api/groups/leave). ## Exemplos ### Manter mídia local Remove o chat da lista do WhatsApp mas preserva os arquivos de mídia armazenados localmente (S3/disco). Útil quando você ainda quer manter os anexos para auditoria ou reprocessamento. ```bash cURL curl -X DELETE "https://ryzeapi.cloud/api/chat/deleteChat/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"number": "5511999999999"}' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/deleteChat/${process.env.Instance_Name}`, { method: "DELETE", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999" }) }); ``` ```python Python import os, requests requests.delete( f"https://ryzeapi.cloud/api/chat/deleteChat/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={"number": "5511999999999"} ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{"number": "5511999999999"}`) req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/chat/deleteChat/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Apagar mídia também Com `deleteMedia: true`, além de remover o chat o servidor também apaga os arquivos de mídia salvos localmente para essa conversa. Operação irreversível do lado da RyzeAPI. ```bash cURL curl -X DELETE "https://ryzeapi.cloud/api/chat/deleteChat/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "deleteMedia": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/deleteChat/${process.env.Instance_Name}`, { method: "DELETE", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", deleteMedia: true }) }); ``` ```python Python import os, requests requests.delete( f"https://ryzeapi.cloud/api/chat/deleteChat/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "deleteMedia": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "deleteMedia": true }`) req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/chat/deleteChat/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Chat deleted successfully (including media)", "chat_jid": "5511999999999@s.whatsapp.net", "delete_media": true } ``` A `message` muda conforme `deleteMedia`: - `false` → `"Chat deleted successfully"` - `true` → `"Chat deleted successfully (including media)"` ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. ## Request body Número (`5511999999999`) ou JID (`5511999999999@s.whatsapp.net`, `...@lid`, `120363...@g.us`) do chat. Se `true`, também remove os arquivos de mídia armazenados localmente para esse chat. ## Notas - O chat some da sua lista em todos os dispositivos vinculados (sincroniza via AppState). - Em grupos, apagar o chat **não** sai do grupo, o link com o grupo permanece. - O WhatsApp do celular tem storage independente; o `deleteMedia` aqui afeta apenas as mídias salvas pela RyzeAPI no S3/disco. ## Respostas de erro | HTTP | `error.message` | Quando | |------|-----------------|--------| | 400 | `Invalid request body` | JSON malformado. | | 400 | `Number is required` | Campo ausente. | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 503 | `Instance is not connected to WhatsApp` | Sem sessão ativa. | **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Cria uma etiqueta na instância. O `id` é alocado pela API (sequencial) e depois propagado ao WhatsApp via app state, pode levar 1 a 2 segundos para aparecer no celular. `color` é opcional (default `0`) e aceita um inteiro entre 0 e 10, que corresponde à paleta nativa do WhatsApp Business. ## Exemplos ### Mínimo Cria a etiqueta apenas com `name`, sem cor. O servidor atribui `color: 0` por padrão e gera um `id` sequencial retornado na resposta. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/tag/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"name":"Premium"}' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/tag/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ name: "Premium" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/tag/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={"name": "Premium"} ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{"name":"Premium"}`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/tag/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Com cor Cria a etiqueta "VIP" usando o tom 5 da paleta do WhatsApp Business via `color: 5` (faixa aceita: 0 a 10), permitindo diferenciar etiquetas visualmente no celular. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/tag/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"name":"VIP","color":5}' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/tag/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ name: "VIP", color: 5 }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/tag/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={"name": "VIP", "color": 5} ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{"name":"VIP","color":5}`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/tag/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O objeto `tag` traz o `id` gerado pelo WhatsApp (use-o em `tags-atribuir`/`tags-desatribuir`), o `name` salvo, a `color` aplicada (`0` quando o cliente não enviou ou enviou fora da faixa 0–10) e o `type` da etiqueta. `deleted` permanece `false` em criações. ```json 200 OK { "success": true, "message": "Tag created successfully", "tag": { "id": "3", "name": "VIP", "color": 5, "type": "CUSTOM", "deleted": false } } ``` ## Parâmetros de rota Nome da instância. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `Content-Type` | sim | `application/json` |, | | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Request body Nome da etiqueta. Não pode estar vazio após `TrimSpace`. Cor da etiqueta. Aceita inteiros entre `0` e `10`, cada índice mapeia para uma cor da paleta nativa do WhatsApp Business: | `color` | Hex | Cor | |---------|-----|-----| | `0` | `#ff9485` | Vermelho coral | | `1` | `#64c4ff` | Azul céu | | `2` | `#ffd429` | Amarelo dourado | | `3` | `#dfaef0` | Lilás claro | | `4` | `#99b6c1` | Cinza azulado | | `5` | `#55ccb3` | Verde menta | | `6` | `#ff9dff` | Rosa pink | | `7` | `#d3a91d` | Mostarda | | `8` | `#6d7cce` | Azul violeta | | `9` | `#d7e752` | Verde limão | | `10` | `#00d0e2` | Ciano | Os valores acima refletem a paleta atual do WhatsApp Business (pode mudar em versões futuras do app, o índice é estável, o tom em si é definido pelo cliente WhatsApp). Valores fora da faixa `0`–`10` são silenciosamente normalizados para `0`. ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` | `:instance` vazio. | | 400 | `Invalid request body: <...>` | JSON malformado. | | 400 | `Tag name is required` | `name` vazio. | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 503 | `Instance is not connected to WhatsApp` |, | ```json Erro 400 { "success": false, "error": { "message": "Tag name is required" } } ``` ## Relacionados Conferir o `id` recém-criado. Aplicar a etiqueta após criar. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna todas as etiquetas conhecidas pela instância, incluindo as criadas via API e as importadas via app state do WhatsApp. Cada etiqueta carrega um `id`, `name`, `color` (paleta nativa do WhatsApp), `type` e flag `deleted`. `color` é um inteiro entre **0 e 10** que mapeia para as cores oficiais do WhatsApp Business. Tags marcadas com `deleted=true` ainda aparecem na listagem até o sync remover de vez. ## Exemplo ```bash cURL curl -X GET "https://ryzeapi.cloud/api/chat/tag/$Instance_Name" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/tag/${process.env.Instance_Name}`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/chat/tag/{os.environ['Instance_Name']}", headers={"token": os.environ["Token_Instance"]} ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/chat/tag/"+os.Getenv("Instance_Name"), nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso `tags` é o array de etiquetas conhecidas pela conta WhatsApp Business; `total` ecoa o tamanho. Cada entrada traz `id` (use em `tags-atribuir`/`tags-desatribuir`/`tags-deletar`), `name`, `color` (0–10) e `type`. Etiquetas marcadas com `deleted: true` representam remoções pendentes de propagação. ```json 200 OK { "success": true, "message": "Tags retrieved successfully", "tags": [ { "id": "1", "name": "Important", "color": 0, "type": "CUSTOM", "deleted": false }, { "id": "2", "name": "VIP", "color": 5, "type": "CUSTOM", "deleted": false } ], "total": 2 } ``` ## Parâmetros de rota Nome da instância. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` | `:instance` vazio. | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 503 | `Instance is not connected to WhatsApp` |, | ```json Erro 401 { "success": false, "error": { "message": "Invalid token" } } ``` ## Relacionados `POST /api/chat/tag/:instance` `POST /api/chat/assignTag/:instance` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Atribui uma etiqueta existente a um chat. Se a etiqueta já estiver presente no chat, a operação é tratada como sucesso (idempotente). A mudança é propagada ao WhatsApp via app state. ## Exemplo ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/assignTag/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "tagId": "2" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/assignTag/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", tagId: "2" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/assignTag/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "tagId": "2" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "tagId": "2" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/assignTag/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta confirma a atribuição ecoando o `tag_id` aplicado. O `chat_jid` não é incluído quando o handler delega apenas para o serviço (campos `omitempty`); confie em `success`/`message` para o resultado da operação. ```json 200 OK { "success": true, "message": "Tag assigned successfully", "tag_id": "2" } ``` ## Parâmetros de rota Nome da instância. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `Content-Type` | sim | `application/json` |, | | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Request body Telefone (`5511999999999`), JID privado (`...@s.whatsapp.net` ou `...@lid`), JID de grupo (`...@g.us`) ou newsletter. ID da etiqueta a atribuir. ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` |, | | 400 | `Invalid request body: <...>` | JSON malformado. | | 400 | `Number is required` |, | | 400 | `tagId is required` |, | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 404 | `Tag not found` | `tagId` inexistente. | | 503 | `Instance is not connected to WhatsApp` |, | ```json Erro 404 { "success": false, "error": { "message": "Tag not found" } } ``` ## Relacionados Remover a etiqueta do chat. Listar todos os chats com a etiqueta. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna chats que possuem ao menos uma das etiquetas informadas. Você pode filtrar por **IDs**, **nomes** (case-insensitive) ou retornar todos os chats que tenham qualquer etiqueta. Use `refresh=true` para forçar uma sincronização com o WhatsApp antes de listar, útil quando uma etiqueta foi alterada no celular e você quer o dado mais recente. ## Exemplos ### Por IDs Filtra os chats que possuem pelo menos uma das etiquetas listadas em `?labelIds=1,2`. Útil quando você já conhece os IDs vindos de [`/tags-listar`](/pt/api/chat/tags-list). ```bash cURL curl -X GET "https://ryzeapi.cloud/api/chat/contactsByLabel/$Instance_Name?labelIds=1,2" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/contactsByLabel/${process.env.Instance_Name}?labelIds=1,2`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/chat/contactsByLabel/{os.environ['Instance_Name']}?labelIds=1,2", headers={"token": os.environ["Token_Instance"]} ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/chat/contactsByLabel/"+os.Getenv("Instance_Name")+"?labelIds=1,2", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Por nomes (com refresh) Filtra por nome via `?labelNames=VIP,Suporte` (case-insensitive) e adiciona `refresh=true` para forçar uma sincronização com o WhatsApp antes de listar, garantindo que mudanças feitas no celular já apareçam. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/chat/contactsByLabel/$Instance_Name?labelNames=VIP,Suporte&refresh=true" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/contactsByLabel/${process.env.Instance_Name}?labelNames=VIP,Suporte&refresh=true`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/chat/contactsByLabel/{os.environ['Instance_Name']}?labelNames=VIP,Suporte&refresh=true", headers={"token": os.environ["Token_Instance"]} ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/chat/contactsByLabel/"+os.Getenv("Instance_Name")+"?labelNames=VIP,Suporte&refresh=true", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Todos com etiquetas Sem `labelIds` nem `labelNames`, retorna todos os chats que tenham qualquer etiqueta atribuída. Útil para auditar o uso geral de labels na conta. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/chat/contactsByLabel/$Instance_Name" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/contactsByLabel/${process.env.Instance_Name}`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/chat/contactsByLabel/{os.environ['Instance_Name']}", headers={"token": os.environ["Token_Instance"]} ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/chat/contactsByLabel/"+os.Getenv("Instance_Name"), nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso `chats` é uma lista de objetos `ChatWithLabels` agrupando todas as etiquetas associadas a cada chat. Use `total_chats` para o número de chats únicos e `total_rows` para o total de pares chat-etiqueta. `refreshed` indica se o cache foi reconstruído nesta chamada (`refresh=true`); `filter_label_ids` repete os IDs efetivamente aplicados. ```json 200 OK { "success": true, "message": "Chats by label retrieved successfully", "chats": [ { "chat_jid": "5511999999999@s.whatsapp.net", "labels": [ { "id": "1", "name": "Important" } ] }, { "chat_jid": "5511988887777@s.whatsapp.net", "labels": [ { "id": "1", "name": "Important" }, { "id": "2", "name": "VIP" } ] } ], "total_chats": 2, "total_rows": 3, "refreshed": false, "filter_label_ids": ["1"] } ``` ## Parâmetros de rota Nome da instância. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Query params IDs de etiquetas separados por vírgula. Exemplo: `"1,2,3"`. Nomes de etiquetas separados por vírgula (case-insensitive). Exemplo: `"VIP,Suporte"`. Se `true`, sincroniza etiquetas com o WhatsApp antes de retornar. Sem `labelIds` nem `labelNames`, retorna **todos** os chats que possuam qualquer etiqueta. ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` | `:instance` vazio. | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 503 | `Instance is not connected to WhatsApp` | Necessário para `refresh=true`. | ```json Erro 400 { "success": false, "error": { "message": "Instance name is required" } } ``` ## Relacionados Recuperar IDs e nomes para usar no filtro. Adicionar uma etiqueta a um chat. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Remove **uma** etiqueta de um chat. Se o chat não possuir aquela etiqueta, a operação é considerada sucesso (idempotente). A etiqueta em si **não é excluída**, para excluir use [Deletar etiqueta](/pt/api/chat/tags-delete). ## Exemplo ```bash cURL curl -X DELETE "https://ryzeapi.cloud/api/chat/assignTag/$Instance_Name?number=5511999999999&tagId=2" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/assignTag/${process.env.Instance_Name}?number=5511999999999&tagId=2`, { method: "DELETE", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.delete( f"https://ryzeapi.cloud/api/chat/assignTag/{os.environ['Instance_Name']}?number=5511999999999&tagId=2", headers={"token": os.environ["Token_Instance"]} ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/chat/assignTag/"+os.Getenv("Instance_Name")+"?number=5511999999999&tagId=2", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta confirma a remoção ecoando o `tag_id`. O envelope reusa o mesmo shape de `tags-atribuir`, apenas com `message: "Tag removed successfully"`. ```json 200 OK { "success": true, "message": "Tag removed successfully", "tag_id": "2" } ``` ## Parâmetros de rota Nome da instância. ## Query params Telefone, JID privado (`...@s.whatsapp.net` ou `...@lid`), JID de grupo (`...@g.us`) ou newsletter. ID da etiqueta a remover. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` |, | | 400 | `number query parameter is required` |, | | 400 | `tagId query parameter is required` |, | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 404 | `Tag not found` |, | | 503 | `Instance is not connected to WhatsApp` |, | ```json Erro 400 { "success": false, "error": { "message": "tagId query parameter is required" } } ``` ## Relacionados Reatribuir a etiqueta a outro chat. Excluir a etiqueta da instância inteira. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Remove a etiqueta identificada por `tagId` e também desatribui automaticamente de todos os chats que a possuíam. A propagação até o WhatsApp via app state pode levar **1 a 2 segundos**. ## Exemplo ```bash cURL curl -X DELETE "https://ryzeapi.cloud/api/chat/tag/$Instance_Name?tagId=3" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/tag/${process.env.Instance_Name}?tagId=3`, { method: "DELETE", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.delete( f"https://ryzeapi.cloud/api/chat/tag/{os.environ['Instance_Name']}?tagId=3", headers={"token": os.environ["Token_Instance"]} ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/chat/tag/"+os.Getenv("Instance_Name")+"?tagId=3", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O envelope vem com `tag` contendo apenas o `id` removido e `deleted: true`, os demais campos (`name`, `color`, `type`) ficam vazios pois a etiqueta já não existe mais. Use `success` para auditar a operação. ```json 200 OK { "success": true, "message": "Tag deleted successfully", "tag": { "id": "3", "name": "", "color": 0, "type": "", "deleted": true } } ``` ## Parâmetros de rota Nome da instância. ## Query params ID da etiqueta a remover. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` | `:instance` vazio. | | 400 | `tagId query parameter is required` | `tagId` ausente. | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 404 | `Tag not found` |, | | 503 | `Instance is not connected to WhatsApp` |, | ```json Erro 400 { "success": false, "error": { "message": "tagId query parameter is required" } } ``` ## Relacionados Conferir os IDs disponíveis. Remover a etiqueta de um chat específico em vez de excluir. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna a lista de contatos sincronizados no `ContactStore` do WhatsMeow, são os contatos vindos da agenda do telefone principal: nome, push name, business name e telefone redacted. Se `?number=` for fornecido, devolve um único contato. Caso o número não esteja na agenda, ainda assim a resposta é `200 OK` com `contact.found = false`. Operação de **leitura pura** sobre o store local, não gera tráfego no WhatsApp. ## Exemplos ### Listar todos Sem query params, retorna todos os contatos sincronizados no `ContactStore` local da instância, com `total` indicando o tamanho da lista. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/chat/contacts/$Instance_Name" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/contacts/${process.env.Instance_Name}`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/chat/contacts/{os.environ['Instance_Name']}", headers={"token": os.environ["Token_Instance"]} ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/chat/contacts/"+os.Getenv("Instance_Name"), nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### Número específico Filtra por `?number=5511999999999` (telefone internacional). Retorna o objeto `contact` único, com `found=false` quando o número não está na agenda sincronizada. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/chat/contacts/$Instance_Name?number=5511999999999" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/contacts/${process.env.Instance_Name}?number=5511999999999`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/chat/contacts/{os.environ['Instance_Name']}?number=5511999999999", headers={"token": os.environ["Token_Instance"]} ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/chat/contacts/"+os.Getenv("Instance_Name")+"?number=5511999999999", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ### JID completo Aceita o JID inteiro em `?number=5511999999999@s.whatsapp.net` quando você já possui o identificador completo (vindo de webhook ou outra resposta da API), evitando montar manualmente o sufixo. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/chat/contacts/$Instance_Name?number=5511999999999@s.whatsapp.net" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/contacts/${process.env.Instance_Name}?number=5511999999999@s.whatsapp.net`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/chat/contacts/{os.environ['Instance_Name']}?number=5511999999999@s.whatsapp.net", headers={"token": os.environ["Token_Instance"]} ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/chat/contacts/"+os.Getenv("Instance_Name")+"?number=5511999999999@s.whatsapp.net", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso Sem `?number`, retorna `contacts` (array) com todos os contatos sincronizados pela agenda + `total`. Com `?number=...`, retorna apenas `contact` (objeto único). Cada item traz `jid`, `lid` (quando aplicável), os nomes disponíveis (`first_name`, `full_name`, `push_name`, `business_name`) e `redacted_phone` para casos em que só conhecemos o LID. O campo `found` indica se a entrada veio do store. ```json 200 OK { "success": true, "message": "Contacts retrieved successfully", "contacts": [ { "jid": "5511999999999@s.whatsapp.net", "lid": "", "first_name": "João", "full_name": "João Silva", "push_name": "João", "business_name": "", "redacted_phone": "+55 11 ..... 9999", "found": true }, { "jid": "5511888888888@s.whatsapp.net", "full_name": "Maria", "push_name": "Maria S.", "found": true } ], "total": 42 } ``` ## Parâmetros de rota Nome da instância (ex.: `$Instance_Name`). ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Query params Telefone internacional (`5511999999999`) ou JID (`5511999999999@s.whatsapp.net`, `...@lid`). Se fornecido, retorna apenas este contato. ## Notas e gotchas - `found=false` com `push_name` vazio e `redacted_phone` vazio normalmente significa que o número não tem WhatsApp ou nunca trocou mensagens com você. - `business_name` só é preenchido para contas WhatsApp Business verificadas. - Operações com mais de mil contatos podem demorar alguns segundos por causa do `context.WithTimeout(30s)` aplicado ao `GetAllContacts`. ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` | `:instance` vazio. | | 401 | `Invalid token` | Ver [Autenticação](/pt/guide/authentication). | | 404 | `Instance not found` | Instância não existe na conta. | | 500 | `WhatsApp client not found for instance` | Cliente desalocado. | | 500 | `WhatsApp client is not connected` | Instância desconectada. | | 500 | `contact store not available (use a store that implements ContactStore, e.g. sqlstore)` | Store sem suporte. | | 500 | `invalid number: <...>` | `?number=` malformado. | | 500 | `failed to get contacts: <...>` / `failed to get contact: <...>` | Erro interno do WhatsMeow. | ```json Erro 400 { "success": false, "error": { "message": "Instance name is required" } } ``` **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim Bloqueia ou desbloqueia um contato. A resposta inclui `blocked_count`, total de contatos bloqueados na conta após a operação. ## Exemplos ### Bloquear Adiciona o contato à lista de bloqueados (`block: true`), interrompendo o envio e recebimento de mensagens nas duas direções. O histórico anterior é preservado. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/block/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"number": "5511999999999", "block": true}' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/block/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", block: true }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/block/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={"number": "5511999999999", "block": True} ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{"number": "5511999999999", "block": true}`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/block/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Desbloquear Remove o contato da lista de bloqueados (`block: false`), liberando o tráfego de mensagens nas duas direções. A operação sincroniza automaticamente com o app do celular. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/block/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{"number": "5511999999999", "block": false}' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/block/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", block: false }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/block/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={"number": "5511999999999", "block": False} ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{"number": "5511999999999", "block": false}`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/block/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Contact blocked successfully", "contact_jid": "5511999999999@s.whatsapp.net", "blocked_count": 7, "blocked": true } ``` | Campo | Tipo | Descrição | |-------|------|-----------| | `contact_jid` | string | JID do contato afetado. | | `blocked` | boolean | Estado final (`true` bloqueado, `false` liberado). | | `blocked_count` | int | Total de contatos bloqueados na conta após a operação. | ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. ## Request body Número ou JID do contato. Apenas contatos individuais, não funciona com grupos. `true` bloqueia, `false` desbloqueia. ## Notas - Bloquear interrompe o envio/recebimento de mensagens nas duas direções; o histórico anterior permanece. - Sincroniza com o app do celular automaticamente. - Não há evento de webhook específico para block/unblock no modelo atual. ## Respostas de erro | HTTP | `error.message` | Quando | |------|-----------------|--------| | 400 | `Invalid request body` | JSON malformado. | | 400 | `Number is required` | Campo ausente. | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 503 | `Instance is not connected to WhatsApp` | Sem sessão ativa. | **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim (efêmero) Emite indicador de presença ("digitando...", "gravando áudio...", ou pausa) para um chat. Ideal para simular interação realista antes de enviar uma mensagem. ## Exemplos ### Digitando 5s Mostra o indicador "digitando..." no chat do destinatário e dispara `pause` automaticamente após 5 segundos (graças ao `duration: 5`). Ideal para preceder o envio de uma mensagem de texto. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/presence/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "state": "typing", "duration": 5 }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/presence/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", state: "typing", duration: 5 }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/presence/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "state": "typing", "duration": 5 } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "state": "typing", "duration": 5 }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/presence/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Gravando áudio Exibe "gravando áudio..." com `state: "recording"`. Sem `duration`, o indicador permanece até o WhatsApp expirá-lo naturalmente (~5–10s) ou até você enviar `pause` manualmente. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/presence/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "state": "recording" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/presence/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", state: "recording" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/presence/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "state": "recording" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "state": "recording" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/presence/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Cancelar indicador Com `state: "pause"`, encerra imediatamente qualquer indicador "digitando..." ou "gravando..." ativo no chat. Útil quando um fluxo automatizado termina antes do `duration` previsto. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/presence/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "state": "pause" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/presence/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", state: "pause" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/presence/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "state": "pause" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "state": "pause" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/presence/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Presence 'typing' sent successfully (will auto-pause after 5 seconds)", "chat_jid": "5511999999999@s.whatsapp.net", "state": "typing", "duration": 5 } ``` ```json 200 OK { "success": true, "message": "Presence 'typing' sent successfully", "chat_jid": "5511999999999@s.whatsapp.net", "state": "typing", "duration": 0 } ``` ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. ## Request body Número ou JID do chat. `typing` / `recording` / `pause` / `paused` (case-insensitive). Segundos até auto-pause (0–60). Quando >0 e `state` é `typing`/`recording`, a API envia `pause` automaticamente após esse intervalo. Ignorado em `pause`/`paused`. ## Estados aceitos | `state` | Efeito | |---------|--------| | `typing` | "digitando..." no chat do destinatário | | `recording` | "gravando áudio..." | | `pause` / `paused` | Cancela qualquer indicador atual | ## Notas - Presenças são efêmeras, não persistem em banco e o WhatsApp expira o indicador no celular do destinatário em ~5–10s mesmo sem pause explícito. - `duration > 60` é truncado para 60 (cap interno). - Em chats onde o destinatário desativou as confirmações de presença, o indicador pode não aparecer. ## Respostas de erro | HTTP | `error.message` | Quando | |------|-----------------|--------| | 400 | `Invalid request body` | JSON malformado. | | 400 | `Number is required` | Campo ausente. | | 400 | `State is required. Use 'typing', 'recording', or 'pause'` | `state` vazio. | | 400 | `Invalid state. Use 'typing', 'recording', or 'pause'` | Fora do enum. | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 503 | `Instance is not connected to WhatsApp` | Sem sessão ativa. | **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não Encaminha **uma mensagem** existente para **um destinatário**. Para múltiplos destinos, faça múltiplas chamadas. Suporta encaminhamento de: text, image, video, audio, document, sticker, contact, location.
Não suporta: reactions, polls, buttons. ## Exemplos ### Para contato Encaminha a mensagem identificada por `messageId` para um contato individual em `to` (telefone ou JID `@s.whatsapp.net`). A mensagem chega ao destinatário com a marca "Encaminhada". ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/forward/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "messageId": "3EB08FCF27E532F1B0F5", "to": "5511987654321" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/forward/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ messageId: "3EB08FCF27E532F1B0F5", to: "5511987654321" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/forward/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "messageId": "3EB08FCF27E532F1B0F5", "to": "5511987654321" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "messageId": "3EB08FCF27E532F1B0F5", "to": "5511987654321" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/forward/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Para grupo Mesma operação, mas com `to` apontando para um JID de grupo (`@g.us`). Útil para repassar avisos ou mídias recebidas em outro chat para um grupo inteiro de uma vez. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/forward/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "messageId": "3EB08FCF27E532F1B0F5", "to": "120363406289005073@g.us" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/forward/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ messageId: "3EB08FCF27E532F1B0F5", to: "120363406289005073@g.us" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/forward/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "messageId": "3EB08FCF27E532F1B0F5", "to": "120363406289005073@g.us" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "messageId": "3EB08FCF27E532F1B0F5", "to": "120363406289005073@g.us" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/forward/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Message forwarded successfully", "messageId": "3EB08FCF27E532F1B0F7", "originalId": "3EB08FCF27E532F1B0F5", "destinationJid": "5511987654321@s.whatsapp.net" } ``` | Campo | Descrição | |-------|-----------| | `messageId` | **Novo** ID gerado pelo encaminhamento. | | `originalId` | ID da mensagem original. | | `destinationJid` | JID do destinatário. | ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. ## Request body ID da mensagem original (precisa existir no banco da instância). Destino: número (`5511...`), JID privado (`...@s.whatsapp.net` ou `...@lid`), JID de grupo (`@g.us`) ou newsletter (`@newsletter`). ## Notas - Mídias muito antigas (>14 dias) podem ter os bytes encriptados expirados nos servidores do WhatsApp, nesse caso o encaminhamento falha. - A flag "Encaminhada" aparece naturalmente para o destinatário. - A nova mensagem é ingerida pelo pipeline de eventos e dispara `message.exchange` (outgoing) no webhook/WebSocket. ## Respostas de erro | HTTP | `error.message` | Quando | |------|-----------------|--------| | 400 | `Invalid request body` | JSON malformado. | | 400 | `messageId is required` ou `to is required` | Campo ausente. | | 401 | `Invalid token` |, | | 404 | `Instance not found` ou `message not found` |, | | 500 | `unsupported message type for forwarding: ` | Reação, enquete ou interativo. | | 503 | `Instance is not connected to WhatsApp` | Sem sessão ativa. |
**Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não Edita o conteúdo de uma mensagem **que você enviou**. Apenas mensagens de texto são suportadas. **Janela de edição: ~15 minutos** após o envio (limite imposto pelo WhatsApp). Mensagens mais antigas falham com erro `too old to edit`. ## Exemplo Envie o novo conteúdo no campo `content`. A mensagem original precisa ter sido enviada pela própria instância e estar dentro da janela de 15 minutos. ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chat/edit/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "messageId": "3EB08FCF27E532F1B0F5", "content": "Desculpa, quis dizer 18h" }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/edit/${process.env.Instance_Name}`, { method: "POST", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ messageId: "3EB08FCF27E532F1B0F5", content: "Desculpa, quis dizer 18h" }) }); ``` ```python Python import os, requests requests.post( f"https://ryzeapi.cloud/api/chat/edit/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "messageId": "3EB08FCF27E532F1B0F5", "content": "Desculpa, quis dizer 18h" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "messageId": "3EB08FCF27E532F1B0F5", "content": "Desculpa, quis dizer 18h" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chat/edit/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Message edited successfully", "messageId": "3EB08FCF27E532F1B0F5", "chatJid": "5511999999999@s.whatsapp.net", "oldContent": "Desculpa, quis dizer 17h", "newContent": "Desculpa, quis dizer 18h" } ``` ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. ## Request body ID da mensagem a editar. Precisa existir e ter sido enviada por você. Novo conteúdo da mensagem. ## Notas - O destinatário vê o texto novo com a tag "Editada". - A edição emite evento `message.exchange` no webhook/WebSocket com `type: "message_edit"` e `isEdit: true`. - Edição de legenda de mídia (image/video/document) não é suportada de forma confiável, use apenas em mensagens de texto puro. ## Respostas de erro | HTTP | `error.message` | Quando | |------|-----------------|--------| | 400 | `Invalid request body` | JSON malformado. | | 400 | `messageId is required` | Campo ausente. | | 400 | `content is required` | Campo `content` ausente. | | 400 | mensagem contém `too old to edit` ou `can only edit` | Fora da janela de 15 min ou mensagem não-texto. | | 401 | `Invalid token` |, | | 404 | `Instance not found` ou `message not found` |, | | 503 | `Instance is not connected to WhatsApp` | Sem sessão ativa. | **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna o snapshot persistido de uma mensagem (texto, mídia, caption, mimetype, tamanho, etc.) que já foi processada pela ingestão. Útil para reidratar uma mensagem a partir do `messageId` recebido em um webhook ou em outro endpoint da API. ## Exemplos ### Por id Recupera o snapshot da mensagem usando a query `?messageId=`. Retorna o registro completo (texto, mídia, caption, mimetype, tamanho) persistido pela ingestão. ```bash cURL curl -X GET "https://ryzeapi.cloud/api/chat/getMessage/$Instance_Name?messageId=3EB08FCF27E532F1B0F5" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/getMessage/${process.env.Instance_Name}?messageId=3EB08FCF27E532F1B0F5`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/chat/getMessage/{os.environ['Instance_Name']}?messageId=3EB08FCF27E532F1B0F5", headers={"token": os.environ["Token_Instance"]} ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/chat/getMessage/"+os.Getenv("Instance_Name")+"?messageId=3EB08FCF27E532F1B0F5", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O snapshot vem em `data` com `type`, `from`, `to`, `chat`, `timestamp`, `isGroup` e o `status` do envio. Para mensagens de texto, `content` traz o corpo; em mídias, o objeto `media` aparece com `type`, `mimeType`, `size` e `duration` (áudio/vídeo). Use `messageId` no envelope para citar essa mensagem em chamadas seguintes (favoritar, encaminhar, editar). ```json 200 OK { "success": true, "message": "Mensagem encontrada", "messageId": "3EB08FCF27E532F1B0F5", "data": { "content": "Olá!", "type": "text", "from": "5511999999999@s.whatsapp.net", "to": "5511888888888@s.whatsapp.net", "chat": "5511999999999@s.whatsapp.net", "timestamp": "2026-04-28T14:30:00Z", "isGroup": false, "status": "delivered" }, "status": "found" } ``` ## Parâmetros de rota Nome da instância. ## Query params Message ID (obrigatório). ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Message ID is required (use ?messageId=)` | `messageId` ausente. | | 404 | `Instance not found` |, | | 404 | `Message not found` | ID não existe na base. | | 503 | `Instance is not connected to WhatsApp` |, | ```json Erro 400 { "success": false, "error": { "message": "Message ID is required (use ?messageId=)" } } ``` ## Relacionados Recuperar a mídia bruta a partir do `messageId`. Conferir se foi entregue/lida. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna o **status de entrega** de uma mensagem enviada, equivalente aos checks (cinza, duplo, azul) que aparecem no WhatsApp. O status é atualizado em tempo real conforme eventos do WhatsMeow informam que a mensagem foi recebida pelo servidor, entregue ao destinatário, lida ou reproduzida. Para receber a evolução do status em tempo real, assine o webhook [`message.status`](/pt/api/events/catalog). Este endpoint é a leitura pontual do snapshot atual. ### Mapa de `status` | `status` (string) | `status_code` | Semântica | |-------------------|---------------|-----------| | `pending` | 0 | Aguardando ACK do servidor. | | `sent` | 1 | ACK do servidor (check simples). | | `delivered` | 2 | Entregue ao celular do destinatário (check duplo). | | `received` | 3 | Mensagem recebida (apenas para `direction = "received"`). | | `read` | 4 | Lida pelo destinatário (check azul). | | `played` | 5 | Áudio/vídeo reproduzido. | | `error` | -1 | Falha permanente de envio. | ## Exemplo ```bash cURL curl -X GET "https://ryzeapi.cloud/api/chat/status/$Instance_Name?messageId=3EB08FCF27E532F1B0F5" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/status/${process.env.Instance_Name}?messageId=3EB08FCF27E532F1B0F5`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/chat/status/{os.environ['Instance_Name']}?messageId=3EB08FCF27E532F1B0F5", headers={"token": os.environ["Token_Instance"]} ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/chat/status/"+os.Getenv("Instance_Name")+"?messageId=3EB08FCF27E532F1B0F5", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso `status` é o estado textual (`sent`, `received`, `delivered`, `read`, `played`, `error`, `pending`) e `status_code` traz o código numérico equivalente do WhatsMeow (0–5). `direction` distingue mensagens enviadas pela instância (`"sent"`) das recebidas (`"received"`). `timestamp` é o instante em que a mensagem trafegou. ```json 200 OK { "success": true, "message": "Message status retrieved successfully", "message_id": "3EB08FCF27E532F1B0F5", "status": "read", "status_code": 4, "direction": "sent", "chat_jid": "5511999999999@s.whatsapp.net", "timestamp": "2026-04-28T14:30:00Z" } ``` ## Parâmetros de rota Nome da instância. ## Query params ID da mensagem cujo status deseja consultar. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` |, | | 400 | `messageId query parameter is required` |, | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 404 | `Message not found` | `messageId` inexistente. | | 404 | `Message does not belong to this instance` | Mensagem pertence a outra instância. | ```json Erro 404 { "success": false, "error": { "message": "Message not found" } } ``` ## Notas e gotchas - `played` (5) só faz sentido para áudio e vídeo, mensagens de texto não chegam a esse estado. - Pode haver um delay de 1 a 3 segundos entre a ação no celular do destinatário e o status atualizado aqui. - Para acompanhar várias mensagens em tempo real, prefira o webhook [`message.status`](/pt/api/events/catalog) em vez de polling. ## Relacionados Recuperar conteúdo e metadados. Sinalizar que você já leu uma mensagem recebida. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** parcialmente Apaga uma mensagem específica em duas modalidades: - **Para todos** (`deleteForEveryone: true`), revoga no WhatsApp; aparece "Esta mensagem foi apagada" para todos. `delete_type: "revoke"`. - **Só pra mim** (`deleteForEveryone: false`, padrão), apaga apenas localmente nos seus dispositivos. Destinatários continuam vendo. `delete_type: "delete_for_me"`. A janela de revogação ("para todos") tem limite do WhatsApp: cerca de **2 dias e 12 horas** após o envio. A API sempre aceita o pedido (envia o REVOKE) e retorna sucesso, mas o cliente do destinatário ignora a revogação de mensagens mais antigas que essa janela — ou seja, sucesso na API não garante a remoção na tela do destinatário. ## Exemplos ### Para todos (revoke) Revoga a mensagem no WhatsApp com `deleteForEveryone: true`. Substitui o conteúdo por "Esta mensagem foi apagada" para todos os participantes da conversa, sujeito à janela de cerca de 2 dias e 12 horas após o envio. ```bash cURL curl -X DELETE "https://ryzeapi.cloud/api/chat/delete/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "messageId": "3EB08FCF27E532F1B0F5", "deleteForEveryone": true }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/delete/${process.env.Instance_Name}`, { method: "DELETE", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ messageId: "3EB08FCF27E532F1B0F5", deleteForEveryone: true }) }); ``` ```python Python import os, requests requests.delete( f"https://ryzeapi.cloud/api/chat/delete/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "messageId": "3EB08FCF27E532F1B0F5", "deleteForEveryone": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "messageId": "3EB08FCF27E532F1B0F5", "deleteForEveryone": true }`) req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/chat/delete/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Só pra mim Com `deleteForEveryone: false`, a mensagem some apenas dos seus dispositivos vinculados (sincroniza via AppState). O destinatário continua vendo o conteúdo original normalmente. ```bash cURL curl -X DELETE "https://ryzeapi.cloud/api/chat/delete/$Instance_Name" \ -H "token: $Token_Instance" \ -H "Content-Type: application/json" \ -d '{ "messageId": "3EB08FCF27E532F1B0F5", "deleteForEveryone": false }' ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/delete/${process.env.Instance_Name}`, { method: "DELETE", headers: { "token": process.env.Token_Instance, "Content-Type": "application/json" }, body: JSON.stringify({ messageId: "3EB08FCF27E532F1B0F5", deleteForEveryone: false }) }); ``` ```python Python import os, requests requests.delete( f"https://ryzeapi.cloud/api/chat/delete/{os.environ['Instance_Name']}", headers={ "token": os.environ["Token_Instance"], "Content-Type": "application/json" }, json={ "messageId": "3EB08FCF27E532F1B0F5", "deleteForEveryone": False } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "messageId": "3EB08FCF27E532F1B0F5", "deleteForEveryone": false }`) req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/chat/delete/"+os.Getenv("Instance_Name"), body) req.Header.Set("token", os.Getenv("Token_Instance")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "success": true, "message": "Message deleted for everyone successfully", "message_id": "3EB08FCF27E532F1B0F5", "chat_jid": "5511999999999@s.whatsapp.net", "delete_type": "revoke", "deleted_for_everyone": true } ``` ```json 200 OK { "success": true, "message": "Message deleted for me successfully", "message_id": "3EB08FCF27E532F1B0F5", "chat_jid": "5511999999999@s.whatsapp.net", "delete_type": "delete_for_me", "deleted_for_everyone": false } ``` ## Path parameters Nome da instância. ## Headers TokenAccount ou TokenInstance. ## Request body ID da mensagem a apagar. `true` revoga para todos (`delete_type: "revoke"`); `false` apaga apenas localmente (`delete_type: "delete_for_me"`). ## Notas - **Revoke** dispara evento `message.exchange` com `type: "message_revoke"` no webhook/WebSocket. - **Delete for me** sincroniza via AppState com seus outros dispositivos vinculados, mas não notifica o destinatário. - Se a operação for repetida em uma mensagem já revogada, o WhatsApp retorna erro. ## Respostas de erro | HTTP | `error.message` | Quando | |------|-----------------|--------| | 400 | `Invalid request body` | JSON malformado. | | 400 | `MessageID is required` | Campo ausente. | | 401 | `Invalid token` | Token ausente/inválido. | | 404 | `Instance not found` | Instância inexistente. | | 404 | `message not found` | `messageId` não está no banco. | | 503 | `Instance is not connected to WhatsApp` | Sem sessão ativa. | **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Lê a mídia já armazenada localmente (após a ingestão) e devolve o conteúdo em base64. Funciona para imagens, vídeos, áudios, documentos e stickers. Apenas mensagens cuja mídia já foi baixada e persistida pelo pipeline de ingestão retornam dados aqui. Caso a mídia ainda não exista no storage, a resposta é `404`. ## Exemplo ```bash cURL curl -X GET "https://ryzeapi.cloud/api/chat/base64/$Instance_Name?messageId=3EB08FCF27E532F1B0F5" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/base64/${process.env.Instance_Name}?messageId=3EB08FCF27E532F1B0F5`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/chat/base64/{os.environ['Instance_Name']}?messageId=3EB08FCF27E532F1B0F5", headers={"token": os.environ["Token_Instance"]} ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/chat/base64/"+os.Getenv("Instance_Name")+"?messageId=3EB08FCF27E532F1B0F5", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso A resposta ecoa `message_id`, traz `mime_type` e `media_type` (categoria geral: `image`, `video`, `audio`, `document`, `sticker`) e devolve em `base64` os bytes brutos da mídia já codificados, pronto para escrever em arquivo, fazer upload em outro lugar ou exibir inline. O payload pode ser grande para vídeos/áudios longos. ```json 200 OK { "success": true, "message": "Base64 recuperado com sucesso", "message_id": "3EB08FCF27E532F1B0F5", "mime_type": "image/jpeg", "media_type": "image", "base64": "iVBORw0KGgoAAAANSUhEUgAAAAEA..." } ``` ## Parâmetros de rota Nome da instância. ## Query params ID da mensagem que contém a mídia. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` |, | | 400 | `messageId query parameter is required` |, | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 404 | `Message not found or no media available` | Mídia ausente ou ainda não baixada. | | 503 | `Instance is not connected to WhatsApp` |, | ```json Erro 404 { "success": false, "error": { "message": "Message not found or no media available" } } ``` ## Relacionados Recuperar metadados antes de baixar. Conferir entrega/leitura. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna os votos de uma enquete enviada pela instância. A resposta inclui: - **Agregados** por opção (`votes[]`), nome da opção e total. - **Detalhamento** voto a voto (`voteDetails[]`), quem votou, em qual opção, quando. `optionHash` é o `SHA256(optionName)` em uppercase, usado pelo WhatsApp para anonimizar votos no protocolo. Útil para correlacionar votos brutos com suas opções. ## Exemplo ```bash cURL curl -X GET "https://ryzeapi.cloud/api/chat/poll/$Instance_Name?messageId=3EB08FCF27E532F1B0F5" \ -H "token: $Token_Instance" ``` ```javascript JavaScript await fetch(`https://ryzeapi.cloud/api/chat/poll/${process.env.Instance_Name}?messageId=3EB08FCF27E532F1B0F5`, { method: "GET", headers: { "token": process.env.Token_Instance } }); ``` ```python Python import os, requests requests.get( f"https://ryzeapi.cloud/api/chat/poll/{os.environ['Instance_Name']}?messageId=3EB08FCF27E532F1B0F5", headers={"token": os.environ["Token_Instance"]} ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/chat/poll/"+os.Getenv("Instance_Name")+"?messageId=3EB08FCF27E532F1B0F5", nil) req.Header.Set("token", os.Getenv("Token_Instance")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso O envelope traz `messageId` da enquete e o objeto `data` (`PollResult`) com a pergunta em `name`, a contagem agregada por opção em `votes` e a lista detalhada de votos em `voteDetails`. Use `votes[].optionVoteCount` para totais por opção e `voteDetails` quando precisar saber quem votou em quê. ```json 200 OK { "success": true, "message": "Poll votes retrieved successfully", "messageId": "3EB08FCF27E532F1B0F5", "data": { "messageId": "3EB08FCF27E532F1B0F5", "name": "Qual sabor?", "votes": [ { "optionName": "Chocolate", "optionVoteCount": 5 }, { "optionName": "Baunilha", "optionVoteCount": 3 } ], "voteDetails": [ { "optionHash": "A1B2C3D4E5F6...", "optionName": "Chocolate", "senderJid": "5511999999999", "voteMessageId": "msg_abc", "createdAt": "2026-04-28T10:00:00Z" } ] } } ``` ## Parâmetros de rota Nome da instância. ## Query params ID da mensagem da enquete. ## Headers | Nome | Obrigatório | Exemplo | Descrição | |------|-------------|---------|-----------| | `token` | sim (ou `Authorization`) | `a1b2c3d4-...` | TokenAccount ou TokenInstance. | ## Respostas de erro | HTTP | `error.message` | Quando ocorre | |------|-----------------|---------------| | 400 | `Instance name is required` |, | | 400 | `messageId query parameter is required` |, | | 401 | `Invalid token` |, | | 404 | `Instance not found` |, | | 404 | `Poll not found` | `messageId` não corresponde a uma enquete. | ```json Erro 404 { "success": false, "error": { "message": "Poll not found" } } ``` ## Relacionados Criar uma enquete. Recuperar a enquete original. ### Chatwoot A integração com o [Chatwoot](https://www.chatwoot.com/) é um recurso nativo da RyzeAPI. A RyzeAPI cria a inbox no Chatwoot e mantém a conexão em tempo real. Quando o módulo Chatwoot não está habilitado no servidor, **todos os endpoints do módulo retornam `503`** com a mensagem `integration gateway not configured`. ## Como funciona Ao chamar `POST /api/chatwoot/set`, a RyzeAPI cria a inbox no Chatwoot e mantém a conexão em tempo real. A inbox criada no Chatwoot passa a receber mensagens do WhatsApp via eventos da RyzeAPI. Mensagens enviadas pelo agente Chatwoot voltam para a RyzeAPI e são despachadas pelo whatsmeow. ## Endpoints de gerenciamento `POST /api/chatwoot/set/:instance`, provisiona a integração (cria inbox + abre WS). `GET /api/chatwoot/list/:instance`, leitura local enriquecida com o estado atual da integração. `DELETE /api/chatwoot/delete/:instance`, remove a integração (a inbox no Chatwoot é preservada). ## Ativação inline na criação da instância A integração pode ser ativada **junto com a criação da instância**, sem precisar chamar `set` separadamente. Basta enviar o bloco `chatwoot*` no body de [`POST /api/instance/new`](/pt/api/instance/create): ```json { "name": "suporte", "chatwootEnabled": true, "chatwootBaseUrl": "https://chatwoot.example.com", "chatwootAccountId": 5, "chatwootApiToken": "sk_live_abc123...", "chatwootInboxName": "WhatsApp - Orion" } ``` Se a ativação falhar (token errado, host inacessível, Chatwoot indisponível), a instância **continua sendo criada**, o objeto `chatwoot` retorna com `status: "error"` e `error: ""`. Você pode então chamar [`POST /api/chatwoot/set/:instance`](/pt/api/chatwoot/activate) para corrigir as credenciais sem recriar a instância. ## Detectar se o módulo Chatwoot está habilitado ```bash curl -s -o /dev/null -w "%{http_code}\n" \ "https://ryzeapi.cloud/api/chatwoot/list/qualquer-coisa" \ -H "token: $Token_Account" # 503 → módulo Chatwoot não habilitado no servidor # 404 → módulo ativo, mas instância/integração não existe # 200 → módulo ativo e integração configurada ``` ## Modelo de dados O servidor persiste cada integração na tabela `chatwoot_integrations`. O `chatwootApiToken` é encriptado at-rest com **AES-256-GCM**. Ele é exposto em plaintext em [`GET /api/chatwoot/list/:instance`](/pt/api/chatwoot/info). Esse token é retornado em texto puro e deve ser tratado como sensível. | Campo | Descrição | | ----- | --------- | | `bridge_integration_id` | ID interno da integração. | | `chatwoot_base_url` | URL da instalação Chatwoot. | | `chatwoot_account_id` | ID numérico da conta Chatwoot. | | `chatwoot_inbox_id` / `chatwoot_inbox_name` | Inbox criada no Chatwoot. | | `status` | `active` / `paused` / `error`. | | `last_error` | Última mensagem de erro da integração. | ## Próximos passos Provisione a integração com `POST /api/chatwoot/set/:instance`. Tabela de mapeamento dos status HTTP e mensagens da integração. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Ativa a integração Chatwoot para uma instância. A RyzeAPI cria a inbox no Chatwoot e mantém a conexão em tempo real para receber e enviar eventos. O `chatwootApiToken` é encriptado at-rest com **AES-256-GCM** e não é retornado por este endpoint (é exposto em plaintext apenas em [`GET /api/chatwoot/list/:instance`](/pt/api/chatwoot/info)). **Três modos de caixa de entrada**, definidos por `createInbox` e `inboxId`: - **Criar automaticamente** (padrão) — `createInbox: true` (ou omitido) e sem `inboxId`. A RyzeAPI cria uma inbox nova no Chatwoot e já aponta o webhook dela para si. - **Reusar uma inbox existente** — informe o `inboxId`. A RyzeAPI reaponta o webhook do canal API dessa inbox para si (`PATCH /inboxes/:id` com `channel.webhook_url`), preservando contatos, histórico e agentes. Ideal para migrar de outra API de WhatsApp sem trocar de caixa de entrada. - **Somente webhook** (manual) — `createInbox: false` e sem `inboxId`. A RyzeAPI apenas ativa a integração e devolve `webhook_url` na resposta; cole essa URL no campo *Webhook URL* do canal API da sua inbox no Chatwoot. O `inbox_id` é aprendido no primeiro evento recebido. Esta operação tem timeout interno de **60s**, a primeira ativação envolve criação de inbox e abertura de WebSocket, o que pode demorar dependendo da latência até o Chatwoot. ## Exemplo ```bash cURL curl -X POST "https://ryzeapi.cloud/api/chatwoot/set/suporte" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{ "chatwootBaseUrl": "https://chatwoot.example.com", "chatwootAccountId": 5, "chatwootApiToken": "sk_live_abc123...", "inboxName": "WhatsApp - Orion", "signMessages": true, "ignoreGroups": false, "startAsPending": false, "reopenResolved": true }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/chatwoot/set/suporte", { method: "POST", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ chatwootBaseUrl: "https://chatwoot.example.com", chatwootAccountId: 5, chatwootApiToken: "sk_live_abc123...", inboxName: "WhatsApp - Orion", signMessages: true, ignoreGroups: false, startAsPending: false, reopenResolved: true }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/chatwoot/set/suporte", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={ "chatwootBaseUrl": "https://chatwoot.example.com", "chatwootAccountId": 5, "chatwootApiToken": "sk_live_abc123...", "inboxName": "WhatsApp - Orion", "signMessages": True, "ignoreGroups": False, "startAsPending": False, "reopenResolved": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "chatwootBaseUrl": "https://chatwoot.example.com", "chatwootAccountId": 5, "chatwootApiToken": "sk_live_abc123...", "inboxName": "WhatsApp - Orion", "signMessages": true, "ignoreGroups": false, "startAsPending": false, "reopenResolved": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/chatwoot/set/suporte", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 201 Created { "instance": "suporte", "status": "active", "bridge_integration_id": "int_xyz789abc", "webhook_url": "https://bridge.ryzeapi.cloud/v1/chatwoot/webhook/int_xyz789abc", "message": "chatwoot integration activated" } ``` | Campo | Descrição | | ----- | --------- | | `instance` | Nome da instância onde a integração foi ativada. | | `status` | `"active"` quando a ativação foi concluída. | | `bridge_integration_id` | ID interno da integração, referência para consultas e DELETE. | | `webhook_url` | URL que o Chatwoot deve chamar (webhook do canal API). Nos modos de criação/reuso a RyzeAPI já a configura no Chatwoot; no modo **somente webhook** cole-a manualmente. | | `message` | Mensagem fixa de confirmação. | ## Parâmetros de rota Nome da instância (ex.: `suporte`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body URL da instalação Chatwoot (RFC 3986). O `/` final é removido. Ex.: `https://chatwoot.example.com`. ID numérico da conta Chatwoot. Precisa ser maior que `0`. API token (`access_token`) do agente Chatwoot. Encriptado at-rest com AES-256-GCM. Não é retornado por este endpoint, mas é exposto em plaintext em [`GET /api/chatwoot/list/:instance`](/pt/api/chatwoot/info). Nome do inbox a ser criado no Chatwoot (usado apenas quando uma inbox nova é criada). Controla a criação automática da inbox. `true` (ou ausente) e sem `inboxId` cria uma inbox nova. `false` e sem `inboxId` ativa o modo **somente webhook**: nenhuma inbox é criada e a resposta traz `webhook_url` para você colar no Chatwoot. ID de uma inbox **já existente** no Chatwoot para reusar. Quando informado (precisa ser `> 0`), a RyzeAPI reaponta o webhook dessa inbox em vez de criar uma nova, e tem precedência sobre `createInbox`. Se `true`, prefixa mensagens enviadas pela RyzeAPI com a assinatura do agente Chatwoot. Se `true`, eventos de grupo não são roteados para o Chatwoot. Se `true`, conversas novas começam como `pending` (em vez de `open`). Se `true`, mensagens novas em conversas marcadas como `resolved` reabrem-nas automaticamente. ## Erros A API tenta classificar a falha da integração para devolver um status HTTP útil + mensagem **acionável**. O texto bruto (com a causa raiz vinda do Chatwoot) é incluído após `Detail:`. | HTTP | `error.message` | Causa | | :--: | --------------- | ----- | | 400 | `Chatwoot account or endpoint not found - verify chatwootBaseUrl (...) and chatwootAccountId (...). Detail: ...` | Account ID errado, URL inválida ou Chatwoot devolveu 404/422. | | 400 | `Chatwoot rejected the request as invalid ... Detail: ...` | Erro 422 do Chatwoot (validação do payload). | | 400 | `invalid body: ...` | Body malformado ou campos obrigatórios ausentes. | | 401 | `Chatwoot rejected the API token - verify chatwootApiToken. Detail: ...` | Token Chatwoot inválido (Chatwoot devolveu `HTTP 401: Invalid Access Token`). | | 403 | `Chatwoot denied the request - verify the API token has admin scope on account . Detail: ...` | Token sem escopo de admin na conta. | | 404 | `instance not found` | Instância não existe na RyzeAPI. | | 500 | `chatwoot integration failed: persist integration: ...` | Falha de persistência local (DB). | | 502 | `Chatwoot is unreachable at - verify chatwootBaseUrl and that the host is reachable from the server. Detail: ...` | DNS, `connection refused`, `i/o timeout`, `dial tcp` ou Chatwoot retornou 5xx. | | 503 | `integration gateway not configured` | Módulo Chatwoot não habilitado no servidor. | Use o **status HTTP** para reagir programaticamente (`401` → corrigir token, `502` → reverificar URL/conectividade) e exiba `error.message` ao usuário final, ela já vem com a próxima ação sugerida. ### Exemplos de payload de erro Token inválido: ```json { "success": false, "error": { "message": "Chatwoot rejected the API token - verify chatwootApiToken. Detail: https://chatwoot.example.com/api/v1/accounts/5/inboxes HTTP 401: {\"error\":\"Invalid Access Token\"}" } } ``` Host inacessível: ```json { "success": false, "error": { "message": "Chatwoot is unreachable at https://chatwoot.example.com - verify chatwootBaseUrl and that the host is reachable from the server. Detail: Post \"https://chatwoot.example.com/api/v1/accounts/5/inboxes\": dial tcp [::1]:443: connect: connection refused" } } ``` ## Próximo Confira o `status` e o `last_error` enriquecidos com o estado atual da integração. Remova a integração (a inbox no Chatwoot é preservada). **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna o status local da integração Chatwoot e tenta enriquecê-lo com o estado atual da integração (best-effort). Esta resposta **inclui** o `chatwoot_api_token` em plaintext e os flags de comportamento (`sign_messages`, `ignore_groups`, `start_as_pending`, `reopen_resolved`). ## Exemplo ```bash cURL curl -X GET "https://ryzeapi.cloud/api/chatwoot/list/suporte" \ -H "token: $Token_Account" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/chatwoot/list/suporte", { method: "GET", headers: { "token": process.env.Token_Account } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/chatwoot/list/suporte", headers={ "token": os.environ["Token_Account"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/chatwoot/list/suporte", nil) req.Header.Set("token", os.Getenv("Token_Account")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "instance_name": "suporte", "status": "active", "bridge_integration_id": "int_xyz789abc", "chatwoot_base_url": "https://chatwoot.example.com", "chatwoot_account_id": 5, "chatwoot_api_token": "sk_live_abc123...", "chatwoot_inbox_id": 42, "chatwoot_inbox_name": "WhatsApp - Orion", "webhook_url": "https://bridge.ryzeapi.cloud/v1/chatwoot/webhook/int_xyz789abc", "last_error": "", "created_at": "2026-04-20T10:15:30Z", "sign_messages": true, "ignore_groups": false, "start_as_pending": false, "reopen_resolved": true } ``` | Campo | Descrição | | ----- | --------- | | `instance_name` | Nome da instância. | | `status` | `active` / `paused` / `error`. Quando disponível, este valor reflete o estado atual da integração. | | `bridge_integration_id` | ID interno da integração no momento do `set`. | | `chatwoot_base_url` | URL da instalação Chatwoot. | | `chatwoot_account_id` | ID numérico da conta Chatwoot. | | `chatwoot_api_token` | API token (`access_token`) da conta Chatwoot, em **plaintext**. Vem da linha local descriptografada, veja o aviso abaixo. | | `chatwoot_inbox_id` | ID do inbox criado no Chatwoot (preenchido após o primeiro `list` que conseguir consultar o estado da integração). | | `chatwoot_inbox_name` | Nome do inbox. | | `webhook_url` | URL do webhook (canal API) que o Chatwoot deve chamar para esta integração. Presente quando a integração está acessível. Útil no modo *somente webhook* para colar no Chatwoot. | | `last_error` | Última mensagem de erro da integração. Vazio quando saudável. | | `created_at` | Timestamp RFC 3339 da criação da integração. | | `sign_messages` | Prefixa cada mensagem enviada por um agente com `*Nome*:` (negrito do WhatsApp). | | `ignore_groups` | Não roteia eventos de grupos para o Chatwoot. | | `start_as_pending` | Cria novas conversas como `pending` em vez de `open`. | | `reopen_resolved` | Reabre uma conversa `resolved` ao chegar nova mensagem, em vez de criar uma nova. | O `chatwoot_api_token` é retornado em **plaintext** nesta resposta (vem da linha local descriptografada; fica encriptado at-rest). Esse token normalmente tem acesso amplo à conta Chatwoot, então trate a resposta como sensível: evite registrá-la em logs ou cache no cliente. Os flags `sign_messages` / `ignore_groups` / `start_as_pending` / `reopen_resolved` são **persistidos localmente** e refletem o último `set`. São sempre retornados, inclusive como `false`. ## Parâmetros de rota Nome da instância (ex.: `suporte`). ## Headers `TokenAccount` ou `TokenInstance`. ## Comportamento Consulta a tabela `chatwoot_integrations`. Esta etapa é rápida e sempre funciona. Consulta o estado atual da integração com timeout de **10s**. Em caso de falha de rede, segue com os dados locais. Quando disponível, `status` e `last_error` são substituídos pelos valores atuais. Se a integração devolveu um `inbox_id` e o local ainda está em `0`, atualiza o banco (`SetInboxID`), útil para casos de eventual consistency logo após o `set`. Quando o estado remoto da integração ainda não está disponível, é tratado como **eventual consistency** (não é erro): seguimos com os dados locais. ## Erros | HTTP | `error.message` | | :--: | --------------- | | 404 | `instance not found` | | 404 | `no chatwoot integration for this instance` | | 503 | `integration gateway not configured` | Se `last_error` está não-vazio, inspecione a mensagem, geralmente indica que o Chatwoot derrubou a sessão (token rotacionado, inbox removido manualmente, etc.). Reativar com [`POST /api/chatwoot/set/:instance`](/pt/api/chatwoot/activate) costuma resolver. ## Próximo Use `POST /api/chatwoot/set/:instance` para corrigir credenciais ou flags. Remova a integração. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Desativa a integração Chatwoot da instância. A RyzeAPI encerra a integração e a conexão em tempo real e remove o registro local. **A inbox no Chatwoot não é deletada**: ela apenas perde a conexão com a instância. A inbox no Chatwoot **não é deletada**: suas conversas e mensagens são preservadas. Ela apenas perde a conexão com a instância. Se você reconectar o Chatwoot depois, uma **nova** inbox será criada. ## Exemplo ```bash cURL curl -X DELETE "https://ryzeapi.cloud/api/chatwoot/delete/suporte" \ -H "token: $Token_Account" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/chatwoot/delete/suporte", { method: "DELETE", headers: { "token": process.env.Token_Account } }); ``` ```python Python import os, requests requests.delete( "https://ryzeapi.cloud/api/chatwoot/delete/suporte", headers={ "token": os.environ["Token_Account"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/chatwoot/delete/suporte", nil) req.Header.Set("token", os.Getenv("Token_Account")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "message": "chatwoot integration deactivated" } ``` ## Parâmetros de rota Nome da instância (ex.: `suporte`). ## Headers `TokenAccount` ou `TokenInstance`. ## Comportamento Lê o `bridge_integration_id` da tabela `chatwoot_integrations`. Se não existir, devolve `404`. A RyzeAPI encerra a integração. A inbox no Chatwoot **é preservada** (perde a conexão com a instância, mas mantém as mensagens). - **Integração já removida** → tratado como sucesso (não falha). - **Outros erros** → log warning no servidor e segue para a próxima etapa. Apaga o registro da `chatwoot_integrations` na RyzeAPI. A operação é considerada bem-sucedida quando esta etapa termina. A inbox no Chatwoot **não é removida**: ela só perde a conexão com a instância. Se você reativar a integração depois (`POST /api/chatwoot/set/:instance`), uma **nova** inbox será criada. ## Erros | HTTP | `error.message` | | :--: | --------------- | | 404 | `instance not found` | | 404 | `no chatwoot integration to delete` | | 503 | `integration gateway not configured` | ## Próximo Recrie a integração com `POST /api/chatwoot/set/:instance`. Volte ao funcionamento do módulo Chatwoot. ### Typebot A RyzeAPI integra com o [Typebot](https://typebot.io). Você cadastra **bots conversacionais** que conduzem a conversa no WhatsApp: a mensagem recebida é enviada ao fluxo do Typebot (`startChat` / `continueChat`) e as respostas voltam para o WhatsApp, incluindo texto, mídia e botões/listas nativos. ## Como funciona 1. Você cadastra um bot com `POST /api/typebot/set/:instance` (ou inline, na criação da instância). 2. A cada mensagem recebida no WhatsApp, a RyzeAPI escolhe o bot pelo **trigger** e envia o texto ao fluxo do Typebot. 3. As respostas do fluxo voltam para a RyzeAPI e são entregues no WhatsApp. 4. A conversa em andamento **não se perde** ao reiniciar o serviço. Uma instância pode ter **vários bots** ao mesmo tempo, roteados por trigger. Um bot `all` funciona como catch-all (qualquer mensagem inicia); bots `keyword` só disparam quando a mensagem casa com o operador/valor configurado. ## Botões e Pix pela marcação no texto Nativamente o Typebot oferece só o botão de resposta (o nó de opções). Para enviar os outros botões do WhatsApp (link, ligação, copiar) e a mensagem de Pix, escreva uma marcação dentro de um **balão de texto** comum do seu fluxo. A RyzeAPI reconhece a marcação, retira ela do texto e envia a mensagem interativa correspondente; o texto que sobra no balão vira o corpo da mensagem. | Marcação | Vira | | -------- | ---- | | `[reply\|Texto]` | Botão de resposta. Ao tocar, o fluxo recebe `Texto` como se a pessoa tivesse digitado, então dá para ramificar por ele. | | `[url:https://exemplo.com\|Texto]` | Botão que abre um link. | | `[call:5511999999999\|Texto]` | Botão que liga para o número. | | `[copy:CODIGO\|Texto]` | Botão que copia o código. | | `[pix:Nome;Chave;Tipo\|Texto]` | Mensagem de Pix. `Tipo` é um de `CPF`, `CNPJ`, `EMAIL`, `PHONE`, `RANDOM`. | Botões de resposta com um texto de apoio: ``` Clique no botão abaixo para saber mais: [reply|Saber mais] [reply|Encerrar] ``` Vários botões de link: ``` Confira nossos produtos: [url:https://exemplo.com/1|Produto 1] [url:https://exemplo.com/2|Produto 2] [url:https://exemplo.com/3|Produto 3] ``` Cobrança por Pix: ``` Para finalizar, pague pelo Pix: [pix:Minha Loja;chave@exemplo.com;EMAIL|Pagar] ``` O WhatsApp aceita no máximo **3 botões** de resposta/link/ligação/copiar por mensagem. Se você escrever mais de 3, eles são enviados em blocos de 3. O Pix é uma mensagem própria, então sai sempre separado dos outros botões. O rótulo depois do `|` no `[pix:...]` não altera o botão de pagamento (o WhatsApp usa o rótulo nativo); o texto exibido acima do Pix é o restante do balão. Não use os caracteres `|`, `]` ou `;` dentro do texto ou dos parâmetros, eles separam os campos da marcação. Um token malformado (tipo desconhecido, parâmetro faltando) é ignorado e o restante do texto do balão é enviado normalmente. ## Endpoints de gerenciamento `POST /api/typebot/set/:instance`, cria um bot (create-only). `PATCH /api/typebot/update/:instance`, edita parcialmente um bot existente. `GET /api/typebot/list/:instance?botId=`, os bots da instância + status da integração. `DELETE /api/typebot/delete/:instance?botId=`, remove um bot (sem `botId`, apaga todos) e re-sincroniza os demais. `POST /api/typebot/start/:instance`, dispara um fluxo manualmente para um número. `GET /api/typebot/sessions/:instance?botId=` lista sessões; `POST` controla (pause/resume/close). ## Roteamento por trigger Cada mensagem recebida é avaliada contra os bots habilitados, na seguinte **ordem de prioridade** (uma palavra-chave específica vence o catch-all): ``` equals > startsWith / endsWith > contains > regex > all ``` **Unicidade:** cada instância pode ter apenas **um** bot `all` habilitado; bots `keyword` são únicos por combinação de `(operator, value)`. ## Ativação inline na criação da instância Um bot pode ser configurado **junto com a criação da instância**, sem precisar chamar `set` separadamente. Basta enviar o bloco `typebot*` no body de [`POST /api/instance/new`](/pt/api/instance/create): ```json { "name": "suporte", "typebotEnabled": true, "typebotUrl": "https://typebot.co/meu-bot-abc123", "typebotTriggerType": "all" } ``` Se a ativação falhar (campos inválidos), a instância **continua sendo criada**, o objeto `typebot` retorna com `status: "error"` e `error: ""`. Você pode então chamar [`POST /api/typebot/set/:instance`](/pt/api/typebot/set) para configurar o bot sem recriar a instância. ## Modelo de dados O servidor persiste os dados em duas tabelas: | Tabela | Descrição | | ------ | --------- | | `typebot_integrations` | Vínculo interno da integração da instância (`status`, `last_error`, `fallback_bot_id`). | | `typebot_bots` | N bots por instância. Cada linha guarda `typebot_url`, `trigger_type` / `trigger_operator` / `trigger_value` e os flags de comportamento (`expire_minutes`, `keyword_finish`, `typing_delay_ms`, `stop_bot_from_me`, `debounce_seconds`, `ignore_groups`, `enabled`). | Sempre que um bot é criado, editado ou removido, a RyzeAPI recomputa a lista completa de bots da instância e (re)ativa a integração. Quando o último bot é removido, a integração é desativada e o vínculo local é apagado. ## Próximos passos Configure o primeiro bot com `POST /api/typebot/set/:instance`. Tabela de mapeamento dos status HTTP e mensagens da integração. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Cria um bot do Typebot para a instância (**create-only**). Para editar um bot existente use [`PATCH /api/typebot/update/:instance`](/pt/api/typebot/update). A cada criação a RyzeAPI recomputa a lista completa de bots da instância e (re)ativa a integração. **Prioridade de trigger**, quando várias regras podem casar com a mesma mensagem, a mais específica vence: ``` equals > startsWith / endsWith > contains > regex > all ``` **Unicidade**, cada instância pode ter apenas **um** bot `all` habilitado; bots `keyword` são únicos por combinação de `(triggerOperator, triggerValue)`. Tentar criar um conflito devolve `400`. A `typebotUrl` deve apontar para um Typebot **publicado** (viewer). O `/` final é removido. Esta operação tem timeout interno de **60s**. ## Exemplo ```bash cURL curl -X POST "https://ryzeapi.cloud/api/typebot/set/suporte" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{ "typebotUrl": "https://typebot.co/meu-bot-abc123", "triggerType": "keyword", "triggerOperator": "contains", "triggerValue": "orçamento", "enabled": true, "description": "Bot de orçamento", "expireMinutes": 30, "expireMessage": "Sessão encerrada por inatividade.", "keywordFinish": "sair", "finishMessage": "Até logo! 👋", "typingDelayMs": 1500, "stopBotFromMe": true, "debounceSeconds": 6, "ignoreGroups": true }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/typebot/set/suporte", { method: "POST", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ typebotUrl: "https://typebot.co/meu-bot-abc123", triggerType: "keyword", triggerOperator: "contains", triggerValue: "orçamento", enabled: true, description: "Bot de orçamento", expireMinutes: 30, expireMessage: "Sessão encerrada por inatividade.", keywordFinish: "sair", finishMessage: "Até logo! 👋", typingDelayMs: 1500, stopBotFromMe: true, debounceSeconds: 6, ignoreGroups: true }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/typebot/set/suporte", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={ "typebotUrl": "https://typebot.co/meu-bot-abc123", "triggerType": "keyword", "triggerOperator": "contains", "triggerValue": "orçamento", "enabled": True, "description": "Bot de orçamento", "expireMinutes": 30, "expireMessage": "Sessão encerrada por inatividade.", "keywordFinish": "sair", "finishMessage": "Até logo! 👋", "typingDelayMs": 1500, "stopBotFromMe": True, "debounceSeconds": 6, "ignoreGroups": True } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "typebotUrl": "https://typebot.co/meu-bot-abc123", "triggerType": "keyword", "triggerOperator": "contains", "triggerValue": "orçamento", "enabled": true, "description": "Bot de orçamento", "expireMinutes": 30, "expireMessage": "Sessão encerrada por inatividade.", "keywordFinish": "sair", "finishMessage": "Até logo! 👋", "typingDelayMs": 1500, "stopBotFromMe": true, "debounceSeconds": 6, "ignoreGroups": true }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/typebot/set/suporte", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` Para o bot mais simples, envie só `typebotUrl` + `triggerType: "all"`: ele responde a qualquer mensagem. Para **editar** um bot já existente, use [`PATCH /api/typebot/update/:instance`](/pt/api/typebot/update) com o `botId` (retornado por [`GET /api/typebot/list/:instance`](/pt/api/typebot/list)). ## Resposta de sucesso ```json 201 Created { "instance": "suporte", "bot": { "id": "8f3a1c2e-...-b7d9", "instance_id": "...", "enabled": true, "description": "Bot de orçamento", "typebot_url": "https://typebot.co/meu-bot-abc123", "trigger_type": "keyword", "trigger_operator": "contains", "trigger_value": "orçamento", "expire_minutes": 30, "expire_message": "Sessão encerrada por inatividade.", "keyword_finish": "sair", "finish_message": "Até logo! 👋", "typing_delay_ms": 1500, "stop_bot_from_me": true, "debounce_seconds": 6, "ignore_groups": true, "no_start_from_me": false, "keep_open": false }, "message": "typebot bot saved" } ``` | Campo | Descrição | | ----- | --------- | | `instance` | Nome da instância onde o bot foi salvo. | | `bot` | O bot criado, já com o `id` gerado. Veja os campos na [listagem de bots](/pt/api/typebot/list). | | `bot.id` | UUID do bot, usado em `update`, `delete` e `sessions`. | | `message` | Mensagem fixa de confirmação. | ## Parâmetros de rota Nome da instância (ex.: `suporte`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body URL do Typebot **publicado** (viewer). Precisa ser uma URL válida. O `/` final é removido. Ex.: `https://typebot.co/meu-bot-abc123`. Como o bot é acionado: `all` (qualquer mensagem inicia o fluxo) ou `keyword` (só quando a mensagem casa com `triggerOperator` + `triggerValue`). Operador do gatilho, **obrigatório se** `triggerType` é `keyword`. Um de: `contains`, `equals`, `startsWith`, `endsWith`, `regex`. Palavra ou expressão do gatilho, **obrigatória se** `triggerType` é `keyword`. Se o bot está ativo no roteamento. Ausente equivale a `true`. Rótulo do bot no painel (ex.: `"Bot de orçamento"`). Expira a sessão por inatividade após N minutos. `0` = nunca expira. Mensagem enviada ao usuário quando a sessão expira (se definida). Palavra que, enviada pelo usuário, finaliza o bot imediatamente (ex.: `"sair"`). Despedida enviada quando o bot é finalizado pela `keywordFinish`. Delay do indicador "digitando..." antes de cada resposta, em milissegundos (convertido para segundos no envio). Se `true`, o bot é pausado naquela conversa quando você (o operador) responde manualmente. Junta fragmentos enviados pelo cliente por N segundos antes de processar (evita disparar o fluxo a cada linha). Se `true`, mensagens de grupo não acionam o bot. Ausente equivale a `true`. Se `true`, o bot não inicia sozinho quando **você** começou a conversa. Quando você envia a primeira mensagem e o contato responde, o bot não dispara. A janela de reativação reaproveita `expireMinutes` (contada a partir da sua primeira mensagem; `0` = permanente). Se `true`, ao terminar o fluxo a conversa fica aberta em vez de encerrar. O bot fica em silêncio (não reinicia) e a sessão só encerra pela `keywordFinish` ou manualmente, aparecendo com status `held`. ## Erros | HTTP | `error.message` | Causa | | :--: | --------------- | ----- | | 400 | `invalid body: ...` | Body malformado, `typebotUrl` inválida ou `triggerType` fora de `all`/`keyword`. | | 400 | `triggerOperator and triggerValue are required when triggerType is 'keyword'` | `triggerType: "keyword"` sem operador/valor. | | 400 | `this instance already has an enabled 'all' trigger bot` | Já existe um bot `all` habilitado (unicidade). | | 400 | `another enabled bot already uses this trigger operator+value` | Já existe um bot `keyword` habilitado com o mesmo `(operator, value)`. | | 404 | `instance not found` | Instância não existe na RyzeAPI. | | 500 | `create bot: ...` / `activate typebot: ...` | Falha de persistência local ou na (re)ativação da integração. | | 503 | `integration gateway not configured` | Serviço de integração indisponível no servidor. | ### Exemplo de payload de erro Trigger `keyword` sem operador/valor: ```json { "success": false, "error": { "message": "triggerOperator and triggerValue are required when triggerType is 'keyword'" } } ``` ## Próximo Confira todos os bots da instância e o status da integração. Dispare o fluxo manualmente para um número com `POST /api/typebot/start/:instance`. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Edita **parcialmente** um bot existente do Typebot. Envie apenas os campos que quer alterar: os omitidos são preservados. O `botId` identifica o bot. A cada mudança a RyzeAPI recomputa a lista completa de bots da instância e re-sincroniza a integração. Para **criar** um bot novo use [`POST /api/typebot/set/:instance`](/pt/api/typebot/set). As regras de **unicidade** e **prioridade** de trigger continuam valendo: cada instância só pode ter um bot `all` habilitado e bots `keyword` são únicos por `(triggerOperator, triggerValue)`. Um conflito devolve `400`. ## Exemplo ```bash cURL curl -X PATCH "https://ryzeapi.cloud/api/typebot/update/suporte" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{ "botId": "8f3a1c2e-...-b7d9", "enabled": false, "typingDelayMs": 800 }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/typebot/update/suporte", { method: "PATCH", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ botId: "8f3a1c2e-...-b7d9", enabled: false, typingDelayMs: 800 }) }); ``` ```python Python import os, requests requests.patch( "https://ryzeapi.cloud/api/typebot/update/suporte", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={ "botId": "8f3a1c2e-...-b7d9", "enabled": False, "typingDelayMs": 800 } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "botId": "8f3a1c2e-...-b7d9", "enabled": false, "typingDelayMs": 800 }`) req, _ := http.NewRequest("PATCH", "https://ryzeapi.cloud/api/typebot/update/suporte", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` Descubra o `botId` a editar com [`GET /api/typebot/list/:instance`](/pt/api/typebot/list). Como é um `PATCH`, você pode enviar um único campo (ex.: só `enabled`) sem reenviar toda a configuração. ## Resposta de sucesso ```json 200 OK { "instance": "suporte", "bot": { "id": "8f3a1c2e-...-b7d9", "enabled": false, "description": "Bot de orçamento", "typebot_url": "https://typebot.co/meu-bot-abc123", "trigger_type": "keyword", "trigger_operator": "contains", "trigger_value": "orçamento", "expire_minutes": 30, "keyword_finish": "sair", "typing_delay_ms": 800, "stop_bot_from_me": true, "debounce_seconds": 6, "ignore_groups": true, "no_start_from_me": false, "keep_open": false }, "message": "typebot bot updated" } ``` | Campo | Descrição | | ----- | --------- | | `instance` | Nome da instância onde o bot foi editado. | | `bot` | O bot já com os campos atualizados. Veja todos os campos na [listagem de bots](/pt/api/typebot/list). | | `message` | Mensagem fixa de confirmação. | ## Parâmetros de rota Nome da instância (ex.: `suporte`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body UUID do bot a editar, obtido em [`GET /api/typebot/list/:instance`](/pt/api/typebot/list). URL do Typebot **publicado** (viewer). Precisa ser uma URL válida. O `/` final é removido. Como o bot é acionado: `all` (qualquer mensagem inicia o fluxo) ou `keyword` (só quando a mensagem casa com `triggerOperator` + `triggerValue`). Operador do gatilho, **obrigatório se** `triggerType` é `keyword`. Um de: `contains`, `equals`, `startsWith`, `endsWith`, `regex`. Palavra ou expressão do gatilho, **obrigatória se** `triggerType` é `keyword`. Se o bot está ativo no roteamento. Rótulo do bot no painel (ex.: `"Bot de orçamento"`). Expira a sessão por inatividade após N minutos. `0` = nunca expira. Mensagem enviada ao usuário quando a sessão expira (se definida). Palavra que, enviada pelo usuário, finaliza o bot imediatamente (ex.: `"sair"`). Despedida enviada quando o bot é finalizado pela `keywordFinish`. Delay do indicador "digitando..." antes de cada resposta, em milissegundos. Se `true`, o bot é pausado naquela conversa quando você (o operador) responde manualmente. Junta fragmentos enviados pelo cliente por N segundos antes de processar. Se `true`, mensagens de grupo não acionam o bot. Se `true`, o bot não inicia sozinho quando **você** começou a conversa. Quando você envia a primeira mensagem e o contato responde, o bot não dispara. A janela de reativação reaproveita `expireMinutes` (contada a partir da sua primeira mensagem; `0` = permanente). Se `true`, ao terminar o fluxo a conversa fica aberta em vez de encerrar. O bot fica em silêncio (não reinicia) e a sessão só encerra pela `keywordFinish` ou manualmente, aparecendo com status `held`. ## Erros | HTTP | `error.message` | Causa | | :--: | --------------- | ----- | | 400 | `invalid body: ...` | Body malformado ou campo inválido. | | 400 | `botId is required` | `botId` ausente no body. | | 400 | `this instance already has an enabled 'all' trigger bot` | Já existe outro bot `all` habilitado (unicidade). | | 400 | `another enabled bot already uses this trigger operator+value` | Já existe outro bot `keyword` habilitado com o mesmo `(operator, value)`. | | 404 | `instance not found` | Instância não existe na RyzeAPI. | | 404 | `bot not found for this instance` | O `botId` informado não existe nesta instância. | | 500 | `update bot: ...` / `activate typebot: ...` | Falha de persistência local ou na re-sincronização da integração. | | 503 | `integration gateway not configured` | Serviço de integração indisponível no servidor. | ## Próximo Confira os bots da instância e o status da integração. Remova o bot com `DELETE /api/typebot/delete/:instance?botId=`. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Retorna os bots do Typebot cadastrados na instância, junto com o `status` e o `last_error` da integração. Cada bot vem **enriquecido** com `active_sessions` (número de sessões ao vivo) e `last_activity_at` (última atividade). Passe `?botId=` para restringir a um único bot. O status local é complementado (best-effort) com o estado atual da integração, com timeout de **10s**; se o serviço não responder, os valores locais são mantidos. Este endpoint **não** retorna `503` quando a integração está indisponível: ele responde com os dados locais e, se houver, um `last_error`. O `503 integration gateway not configured` só aparece em `set`, `update` e `start`. ## Exemplo ```bash cURL # todos os bots curl -X GET "https://ryzeapi.cloud/api/typebot/list/suporte" \ -H "token: $Token_Account" # um bot específico curl -X GET "https://ryzeapi.cloud/api/typebot/list/suporte?botId=8f3a1c2e-...-b7d9" \ -H "token: $Token_Account" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/typebot/list/suporte", { method: "GET", headers: { "token": process.env.Token_Account } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/typebot/list/suporte", headers={ "token": os.environ["Token_Account"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/typebot/list/suporte", nil) req.Header.Set("token", os.Getenv("Token_Account")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "instance": "suporte", "status": "active", "last_error": "", "bots": [ { "id": "8f3a1c2e-...-b7d9", "enabled": true, "description": "Bot de orçamento", "typebot_url": "https://typebot.co/meu-bot-abc123", "trigger_type": "keyword", "trigger_operator": "contains", "trigger_value": "orçamento", "expire_minutes": 30, "keyword_finish": "sair", "typing_delay_ms": 1500, "stop_bot_from_me": true, "debounce_seconds": 6, "ignore_groups": true, "no_start_from_me": false, "keep_open": false, "active_sessions": 3, "last_activity_at": "2026-07-27T14:03:11Z" } ] } ``` | Campo | Descrição | | ----- | --------- | | `instance` | Nome da instância. | | `status` | Estado da integração: `active` / `paused` / `error`. Vazio quando não há integração ativa. | | `last_error` | Última mensagem de erro da integração. Vazio quando saudável. | | `bots` | Lista dos bots da instância (ou só o bot filtrado por `?botId=`). Cada item traz todos os campos do bot mais o enriquecimento abaixo. Vazio quando não há bots. | | `bots[].active_sessions` | Número de sessões do Typebot ao vivo para aquele bot. | | `bots[].last_activity_at` | Timestamp ISO-8601 da última atividade do bot (ou `null` se nunca houve). | ## Parâmetros de rota Nome da instância (ex.: `suporte`). ## Query params UUID de um bot para retornar apenas ele. Omitido, retorna todos os bots da instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Comportamento Consulta a tabela `typebot_bots` da instância. Esta etapa é rápida e sempre funciona. Busca a linha em `typebot_integrations` para obter `status` e `last_error` locais. Se houver integração configurada, a RyzeAPI consulta o estado atual com timeout de **10s** e sobrescreve `status` / `last_error` com os valores live. Em caso de falha de rede, mantém os locais. ## Erros | HTTP | `error.message` | | :--: | --------------- | | 404 | `instance not found` | ## Próximo Ajuste os campos de um bot com `PATCH /api/typebot/update/:instance`. Veja e controle as sessões ativas com `GET`/`POST /api/typebot/sessions/:instance`. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** não ## Descrição Inicia manualmente um fluxo do Typebot para um número, sem esperar por uma mensagem de trigger. A RyzeAPI dispara o `startChat` do bot indicado e persiste a sessão. Útil para campanhas de saída (outbound) e para retomar uma conversa a partir do seu backend. A instância precisa já ter pelo menos um bot cadastrado (uma integração ativa). ## Exemplo ```bash cURL curl -X POST "https://ryzeapi.cloud/api/typebot/start/suporte" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{ "number": "5511999999999", "botId": "8f3a1c2e-...-b7d9", "variables": { "nome": "João", "plano": "premium" } }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/typebot/start/suporte", { method: "POST", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ number: "5511999999999", botId: "8f3a1c2e-...-b7d9", variables: { nome: "João", plano: "premium" } }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/typebot/start/suporte", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={ "number": "5511999999999", "botId": "8f3a1c2e-...-b7d9", "variables": { "nome": "João", "plano": "premium" } } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "number": "5511999999999", "botId": "8f3a1c2e-...-b7d9", "variables": { "nome": "João", "plano": "premium" } }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/typebot/start/suporte", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "message": "typebot flow started" } ``` ## Parâmetros de rota Nome da instância (ex.: `suporte`). ## Headers `TokenAccount` ou `TokenInstance`. `application/json` ## Request body Número de destino no formato E.164 sem o `+` (ex.: `5511999999999`). UUID do bot a iniciar, obtido em [`GET /api/typebot/list/:instance`](/pt/api/typebot/list). Variáveis prefilled passadas ao Typebot (mapa `string → string`). Ex.: `{ "nome": "João", "plano": "premium" }`. ## Erros | HTTP | `error.message` | Causa | | :--: | --------------- | ----- | | 400 | `invalid body: ...` | Body malformado ou `number` / `botId` ausentes. | | 404 | `instance not found` | Instância não existe na RyzeAPI. | | 404 | `no typebot integration for this instance, create a bot first` | A instância ainda não tem nenhum bot/integração. | | 404 | `bot not found for this instance` | O `botId` informado não existe nesta instância. | | 500 | `start: ...` | Falha ao iniciar o fluxo. | | 503 | `integration gateway not configured` | Serviço de integração indisponível no servidor. | ## Próximo Descubra o `botId` a usar com `GET /api/typebot/list/:instance`. Configure um bot antes de iniciar fluxos manualmente. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** sim ## Descrição Remove um bot da instância (identificado por `?botId=`) e re-sincroniza a lista restante. **Sem `botId`, apaga todos os bots** da instância. Se o último bot é removido, a integração é desativada e o vínculo local é apagado. A re-sincronização da integração é **best-effort**: se ela falhar, o servidor apenas registra um warning e a remoção do bot **continua válida** (a linha local já foi apagada). Por isso este endpoint não expõe erros de re-sincronização no retorno. ## Exemplo ```bash cURL # remove um bot específico curl -X DELETE "https://ryzeapi.cloud/api/typebot/delete/suporte?botId=8f3a1c2e-...-b7d9" \ -H "token: $Token_Account" # remove TODOS os bots da instância curl -X DELETE "https://ryzeapi.cloud/api/typebot/delete/suporte" \ -H "token: $Token_Account" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/typebot/delete/suporte?botId=8f3a1c2e-...-b7d9", { method: "DELETE", headers: { "token": process.env.Token_Account } }); ``` ```python Python import os, requests requests.delete( "https://ryzeapi.cloud/api/typebot/delete/suporte", params={ "botId": "8f3a1c2e-...-b7d9" }, headers={ "token": os.environ["Token_Account"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("DELETE", "https://ryzeapi.cloud/api/typebot/delete/suporte?botId=8f3a1c2e-...-b7d9", nil) req.Header.Set("token", os.Getenv("Token_Account")) http.DefaultClient.Do(req) } ``` ## Resposta de sucesso ```json 200 OK { "message": "typebot bot deleted" } ``` ## Parâmetros de rota Nome da instância (ex.: `suporte`). ## Query params UUID do bot a remover, obtido em [`GET /api/typebot/list/:instance`](/pt/api/typebot/list). **Omitido, apaga todos os bots** da instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Comportamento Apaga a linha do bot em `typebot_bots` (ou todas as linhas da instância, quando `botId` é omitido). Se o `botId` informado não existir, devolve `404`. A RyzeAPI recomputa a lista de bots e (re)ativa a integração. Falha aqui é apenas logada como warning, não reverte a remoção. Quando não sobra nenhum bot, a integração é desativada e o vínculo em `typebot_integrations` é apagado. ## Erros | HTTP | `error.message` | | :--: | --------------- | | 404 | `instance not found` | | 404 | `bot not found` | ## Próximo Confira quais bots ainda estão cadastrados na instância. Recrie ou adicione um bot com `POST /api/typebot/set/:instance`. **Auth:** `TokenAccount` ou `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** `GET` sim / `POST` não ## Descrição Gerencia as **sessões ao vivo** do Typebot da instância, cada sessão é uma conversa em andamento entre um número e um bot. - **`GET /api/typebot/sessions/:instance?botId=`**, lista as sessões em andamento (`opened`, `paused` e `held`). Passe `?botId=` para filtrar por um bot. - **`POST /api/typebot/sessions/:instance`**, controla uma sessão: `pause`, `resume` ou `close`. A leitura e o controle das sessões dependem do estado mantido pela integração. Uma sessão pausada (`pause`) para de processar mensagens até um `resume`; `close` a encerra. ## Listar sessões (GET) ```bash cURL curl -X GET "https://ryzeapi.cloud/api/typebot/sessions/suporte?botId=8f3a1c2e-...-b7d9" \ -H "token: $Token_Account" ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/typebot/sessions/suporte?botId=8f3a1c2e-...-b7d9", { method: "GET", headers: { "token": process.env.Token_Account } }); ``` ```python Python import os, requests requests.get( "https://ryzeapi.cloud/api/typebot/sessions/suporte", params={ "botId": "8f3a1c2e-...-b7d9" }, headers={ "token": os.environ["Token_Account"] } ) ``` ```go Go package main import ( "net/http" "os" ) func main() { req, _ := http.NewRequest("GET", "https://ryzeapi.cloud/api/typebot/sessions/suporte?botId=8f3a1c2e-...-b7d9", nil) req.Header.Set("token", os.Getenv("Token_Account")) http.DefaultClient.Do(req) } ``` ### Resposta de sucesso ```json 200 OK { "instance": "suporte", "sessions": [ { "botId": "8f3a1c2e-...-b7d9", "number": "5511999999999", "status": "opened", "last_activity_at": "2026-07-27T14:03:11Z" } ] } ``` | Campo | Descrição | | ----- | --------- | | `instance` | Nome da instância. | | `sessions` | Lista das sessões ao vivo (filtradas por `?botId=`, se informado). Vazio quando não há sessões. | | `sessions[].botId` | UUID do bot que conduz a sessão. | | `sessions[].number` | Número do contato na conversa (E.164 sem `+`). | | `sessions[].status` | Estado da sessão: `opened` (ativa), `paused` (pausada pelo operador/regra), `held` (mantida aberta ao fim do fluxo pelo `keepOpen`, aguardando atendimento) ou `closed` (encerrada). O `GET` lista `opened`, `paused` e `held`. | | `sessions[].last_activity_at` | Timestamp ISO-8601 da última atividade na sessão. | ## Controlar uma sessão (POST) ```bash cURL curl -X POST "https://ryzeapi.cloud/api/typebot/sessions/suporte" \ -H "token: $Token_Account" \ -H "Content-Type: application/json" \ -d '{ "botId": "8f3a1c2e-...-b7d9", "number": "5511999999999", "status": "pause" }' ``` ```javascript JavaScript await fetch("https://ryzeapi.cloud/api/typebot/sessions/suporte", { method: "POST", headers: { "token": process.env.Token_Account, "Content-Type": "application/json" }, body: JSON.stringify({ botId: "8f3a1c2e-...-b7d9", number: "5511999999999", status: "pause" }) }); ``` ```python Python import os, requests requests.post( "https://ryzeapi.cloud/api/typebot/sessions/suporte", headers={ "token": os.environ["Token_Account"], "Content-Type": "application/json" }, json={ "botId": "8f3a1c2e-...-b7d9", "number": "5511999999999", "status": "pause" } ) ``` ```go Go package main import ( "net/http" "os" "strings" ) func main() { body := strings.NewReader(`{ "botId": "8f3a1c2e-...-b7d9", "number": "5511999999999", "status": "pause" }`) req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/typebot/sessions/suporte", body) req.Header.Set("token", os.Getenv("Token_Account")) req.Header.Set("Content-Type", "application/json") http.DefaultClient.Do(req) } ``` ### Resposta de sucesso ```json 200 OK { "success": true, "message": "session paused" } ``` ## Parâmetros de rota Nome da instância (ex.: `suporte`). ## Query params (GET) UUID do bot para filtrar as sessões. Omitido, retorna as sessões de todos os bots da instância. ## Request body (POST) Número da conversa cuja sessão será controlada (E.164 sem o `+`, ex.: `5511999999999`). Ação sobre a sessão: `pause` (pausa o processamento), `resume` (retoma) ou `close` (encerra). UUID do bot da sessão. Opcional quando o número tem apenas uma sessão ativa na instância. ## Headers `TokenAccount` ou `TokenInstance`. ## Erros | HTTP | `error.message` | Causa | | :--: | --------------- | ----- | | 400 | `invalid body: ...` | Body malformado ou `status` fora de `pause` / `resume` / `close`. | | 404 | `instance not found` | Instância não existe na RyzeAPI. | | 404 | `session not found` | Não há sessão ativa para o `number` (e `botId`) informado. | | 503 | `integration gateway not configured` | Serviço de integração indisponível no servidor. | ## Próximo Veja os bots e o total de `active_sessions` de cada um. Abra uma nova sessão manualmente com `POST /api/typebot/start/:instance`. ### Observabilidade O módulo **Observabilidade** expõe um único endpoint, `GET /health`, para você verificar se a API está saudável. Ele não exige autenticação e bypassa rate-limit e CORS, para que monitores externos possam consultá-lo sem credenciais. ## Endpoint `GET /health`, probe combinado (processo + DB + dependências opcionais como S3 quando configurado). ## Como usar Aponte um **uptime monitor** (UptimeRobot, Better Stack, Pingdom, etc.) para `GET /health`: - `200` → API saudável. - `503` → API degradada, dispare o alerta. ## Relacionados Limites globais e regras de CORS, `/health` está fora deles. Como interpretar os códigos HTTP retornados pela API. **Auth:** Nenhuma • **Rate-limit:** Bypass (fora do limite global) • **Idempotente:** sim ## Descrição Endpoint **aberto** (sem token) que devolve um snapshot do estado da API a partir de checks de dependências (DB sempre, mais probes opcionais como S3 quando configurado). Bypassa rate-limit e CORS para que monitores externos possam consultá-lo sem credenciais. ## Exemplo ```bash cURL curl -X GET "https://ryzeapi.cloud/health" ``` ## Resposta, saudável ```json 200 OK { "status": "ok", "service": "RyzeAPI", "uptime": "12h34m56s", "timestamp": "2026-04-28T14:35:21Z", "checks": { "db": "ok" } } ``` ## Resposta, degradado ```json 503 Service Unavailable { "status": "degraded", "service": "RyzeAPI", "uptime": "12h34m56s", "timestamp": "2026-04-28T14:35:21Z", "checks": { "db": "fail: connection refused" } } ``` ## Campos | Campo | Descrição | | ----- | --------- | | `status` | `"ok"` quando todos os checks passam, `"degraded"` quando algum falha. | | `service` | Sempre `"RyzeAPI"`. | | `uptime` | Tempo desde o boot do processo, no formato Go duration (ex.: `12h34m56s`, `1h2m3.456s`). | | `timestamp` | Momento da resposta em RFC 3339 (UTC). | | `checks` | Mapa `nome → "ok" \| "fail: "` para cada dependência verificada. | Entradas em `checks` aparecem condicionalmente: `db` está sempre presente; outras dependências (ex.: `s3`) só aparecem quando configuradas no servidor. ## Uso em uptime monitors Aponte um serviço de monitoramento (UptimeRobot, Better Stack, Pingdom, etc.) para `GET /health`: - `200` → API saudável. - `503` → API degradada, dispare o alerta. ## Notas - Não é necessário enviar header `token`. Tokens enviados são ignorados. - O endpoint **não conta** para o limite global de `100 req/min`, pode ser chamado sem restrição por probes externas. ## Relacionados Posicionamento do endpoint dentro do ciclo de vida da API. Tabela de status HTTP usados pela RyzeAPI.