Gateway https://comflow.com.br/api/v1 com o header apikey, fila da proteção do número, webhooks e API de parceiro.
URL base https://comflow.com.br/api/v1. Header apikey: SEU_TOKEN em todas as requisições (também aceito como Authorization: Bearer). O token é exclusivo por instância e a identifica, então os caminhos vão sem o nome da instância. Corpo e respostas em JSON, UTF-8.
Sucesso: 200 ou 201 com a resposta da engine (por exemplo key.id no envio). 202: aceito e enfileirado pela Proteção do número (veja abaixo). Toda resposta traz X-Plan-Used, X-Plan-Limit e X-RateLimit-Remaining. Erros do gateway vêm como {"ok":false,"error":"codigo","message":"motivo"}:
401 missing_token / invalid_token: token ausente ou inválido.403 client_suspended / instance_pending / instance_suspended / instance_canceled: conta ou instância bloqueada por cobrança.409 instance_disconnected: a instância não está conectada; leia o QR no painel.429 rate_limited: mais de 60 requisições por minuto; aguarde retry_after segundos.429 plan_limit_reached: limite mensal do plano atingido; volta no próximo ciclo ou com upgrade.404 not_found: rota não disponível no gateway.502 upstream_unreachable: engine indisponível; tente de novo.Estado da instância: instance, connection (open, close, connecting), phone, plan, billing_status, usage.sent_month, usage.limit_month, usage.remaining, usage.paused, rate_limit_per_min, trial_ends_at, next_due_date.
curl "https://comflow.com.br/api/v1/me" -H "apikey: SEU_TOKEN"
Mesmos caminhos e corpos da Evolution API v2. Os mais usados:
POST /message/sendText {"number":"5511999999999","text":"Olá"}
POST /message/sendMedia {"number":"...","mediatype":"image|video|document","mimetype":"image/png","caption":"...","media":"URL ou base64","fileName":"arquivo.png"}
POST /message/sendWhatsAppAudio {"number":"...","audio":"URL ou base64"}
POST /message/sendLocation {"number":"...","name":"Loja","address":"Rua...","latitude":-27.59,"longitude":-48.55}
POST /message/sendContact {"number":"...","contact":[{"fullName":"Ana","wuid":"5511...","phoneNumber":"+55 11 ..."}]}
POST /message/sendPoll {"number":"...","name":"Pergunta?","selectableCount":1,"values":["A","B"]}
POST /message/sendReaction {"key":{"remoteJid":"...@s.whatsapp.net","fromMe":false,"id":"ID"},"reaction":"👍"}
POST /message/sendList · sendButtons · sendSticker · sendStatus
Campos opcionais aceitos pela engine (delay, quoted, mentions, linkPreview) passam normalmente. Grupos: use o id do grupo em number (...@g.us).
Com a proteção ativa na instância, cada POST /message/* passa pelas regras: janela 8h–21h, teto diário por idade do número, intervalo entre envios e limite de rajada por minuto. Se cabe, sai na hora (200). Se não, é enfileirado e a resposta é 202:
{ "ok": true, "queued": true, "id": "q_3f9a1c27b0d4e5", "status": "queued",
"reason": "gap", "reason_text": "intervalo mínimo entre envios",
"scheduled_at": "2026-01-01 14:32:18", "position": 3,
"status_url": "https://comflow.com.br/api/v1/messages/q_3f9a1c27b0d4e5" }
GET /messages/{id} → status queued | sending | sent | failed | canceled, sent_at, result (resposta da engine)
DELETE /messages/{id} → cancela se ainda estiver na fila (409 not_cancelable se já saiu)
GET /messages/queue → itens aguardando desta instância
Horários, em detalhe. Envio normal fora da janela (8h–21h) não sai: entra na fila e é agendado para o início da janela seguinte, com até 15 min de espalhamento. Entre 22h e 7h é o horário de silêncio: nenhum envio normal sai, e a nota de saúde do número cai se 20% ou mais dos seus envios caírem nessa faixa. A fila anda só dentro da janela. Entre os itens cujo horário chegou, o mais antigo sai primeiro (prioridade alta antes).
Prioridade: inclua "priority": "high" no corpo do envio para sair na hora, em qualquer horário, respeitando só o intervalo mínimo: até 30 por hora dentro do dia e 10 por hora no silêncio (22h–7h). Acima da cota entra na frente da fila (reason priority_quota ou priority_quiet). Use em códigos e respostas que alguém está esperando, não em campanhas: o painel mostra o uso de prioridade por instância.
Fila acumulada. Se o que está aguardando não cabe em um dia de janela (teto diário e cadência), a instância mostra o aviso no painel e, uma vez por dia, o seu webhook recebe o evento queue.backlog com queued, capacity_per_day, days_to_drain e drains_by. É sinal de volume acima do que o número aguenta: reduza o ritmo ou divida a base em mais números.
Motivos possíveis em reason: hours (fora da janela), daily_cap (teto do dia), burst (rajada no minuto), gap (intervalo mínimo), queue_active (já havia fila), priority_quota e priority_quiet (cota de prioridade).
POST /chat/whatsappNumbers {"numbers":["5511999999999"]} # verifica se existe no WhatsApp
POST /chat/findMessages {"where":{"key":{"remoteJid":"...@s.whatsapp.net"}}}
POST /chat/markMessageAsRead {"readMessages":[{"remoteJid":"...","fromMe":false,"id":"ID"}]}
POST /chat/sendPresence {"number":"...","presence":"composing","delay":1200}
POST /chat/fetchProfilePictureUrl {"number":"..."}
GET /group/fetchAllGroups?getParticipants=false
POST /group/create {"subject":"Nome","participants":["5511..."]}
GET /group/participants?groupJid=...@g.us
GET /label/findLabels · POST /label/handleLabel
GET /instance/connectionState → {"instance":{"state":"open"}}
GET /instance/connect → QR em base64 (e pairingCode) quando desconectada
POST /instance/restart
DELETE /instance/logout
GET /webhook/find → webhook configurado na engine
Criar, apagar e configurar webhook da instância são feitos pelo painel (ou pela API de parceiro). O gateway não expõe /instance/create nem /instance/delete.
POST em JSON na URL configurada no painel (ou por webhook_url na API de parceiro). Eventos: messages.upsert (mensagem recebida ou enviada), send.message, connection.update (state open/close/connecting), qrcode.updated e, com webhook configurado, também messages.update (entrega e leitura), messages.delete, labels.association e labels.edit (etiquetas), contacts.upsert/update, chats.upsert/update/delete, groups.upsert, group.update, group.participants.update, presence.update (digitando/online) e call. O gateway também emite queue.backlog (fila da Proteção do número acumulada, no máximo uma vez por dia por instância). Formato:
{ "event": "messages.upsert", "instance": "sua-instancia",
"data": { "key": { "remoteJid": "5511999990000@s.whatsapp.net", "fromMe": false, "id": "3EB0..." },
"pushName": "Cliente", "message": { "conversation": "Oi" }, "messageTimestamp": 1700000000 } }
Responda 200 em até alguns segundos. Se a sua URL falhar (rede, timeout ou erro 5xx), o evento é reenviado em 1, 5 e 15 minutos; erro 4xx não repete. Mensagens enviadas pela API chegam com fromMe: true e não contam duas vezes no seu limite.
/chat/find* e /chat/fetch*) têm cota própria de 300 por minuto, para importar histórico sem disputar com os envios. Envios contam no limite mensal do plano, recebidas não.key.id das mensagens para conciliar com o webhook.202 como sucesso e 429 com espera; nunca repita em loop.Para parceiros que revendem o Comflow e criam contas dos próprios clientes pelo sistema deles. Header X-Partner-Key: cfp_… (chave do parceiro, entregue pelo suporte), só HTTPS, JSON. Toda resposta tem ok e, em erro, error e message. Os benefícios do parceiro (dias, desconto, adicional grátis) são aplicados automaticamente às contas criadas; a cobrança pode ser do próprio cliente ou consolidada na conta do parceiro.
GET /me → benefícios, modo de cobrança, contas usadas/restantes, vagas do servidor, planos (plan_id) e plano padrão
GET /accounts → todas as contas do parceiro com instâncias, conexão, número, uso e vencimento
GET /accounts/{external_id} → uma conta
POST /accounts → cria conta + instância (idempotente por external_id)
POST /accounts/{external_id}/instances → mais uma instância na conta
PUT /accounts/{external_id}/instances/{nome} → troca ou remove o webhook do cliente ({"webhook_url": "https://…"} · "" remove) e/ou liga o histórico completo ({"sync_full_history": true})
DELETE /accounts/{external_id}[/instances/{nome}] → agenda o fim no vencimento; com {"mode":"now"} apaga na hora (se liberado)
POST https://comflow.com.br/api/partner/v1/accounts
{ "external_id": "cliente-123", "name": "Loja Exemplo", "email": "dono@loja.com", "phone": "47999990000",
"document": "12345678000199", "plan_id": 2, "label": "principal", "addon_protect": true, "password": "opcional",
"webhook_url": "https://seu-sistema.com/webhook/comflow?id=123", "sync_full_history": false }
201 { "ok": true,
"account": { "external_id", "client_id", "email", "billing": "client|partner", "magic_login_url", "password_reset_url" },
"instance": { "name", "token", "uid", "gateway_url": "https://comflow.com.br/api/v1", "connection": "close", "billing_status": "active", "plan", "next_due_date", "webhook_url", "sync_full_history" },
"applied": { "discount_pct", "discount_months", "addon_protect_free", "billing" } }
200 { "ok": true, "existing": true, "account": {…} } # mesmo external_id: nada é criado de novo
A instância nasce ativa, sem teste, e a cobrança entra na próxima fatura. Para conectar, o cliente usa o magic_login_url e lê o QR, ou o sistema do parceiro chama GET https://comflow.com.br/api/v1/instance/connect com apikey = token da instância.
Webhook do cliente: informe webhook_url (https) na criação ou depois com PUT. O Comflow recebe tudo da engine, conta e atualiza o estado, e repassa o JSON original para essa URL; com webhook a instância assina também messages.update (entrega/leitura), messages.delete, labels.association, contacts.update, chats.update, groups.upsert, group.participants.update, presence.update e call. Se a sua URL falhar (rede, timeout ou 5xx), o evento é reenviado em 1, 5 e 15 minutos; erro 4xx não repete. GET /accounts/{external_id} mostra webhook_last_error por instância.
Histórico completo: por padrão a engine guarda só o que chega depois da conexão (e a sincronização inicial curta do WhatsApp). Se o seu sistema importa conversas antigas com POST https://comflow.com.br/api/v1/chat/findMessages, envie "sync_full_history": true na criação ou depois com PUT: na próxima leitura de QR a engine baixa o histórico inteiro do aparelho e ele passa a vir no findMessages. Pesa o servidor e o banco, por isso é opcional; em instância já conectada, é preciso desconectar e parear de novo para valer.
401 missing_key / invalid_key · 403 api_disabled / ip_not_allowed / partner_limit_reached / delete_not_allowed409 email_exists · 422 invalid_data / invalid_plan / invalid_webhook / document_required / external_id_required (o retorno lista os campos)429 rate_limited · 503 vagas_esgotadas / sales_closed · 502 provision_failed (conta criada, repita a chamada)Acesse seu painel de instâncias.
2 dias grátis, sem cartão.
Conte rapidamente sobre o seu negócio. Respondemos em horário comercial com as condições de parceria.