WhatsApp Flows
Um Flow é um formulário que o cliente preenche dentro do WhatsApp. Pela API você envia o Flow e recebe a resposta por webhook. Exige a feature waFlows no plano e o Flow criado no app.
Fluxo#
- Emita um
flow_tokencomPOST /v1/numbers/{phone_number_id}/flows/{flow_id}/token. - Envie o Flow com
POST /{version}/{phone_number_id}/messages, com o token eminteractive.action.parameters.flow_token. - Receba a resposta no webhook
messages.
1. Emitir o token#
Escopo messages; o corpo tem até 4 KB. O token vale para este número, este Flow e este destinatário, por 3 dias. O destinatário vai no corpo (to: só dígitos, de 8 a 20, com DDI), nunca no caminho. context (opcional) é um objeto de textos validado pelo tipo do Flow.
Resposta 200: { "flow_token": "v1.…", "expires_at": "2026-01-04T00:00:00.000Z" }.
Erros: 400 (to ou context inválido, corpo acima de 4 KB), 403 código 1390007 (plano sem Flows), 404 (o Flow não é deste número), 409 código 1390008 (o Flow usa o endpoint de outro provedor, está em rascunho ou não pode ser enviado). Se o destinatário ainda não é contato do número, ele é criado ao emitir o token.
curl
curl -X POST 'https://api.synergyconnect.com.br/v1/numbers/106540352242922/flows/1234567890/token' \
-H "Authorization: Bearer $SYNERGY_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"to":"5511999999999"}'JavaScript
const res = await fetch('https://api.synergyconnect.com.br/v1/numbers/106540352242922/flows/1234567890/token', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SYNERGY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ to: '5511999999999' }),
});
const { flow_token, expires_at } = await res.json();Python
import os
import requests
res = requests.post(
"https://api.synergyconnect.com.br/v1/numbers/106540352242922/flows/1234567890/token",
headers={"Authorization": f"Bearer {os.environ['SYNERGY_API_KEY']}"},
json={"to": "5511999999999"},
)
data = res.json() # data["flow_token"], data["expires_at"]2. Enviar o Flow#
O envio é o da Cloud API: uma mensagem interactive do tipo flow, ou um modelo com botão de Flow. O corpo vai à Meta sem alteração. O token é obrigatório quando o Flow usa o endpoint da Synergy; sem ele, 400 código 1390009.
Os campos do corpo (flow_message_version, flow_cta etc.) são os da Meta: a Synergy só lê o token. O envio de Flow também conta no limite de envios de Flow do plano (429 código 1390010 ao esgotar).
curl
curl -X POST 'https://api.synergyconnect.com.br/v25.0/106540352242922/messages' \
-H "Authorization: Bearer $SYNERGY_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"messaging_product": "whatsapp",
"to": "5511999999999",
"type": "interactive",
"interactive": {
"type": "flow",
"body": { "text": "Preencha o formulário" },
"action": {
"name": "flow",
"parameters": {
"flow_message_version": "3",
"flow_id": "1234567890",
"flow_cta": "Abrir",
"flow_token": "'"$FLOW_TOKEN"'"
}
}
}
}'JavaScript
const res = await fetch('https://api.synergyconnect.com.br/v25.0/106540352242922/messages', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SYNERGY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messaging_product: 'whatsapp',
to: '5511999999999',
type: 'interactive',
interactive: {
type: 'flow',
body: { text: 'Preencha o formulário' },
action: {
name: 'flow',
parameters: { flow_message_version: '3', flow_id: '1234567890', flow_cta: 'Abrir', flow_token },
},
},
}),
});
console.log(res.status, await res.json());Python
res = requests.post(
"https://api.synergyconnect.com.br/v25.0/106540352242922/messages",
headers={"Authorization": f"Bearer {os.environ['SYNERGY_API_KEY']}"},
json={
"messaging_product": "whatsapp",
"to": "5511999999999",
"type": "interactive",
"interactive": {
"type": "flow",
"body": {"text": "Preencha o formulário"},
"action": {
"name": "flow",
"parameters": {
"flow_message_version": "3",
"flow_id": "1234567890",
"flow_cta": "Abrir",
"flow_token": flow_token,
},
},
},
},
)
print(res.status_code, res.json())3. Receber a resposta#
Quando o cliente envia o formulário, a Meta entrega uma mensagem interactive com nfm_reply. Ela chega no webhook que assina messages, com o value da Meta, sem alteração. response_json é um texto JSON com as respostas e o flow_token. O recebimento nunca é bloqueado pelo plano.
Use X-Synergy-Delivery-Id e o messages[].id para descartar repetições (Webhooks). A assinatura se valida como nos demais eventos. O campo flows da Meta (eventos de saúde do endpoint) não é entregue a webhooks.
{
"object": "whatsapp_business_account",
"entry": [{ "id": "102290129340398", "time": 1767225600, "changes": [{
"field": "messages",
"value": {
"messaging_product": "whatsapp",
"metadata": { "display_phone_number": "554430000000", "phone_number_id": "106540352242922" },
"messages": [{
"from": "5511999999999", "id": "wamid.…", "timestamp": "1767225600", "type": "interactive",
"interactive": { "type": "nfm_reply", "nfm_reply": { "name": "flow", "body": "Sent", "response_json": "{\"flow_token\":\"v1.…\",\"nome\":\"Maria\"}" } }
}]
}
}] }]
}JavaScript
function onFlowReply(message) {
if (message.type !== 'interactive' || message.interactive?.type !== 'nfm_reply') return;
const answers = JSON.parse(message.interactive.nfm_reply.response_json);
// answers.flow_token identifica o envio; o resto são os campos do formulário
}Python
import json
def on_flow_reply(message):
interactive = message.get("interactive", {})
if message.get("type") != "interactive" or interactive.get("type") != "nfm_reply":
return
answers = json.loads(interactive["nfm_reply"]["response_json"])
# answers["flow_token"] identifica o envio; o resto são os campos do formulárioFicou com dúvida? Escreva para suporte@synergyconnect.com.br.