Comflow

Referência da API

Gateway https://comflow.com.br/api/v1 com o header apikey, fila da proteção do número, webhooks e API de parceiro.

Base e autenticação

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.

Respostas, headers e erros

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.

GET /me

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"

Mensagens · POST /message/*

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).

Proteção do número · fila

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).

Chat, grupos, etiquetas · /chat/* · /group/* · /label/*

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

Instância · /instance/*

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.

Webhook (eventos que você recebe)

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.

Limites e boas práticas

  • 60 requisições por minuto por instância; leituras de conversas (/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.
  • Guarde o key.id das mensagens para conciliar com o webhook.
  • Trate 202 como sucesso e 429 com espera; nunca repita em loop.
  • Não use uma instância em dois sistemas ao mesmo tempo.

API de parceiro · https://comflow.com.br/api/partner/v1

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_allowed
  • 409 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)