Jornadas
Uma Jornada é um fluxo de conversa que você monta no app (Jornadas): mensagens, botões, perguntas, esperas, ramificações, pedidos de dado e a passagem para um atendente. Ela roda sozinha dentro do WhatsApp e do Instagram. A API pública deixa o seu sistema ver as Jornadas ativas de uma conta e iniciar uma execução de uma Jornada para uma pessoa, e os webhooks avisam o seu sistema do que acontece nelas.
Pela API só se inicia uma Jornada cujo gatilho é "API" (escolhido no editor). Jornadas de palavra-chave, comentário, anúncio e outros gatilhos começam sozinhas, por eventos da própria conversa.
Exige um plano com os recursos api, journeys e journeysPro (e instagram, para contas do Instagram). Sem eles a resposta é 403 (código 1390004).
O {id} do caminho#
As duas rotas valem para os dois canais, e o {id} é o do canal:
- WhatsApp: o
phone_number_iddo número. - Instagram: o
ig-user-idda conta.
A Synergy descobre o canal pelo id, com a mesma regra do envio de mensagens (Instagram): um id desconhecido é tentado como conta do Instagram uma vez, e, se a chave já gastou as tentativas do minuto, a resposta pode ser 429 com Retry-After: 1 em vez de 404. Repita.
Listar as Jornadas ativas#
GET /{version}/{id}/journeys, com uma chave de escopo messages ou read. Devolve as Jornadas ativas da conta, lidas da cópia que a conta guarda (sem consultar nada fora dela):
O id tem o formato jrn_ seguido de 16 caracteres (letras minúsculas de a a z e dígitos de 2 a 7). O trigger é o tipo do gatilho: keyword, comment, api e os outros do editor.
curl
curl 'https://api.synergyconnect.com.br/v25.0/17841400000000001/journeys' \
-H "Authorization: Bearer $SYNERGY_API_KEY"JavaScript
const res = await fetch('https://api.synergyconnect.com.br/v25.0/17841400000000001/journeys', {
headers: { Authorization: `Bearer ${process.env.SYNERGY_API_KEY}` },
});
const { data } = await res.json();
const api = data.filter((j) => j.trigger === 'api'); // as que se iniciam pela APIPython
import os
import requests
res = requests.get(
"https://api.synergyconnect.com.br/v25.0/17841400000000001/journeys",
headers={"Authorization": f"Bearer {os.environ['SYNERGY_API_KEY']}"},
)
api = [j for j in res.json()["data"] if j["trigger"] == "api"]{
"data": [
{ "id": "jrn_mfrggzdfmztwq2lk", "name": "Boas-vindas", "trigger": "keyword" },
{ "id": "jrn_nbswy3dpeb3w64tm", "name": "Pedido a caminho", "trigger": "api" }
]
}Iniciar uma execução#
POST /{version}/{id}/journeys/{journey_id}/runs, com uma chave de escopo messages.
| Parte | Regra |
|---|---|
Header Idempotency-Key | Obrigatório: de 8 a 128 caracteres (A-Z a-z 0-9 _ . : -). A mesma chave nunca inicia uma segunda execução |
recipient.id | No Instagram: o IGSID da pessoa |
recipient.wa_id | No WhatsApp: o telefone da pessoa, só dígitos (de 8 a 15) |
| Corpo | Só o recipient; qualquer outro campo é recusado com 400. Até 1 KB |
No WhatsApp, a pessoa não precisa ter uma conversa com a conta quando o primeiro bloco da Jornada é um modelo (template): a API cria o contato e a conversa e a Jornada abre com esse modelo, aprovado pela Meta, como qualquer modelo enviado a um número que nunca falou com você. Se o primeiro bloco for outro (uma mensagem de texto, por exemplo), a pessoa precisa já ter uma conversa com a conta, e a janela de 24 horas dela precisa estar aberta. No Instagram, nada muda: a pessoa precisa já ter uma conversa e a janela de 24 horas precisa estar aberta (Janela de atendimento de 24 horas). O envio de cada bloco da Jornada obedece às mesmas regras e tetos de uma Jornada que começou sozinha (janela, limites por pessoa e por hora, disjuntor).
No WhatsApp, o destinatário é o wa_id:
A resposta é 202: a execução foi criada e os blocos rodam em seguida, não dentro da chamada.
Idempotência#
Repetir a chamada com a mesma Idempotency-Key para a mesma pessoa devolve o mesmo run_id (202), sem iniciar outra execução. A chave vale por Jornada. Usar a mesma chave para outra pessoa é recusado com 422 (código 1390003). Para iniciar a mesma Jornada de novo para a mesma pessoa depois que a execução terminar, use uma chave nova.
Erros#
Todos no formato da Graph API (Erros).
| Status | Código | Quando |
|---|---|---|
| 400 | 100 | Falta a Idempotency-Key, ou ela não tem de 8 a 128 caracteres válidos; corpo inválido ou com campo desconhecido; a Jornada não existe nesta conta, está inativa ou não tem o gatilho api; a pessoa não tem conversa com a conta (no WhatsApp, isso só vale quando o primeiro bloco da Jornada não é um modelo); no Instagram, a janela de 24 horas fechou |
| 401 | 190 | Chave inválida, revogada, ou que não alcança a conta |
| 403 | 200 | A chave não tem o escopo, ou é de assistente e a conta é do Instagram |
| 403 | 1390004 | O plano não tem os recursos da Jornada (api, journeys, journeysPro e, no Instagram, instagram) |
| 403 | 1390005 | Conta em modo leitura ou suspensa |
| 404 | 100 | O id do caminho não é uma conta ou número desta organização, ou o journey_id não tem o formato de um id de Jornada |
| 409 | 1390001 | A conversa está com um atendente, ou com a automação de uma chave de API: a Jornada não toma a conversa |
| 409 | 1390016 | A pessoa já tem uma execução de Jornada em andamento, mesmo com uma Idempotency-Key nova: espere a Jornada atual terminar |
| 409 | 1390002 | Uma chamada com a mesma Idempotency-Key ainda está em andamento |
| 422 | 1390003 | A Idempotency-Key já foi usada para outra pessoa |
| 429 | 130429 | Um teto da Jornada barrou a entrada (por pessoa no dia, por hora, reentrada ou o disjuntor): Retry-After: 1. Uma execução barrada é contada nas métricas da Jornada |
curl
curl -X POST 'https://api.synergyconnect.com.br/v25.0/17841400000000001/journeys/jrn_nbswy3dpeb3w64tm/runs' \
-H "Authorization: Bearer $SYNERGY_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: pedido-42-a-caminho' \
-d '{"recipient":{"id":"17841400000000099"}}'JavaScript
const res = await fetch(
'https://api.synergyconnect.com.br/v25.0/17841400000000001/journeys/jrn_nbswy3dpeb3w64tm/runs',
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SYNERGY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'pedido-42-a-caminho',
},
body: JSON.stringify({ recipient: { id: '17841400000000099' } }),
},
);
const { run_id } = await res.json(); // 202Python
import os
import requests
res = requests.post(
"https://api.synergyconnect.com.br/v25.0/17841400000000001/journeys/jrn_nbswy3dpeb3w64tm/runs",
headers={
"Authorization": f"Bearer {os.environ['SYNERGY_API_KEY']}",
"Idempotency-Key": "pedido-42-a-caminho",
},
json={"recipient": {"id": "17841400000000099"}},
)
run_id = res.json()["run_id"] # 202{ "recipient": { "wa_id": "5511999999999" } }{ "run_id": 4821 }Webhooks da Jornada#
O campo synergy_journeys entrega os eventos de uma Jornada, nos mesmos webhooks de sempre (Webhooks), assinados como todos eles (Assinatura dos webhooks). Vale para os dois canais. Numa conta do Instagram o envelope sai com object: "instagram" e entry[].id igual ao ig-user-id (Instagram); numa do WhatsApp, com object: "whatsapp_business_account" e o id da conta do WhatsApp Business.
Hoje saem dois eventos:
| Evento | Quando |
|---|---|
link.clicked | A pessoa tocou num botão com link rastreado. Uma vez por execução e botão |
| o nome que você deu ao bloco | Um bloco "Webhook" em modo aviso foi executado: o event é o nome escolhido no editor, e o aviso vai só ao endpoint escolhido no bloco |
Um link.clicked no Instagram:
Um aviso de bloco (aqui o bloco se chama pedido_enviado), com o endpoint_id do webhook a quem o aviso se dirige:
Os demais nomes do catálogo de eventos (run.started, run.completed, run.ended, data.captured, journey.paused) ainda não são enviados: assine o campo, trate cada event pelo nome e ignore os que não conhece. Os eventos nunca levam token, link de mídia nem segredo.
{
"object": "instagram",
"entry": [{
"id": "17841400000000001",
"time": 1767225600,
"changes": [{
"field": "synergy_journeys",
"value": {
"messaging_product": "instagram",
"journeys": [{
"event": "link.clicked",
"journey_id": "jrn_nbswy3dpeb3w64tm",
"run_id": 4821,
"contact": { "igsid": "17841400000000099" },
"reason": null,
"data": { "link_key": "b1" },
"timestamp": "1767225600"
}]
}
}]
}]
}{
"field": "synergy_journeys",
"value": { "event": "pedido_enviado", "journey_id": "jrn_nbswy3dpeb3w64tm", "run_id": 4821, "endpoint_id": "7c1f0c7e-4b53-4c7b-9a51-3f6f0a2d9e10" }
}Bloco "Requisição HTTP"#
O bloco Requisição HTTP faz a Jornada chamar um endereço do seu sistema no meio da conversa, sem cadastrar webhook antes. Ele é configurado no editor: método (GET, POST, PUT, PATCH ou DELETE), endereço, cabeçalhos e corpo JSON. Exige o recurso journeysPro no plano.
Ele tem dois modos:
| Modo | O que acontece |
|---|---|
| Só enviar e seguir | O pedido sai e a Jornada segue pelo próximo bloco, qualquer que seja a resposta (200, 500 ou nenhuma). A resposta não é lida |
| Esperar a resposta | A Jornada espera a resposta, decide se deu certo pela regra de sucesso e segue por Deu certo ou Se falhar. Nas duas saídas ela pode guardar o status e valores do JSON da resposta em variáveis ou em campos do contato |
O que enviamos#
| Parte | Regra |
|---|---|
| Endereço | Só https://, porta padrão, endereço público, sem usuário e senha. Variáveis ({{contact.first_name}} e as outras do editor) só depois do ?, e cada valor sai codificado dentro do parâmetro: um valor nunca muda o host nem o caminho |
| Corpo | JSON, só com POST, PUT e PATCH. Cada variável fica dentro de uma string do molde e o valor sai escapado: um nome com aspas não muda a estrutura do JSON. Até 16 KB depois de preencher as variáveis |
| Seus cabeçalhos | Até 10, na ordem do editor: primeiro os de valor comum, depois os de valor secreto. Cabeçalho não aceita variável. Valor secreto é guardado cifrado e só é aberto na hora do envio, para este endereço (trocar o host ou o caminho pede o valor de novo) |
| Nossos cabeçalhos | Content-Type: application/json (só com corpo), User-Agent: Synergy-Journeys/1.0 e os X-Synergy-* da tabela abaixo. Vêm por último e valem sobre os seus; nomes como Host, Content-Length e X-Synergy-* nem podem ser configurados |
| Redirecionamento | Nunca é seguido: um 3xx é a resposta |
| Assinatura | O pedido não é assinado. Para conferir quem chama, use um cabeçalho secreto (por exemplo um Authorization com um token seu). Para eventos assinados, use o bloco de evento para webhook cadastrado (Webhooks) |
| Cabeçalho | Valor |
|---|---|
X-Synergy-Delivery-Id | Estável por execução e passo da Jornada, só com A-Za-z0-9_- (a mesma regra dos webhooks, Assinatura dos webhooks). É a chave para descartar repetição |
X-Synergy-Instance-Id | O id interno, na Synergy, do número ou da conta em que a Jornada roda (o mesmo dos outros webhooks) |
X-Synergy-Journey-Id | A Jornada (jrn_…) |
X-Synergy-Run-Id | A execução, o mesmo run_id da API |
X-Synergy-Block-Id | O bloco que chamou |
X-Synergy-Test | 1 quando o pedido veio do botão Testar requisição do editor (veja abaixo). Não vem nos pedidos de uma execução |
Prazos e limites#
| Item | Valor |
|---|---|
| Prazo do modo "Só enviar e seguir" | 5 segundos, fixo |
| Prazo do modo "Esperar a resposta" | De 1 a 10 segundos, escolhido no editor (padrão: 5) |
| Resposta lida | Até 64 KB. Acima disso, conta como falha |
| Valor guardado da resposta | Até 200 caracteres por valor; só texto, número ou verdadeiro/falso |
| Pedidos para o mesmo host | Até 300 por minuto por número ou conta. Acima disso, a execução espera 30 segundos e tenta de novo; depois de 15 minutos esperando, segue como falha |
No máximo uma vez#
A Synergy nunca reenvia um pedido. Se o prazo vence, se a conexão cai, ou se o envio foi interrompido do nosso lado com o pedido já a caminho, a Jornada segue em frente (pelo próximo bloco no modo "Só enviar e seguir", por Se falhar no modo "Esperar a resposta") e o pedido não é repetido. Não sabemos se o seu sistema recebeu; por isso, se receber o mesmo X-Synergy-Delivery-Id duas vezes, trate a segunda como repetição.
Responda rápido: faça o trabalho pesado depois de responder, para não estourar o prazo.
Regra de sucesso#
No modo "Esperar a resposta", a regra de sucesso diz quando a Jornada segue por Deu certo. Ela nasce como "status de 200 a 299" e você pode mudar:
- Status: uma lista de status (
404) ou faixas (200a299), de 200 a 599. Status fora da lista vai por Se falhar. Um404na lista conta como sucesso: guarde o status numa variável e use um bloco de condição logo depois para separar "achou" de "não achou". - Condição no corpo (opcional, uma só): um caminho do JSON (
ok,erro.codigo,itens[0].id) e um teste: "é igual a", "é diferente de", "está vazio ou não existe" ou "existe e não está vazio". "Vazio" é ausente,null,false,0,"",[]ou{}. Serve para a API que responde200com erro no corpo.
| A resposta | Saída |
|---|---|
| Status na regra, sem condição | Deu certo |
| Status na regra e condição verdadeira | Deu certo |
| Status fora da regra | Se falhar |
Condição falsa (ex.: 200 com { "ok": false }) | Se falhar |
| Com condição, ou com valor do corpo a guardar, e a resposta não é JSON, está vazia ou passa de 64 KB | Se falhar |
| Sem resposta: prazo, conexão, endereço que deixou de valer ou valor secreto que não abriu | Se falhar, com o status guardado como 0 |
Em Se falhar você também pode guardar o status e campos do corpo de erro (por exemplo error.message), para a mensagem seguinte dizer à pessoa o que houve.
Testar requisição#
No editor, o botão Testar requisição envia um pedido de verdade ao seu endereço, com valores de exemplo ou de um contato escolhido, e mostra o status, o tempo, alguns cabeçalhos da resposta e até 16 KB do corpo. Clicar num campo do JSON guarda esse campo numa variável ou num campo do contato.
O pedido de teste é igual ao de uma execução, com três diferenças: leva X-Synergy-Test: 1; o X-Synergy-Delivery-Id é novo a cada clique; e não leva X-Synergy-Instance-Id, X-Synergy-Journey-Id, X-Synergy-Run-Id nem X-Synergy-Block-Id. Se o seu sistema não deve gravar testes, olhe o X-Synergy-Test. Só administradores da organização testam, até 10 vezes por minuto.
POST /leads?origem=instagram HTTP/1.1
Host: api.seusistema.com.br
Accept: application/json
Authorization: <o valor secreto que você guardou>
Content-Type: application/json
User-Agent: Synergy-Journeys/1.0
X-Synergy-Delivery-Id: j_c4821_c7
X-Synergy-Instance-Id: 9f2c…
X-Synergy-Journey-Id: jrn_nbswy3dpeb3w64tm
X-Synergy-Run-Id: 4821
X-Synergy-Block-Id: crm
{ "nome": "Ana", "telefone": "5511999999999" }const seen = new Set(); // use o seu banco em produção
app.post('/leads', express.json(), (req, res) => {
if (req.get('X-Synergy-Test') === '1') return res.json({ ok: true, teste: true }); // não grave testes
const delivery = req.get('X-Synergy-Delivery-Id');
if (seen.has(delivery)) return res.json({ ok: true }); // repetição: mesma resposta, nada novo
seen.add(delivery);
res.json({ ok: true, cliente: { plano: 'ouro' } });
});Boas práticas#
- Gere a
Idempotency-Keya partir do fato do seu sistema (o id do pedido, do ticket), nunca aleatória: é ela que protege contra uma repetição. - Em 429, espere o
Retry-After. Em 409 (1390016), a pessoa já está numa execução: espere ela terminar; repetir com outra chave não adianta. - Antes de iniciar, liste as Jornadas e confira que a Jornada ainda está ativa e é do gatilho
api: o editor pode ter pausado ou trocado o gatilho. - Para listar sem iniciar nada, uma chave
readbasta.
Ficou com dúvida? Escreva para suporte@synergyconnect.com.br.