ArkeFlow
API para desenvolvedores
Conecte o seu ERP, CRM ou site ao WhatsApp da empresa: dispare templates aprovados, mantenha a base de contatos em dia e receba de volta o que acontece — resposta do cliente, status de entrega, pedido de descadastro.
1. Começando
A base de todas as chamadas é https://arkeflow.com/api/v1. Tudo entra e sai em JSON, com Content-Type: application/json. Toda resposta tem um campo ok booleano; quando ele é false, vem também erro com a explicação em português.
Para começar você precisa de duas coisas, as duas obtidas pelo administrador da empresa na tela Administração do ArkeFlow: uma chave de API e, se você quiser receber eventos, a URL do seu sistema cadastrada no webhook de saída.
O acesso à API faz parte dos planos Equipe e Avançado. Nos outros planos as chamadas respondem 403. Isso não é limitação técnica: é o que o plano contratou.
Um detalhe que economiza tempo de depuração: o template tem de estar aprovado pela Meta e sincronizado no ArkeFlow antes de ser usado pela API. Template novo que ainda está em análise responde 409.
2. Autenticação
A chave vai no header Authorization, no formato Bearer. Ela começa com ak_live_ e é mostrada uma única vez, no momento em que é criada — guardamos só o hash, então nem nós conseguimos recuperá-la depois. Se perder, revogue e gere outra.
curl https://arkeflow.com/api/v1/contatos?telefone=54999998888 \ -H "Authorization: Bearer ak_live_SUA_CHAVE"
Se preferir, a chave também é aceita no header X-Api-Key. Trate-a como senha: ela dá acesso a disparar mensagens cobradas e a ler a base de contatos da empresa. Não a coloque em código de frontend nem em repositório.
3. Limite de chamadas
Cada chave aceita até 120 chamadas por minuto, com teto de 10 por segundo. Passando disso a resposta é 429 com o header Retry-After dizendo quantos segundos esperar. As respostas de sucesso trazem X-RateLimit-Remaining.
Esse limite existe para proteger o cliente de um laço acidental, que gastaria o orçamento dele na Meta em minutos. Um integrador bem-comportado não chega perto dele; se o seu caso de uso precisa de mais, fale com a gente antes de distribuir retry agressivo.
Há um segundo teto, mais apertado, para chaves que não existem. Se você está em homologação e recebe 429 logo nas primeiras tentativas, quase sempre é credencial errada e não volume: confira a chave antes de aumentar o retry.
4. Idempotência
Leia esta seção antes de escrever o retry. Enviar mensagem gasta dinheiro de verdade: a Meta cobra cada template entregue, direto da conta da empresa. Se a sua requisição der timeout e a sua biblioteca repetir o POST, sem cuidado a mensagem sai duas vezes e a cobrança também.
Mande o header Idempotency-Key com um valor único por intenção — o id do pedido no seu sistema serve bem. A primeira chamada executa e nós guardamos a resposta por 24 horas. Qualquer repetição com a mesma chave recebe exatamente a mesma resposta, com o mesmo status e o header X-Idempotencia: repetida, sem enviar nada de novo.
curl -X POST https://arkeflow.com/api/v1/mensagens \
-H "Authorization: Bearer ak_live_SUA_CHAVE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-48213-confirmacao" \
-d '{"telefone":"54999998888","template":"aviso_pedido","variaveis":["Maria","48213"]}'Três comportamentos que vale conhecer:
- Mesma chave, corpo diferente →
422. Quase sempre é chave reaproveitada por engano; devolver a resposta antiga esconderia o seu bug. - Falha passageira (a Meta recusou, o plano estava sem saldo) → a chave é liberada. Você pode tentar de novo com ela. Só resultado definitivo fica guardado.
- Sem o header → vale uma guarda automática: o mesmo template, com as mesmas variáveis, para o mesmo número, dentro de 60 segundos, recebe
409. Se a repetição é intencional, mande umaIdempotency-Keynova e ela passa.
5. Enviar mensagem
POST /api/v1/mensagens envia um template aprovado para um telefone. O envio conta na franquia do plano e aparece no painel da empresa como se tivesse saído de lá.
curl -X POST https://arkeflow.com/api/v1/mensagens \
-H "Authorization: Bearer ak_live_SUA_CHAVE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-48213-confirmacao" \
-d '{
"telefone": "54999998888",
"template": "aviso_pedido",
"idioma": "pt_BR",
"variaveis": ["Maria", "48213"]
}'
# resposta
{ "ok": true, "id": "wamid.HBgM...", "telefone": "5554999998888", "template": "aviso_pedido", "restante": 1832 }O telefone pode vir com ou sem o 55 e com ou sem máscara — normalizamos. variaveis aceita lista (posicional, na ordem em que aparecem no template) ou objeto {"nome": "valor"}. Para template com cabeçalho, mídia ou botões com parâmetro, use cabecalho, midia e botoes:
{
"telefone": "54999998888",
"template": "boleto_mensal",
"variaveis": { "nome": "Maria", "valor": "R$ 240,00" },
"midia": { "tipo": "DOCUMENT", "link": "https://seusistema.com.br/boletos/48213.pdf", "nomeArquivo": "boleto.pdf" },
"botoes": { "0": { "1": "48213" } }
}restante diz quantas mensagens ainda cabem na franquia do mês; vem null em plano sem teto. Vale monitorar: quando chega a zero, as chamadas passam a responder 402.
O que a API não faz: enviar texto livre para quem não falou com a empresa nas últimas 24 horas. Essa é regra da Meta, não nossa — fora da janela de 24 h só template aprovado sai. Quem respondeu há pouco é atendido pela tela de atendimento do ArkeFlow.
6. Contatos
POST /api/v1/contatos cria ou atualiza. As tags e os campos são juntados com o que já existe, não substituídos — então mandar o mesmo contato de novo não apaga nada, e por isso esta rota não precisa de Idempotency-Key.
curl -X POST https://arkeflow.com/api/v1/contatos \
-H "Authorization: Bearer ak_live_SUA_CHAVE" \
-H "Content-Type: application/json" \
-d '{
"telefone": "54999998888",
"nome": "Maria Silva",
"tags": ["clientes", "2026"],
"campos": { "cidade": "Paranhos", "ultimaCompra": "2026-09-30" }
}'
# 201 quando é novo, 200 quando já existia
{ "ok": true, "novo": true, "contato": { "...": "..." } }Para consultar: GET /api/v1/contatos?telefone=54999998888. Para registrar descadastro vindo do seu sistema, mande "optOut": true — e respeite-o: contato em opt-out faz o envio responder 403, de propósito.
7. Campanhas
GET /api/v1/campanhas/{id} devolve a situação e os números de uma campanha criada na tela — útil para mostrar o andamento dentro do seu sistema.
curl https://arkeflow.com/api/v1/campanhas/cmp_abc123 \
-H "Authorization: Bearer ak_live_SUA_CHAVE"
{
"ok": true,
"campanha": {
"id": "cmp_abc123", "nome": "Promoção de outubro", "status": "concluida",
"template": "promo_outubro",
"totais": { "fila": 0, "enviadas": 1840, "entregues": 1801, "lidas": 1203, "respostas": 96, "falhas": 39 },
"taxas": { "entrega": 0.979, "leitura": 0.668, "resposta": 0.053 },
"iniciadaEm": "2026-10-01T12:00:00.000Z", "concluidaEm": "2026-10-01T12:41:00.000Z"
}
}8. Webhook de saída
É o caminho de volta: nós chamamos o seu servidor quando algo acontece. O administrador cadastra a URL em Administração → Webhook de saída e escolhe os eventos. Há um botão que dispara um evento teste na hora, para você conferir a integração sem esperar que alguém responda no WhatsApp.
mensagem.recebida— Cliente respondeu (mensagem recebida)mensagem.status— Status de entrega mudou (enviada, entregue, lida, falhou)contato.optout— Contato pediu para saircontato.optin— Contato voltou a aceitar
Chega como POST, com este corpo:
POST /seu/endpoint
Content-Type: application/json
X-Arkeflow-Evento: mensagem.recebida
X-Arkeflow-Entrega: whk_m2h8x1a9k3
X-Arkeflow-Timestamp: 1760000000
X-Arkeflow-Assinatura: sha256=9f86d081884c7d65...
{
"evento": "mensagem.recebida",
"em": "2026-10-06T14:22:31.000Z",
"entregaId": "whk_m2h8x1a9k3",
"dados": {
"telefone": "5554999998888",
"nome": "Maria Silva",
"texto": "confirmo o pedido",
"tipo": "text",
"em": "2026-10-06T14:22:30.000Z",
"id": "wamid.HBgM...",
"respondendoA": "wamid.HBgL...",
"campanhaId": "cmp_abc123",
"temArquivo": false
}
}Três regras do nosso lado que mudam como você escreve o seu endpoint:
- Responda 2xx em até 5 segundos. Qualquer outra coisa — inclusive redirecionamento, que não seguimos — conta como falha.
- Repicamos até 6 vezes, com intervalo crescente (1 min, 5 min, 15 min, 1 h, 6 h) e então desistimos. O administrador vê na tela quantas desistiram e qual foi o erro.
- A entrega é “pelo menos uma vez”. Em reentrega da Meta ou repique nosso, o mesmo evento pode chegar duas vezes. Use o
entregaIdpara descartar o que você já processou — e faça o seu processamento idempotente, pelo mesmo motivo que nós fizemos o nosso.
O jeito mais seguro de escrever o endpoint é: confira a assinatura, grave o evento numa fila sua, responda 200 e processe depois. Processar antes de responder transforma qualquer lentidão sua em repique nosso.
9. Conferir a assinatura
Confira antes de confiar no corpo. A sua URL de webhook é pública; sem a conferência, qualquer um que descubra o endereço pode mandar um mensagem.recebida inventado e fazer o seu sistema agir. A assinatura é um HMAC-SHA256 sobre timestamp.corpo, com o segredo que aparece na tela de administração.
// Node.js (Express) — note o express.raw: o HMAC é sobre o corpo CRU,
// e JSON.parse + JSON.stringify muda o texto e invalida a conta.
const crypto = require('crypto')
app.post('/arkeflow/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.get('X-Arkeflow-Timestamp')
const assinatura = req.get('X-Arkeflow-Assinatura')
const corpo = req.body.toString('utf8')
// 1. O timestamp é recente? Sem isso, uma entrega capturada hoje vale para sempre.
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.sendStatus(401)
// 2. A assinatura bate? Comparação em tempo constante.
const esperada = 'sha256=' + crypto.createHmac('sha256', process.env.ARKEFLOW_SEGREDO)
.update(ts + '.' + corpo).digest('hex')
const a = Buffer.from(assinatura || '')
const b = Buffer.from(esperada)
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) return res.sendStatus(401)
// 3. Agora pode confiar.
const evento = JSON.parse(corpo)
enfileirarParaProcessar(evento) // responda rápido; processe depois
res.sendStatus(200)
})<?php
// PHP puro
$ts = $_SERVER['HTTP_X_ARKEFLOW_TIMESTAMP'] ?? '';
$assin = $_SERVER['HTTP_X_ARKEFLOW_ASSINATURA'] ?? '';
$corpo = file_get_contents('php://input');
if (abs(time() - (int) $ts) > 300) { http_response_code(401); exit; }
$esperada = 'sha256=' . hash_hmac('sha256', $ts . '.' . $corpo, getenv('ARKEFLOW_SEGREDO'));
if (!hash_equals($esperada, $assin)) { http_response_code(401); exit; }
$evento = json_decode($corpo, true);
// grave e responda; processe fora da requisição
http_response_code(200);Se você trocar o segredo na tela, as entregas em voo passam a ser recusadas pelo seu sistema até você atualizar a variável de ambiente. Troque nos dois lados na mesma janela.
10. Códigos de erro
| Código | O que aconteceu e o que fazer |
|---|---|
400 | Corpo inválido: JSON malformado, telefone impossível, variável do template faltando. O campo detalhes diz qual. Não tente de novo sem corrigir. |
401 | Chave ausente, inválida ou revogada. Confira o header Authorization. |
402 | Franquia do plano esgotada, ou assinatura inadimplente/cancelada. Quem resolve é o administrador da empresa; a chave de idempotência é liberada, então você pode repetir depois. |
403 | Três causas: o plano não inclui API, a empresa está suspensa, ou o contato está em opt-out/bloqueio. Opt-out não se contorna — é obrigação legal. |
404 | Template, contato ou campanha não encontrado. Template precisa estar sincronizado no ArkeFlow. |
409 | Template não aprovado pela Meta, chamada idêntica em andamento, ou a guarda automática de 60 s. Para a última, mande uma Idempotency-Key nova. |
422 | A Idempotency-Key já foi usada com um corpo diferente. Use uma chave nova. |
429 | Limite de chamadas. Espere o que diz o Retry-After — com recuo, não em laço. |
502 | A Meta recusou o envio; vem codigoMeta nos detalhes. É passageiro: a chave de idempotência é liberada e você pode repetir. |
503 | A conexão da empresa com a Meta não está configurada. Quem resolve é o administrador. |
Dúvida que a página não resolve? Fale com quem administra a conta do ArkeFlow na sua empresa — ou com a gente, pelo canal de suporte do contrato.