Synergy Connect

Documentação

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_id do número.
  • Instagram: o ig-user-id da 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.

Exemplo

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 API

Python

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"]
JSON
{
  "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.

ParteRegra
Header Idempotency-KeyObrigatório: de 8 a 128 caracteres (A-Z a-z 0-9 _ . : -). A mesma chave nunca inicia uma segunda execução
recipient.idNo Instagram: o IGSID da pessoa
recipient.wa_idNo WhatsApp: o telefone da pessoa, só dígitos (de 8 a 15)
CorpoSó 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).

StatusCódigoQuando
400100Falta 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
401190Chave inválida, revogada, ou que não alcança a conta
403200A chave não tem o escopo, ou é de assistente e a conta é do Instagram
4031390004O plano não tem os recursos da Jornada (api, journeys, journeysPro e, no Instagram, instagram)
4031390005Conta em modo leitura ou suspensa
404100O 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
4091390001A conversa está com um atendente, ou com a automação de uma chave de API: a Jornada não toma a conversa
4091390016A pessoa já tem uma execução de Jornada em andamento, mesmo com uma Idempotency-Key nova: espere a Jornada atual terminar
4091390002Uma chamada com a mesma Idempotency-Key ainda está em andamento
4221390003A Idempotency-Key já foi usada para outra pessoa
429130429Um 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
Exemplo

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(); // 202

Python

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
JSON
{ "recipient": { "wa_id": "5511999999999" } }
JSON
{ "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:

EventoQuando
link.clickedA pessoa tocou num botão com link rastreado. Uma vez por execução e botão
o nome que você deu ao blocoUm 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.

JSON
{
  "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"
        }]
      }
    }]
  }]
}
JSON
{
  "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:

ModoO que acontece
Só enviar e seguirO 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 respostaA 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#

ParteRegra
EndereçoSó 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
CorpoJSON, 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çalhosAté 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çalhosContent-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
RedirecionamentoNunca é seguido: um 3xx é a resposta
AssinaturaO 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çalhoValor
X-Synergy-Delivery-IdEstá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-IdO id interno, na Synergy, do número ou da conta em que a Jornada roda (o mesmo dos outros webhooks)
X-Synergy-Journey-IdA Jornada (jrn_…)
X-Synergy-Run-IdA execução, o mesmo run_id da API
X-Synergy-Block-IdO bloco que chamou
X-Synergy-Test1 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#

ItemValor
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 lidaAté 64 KB. Acima disso, conta como falha
Valor guardado da respostaAté 200 caracteres por valor; só texto, número ou verdadeiro/falso
Pedidos para o mesmo hostAté 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 (200 a 299), de 200 a 599. Status fora da lista vai por Se falhar. Um 404 na 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 responde 200 com erro no corpo.
A respostaSaída
Status na regra, sem condiçãoDeu certo
Status na regra e condição verdadeiraDeu certo
Status fora da regraSe 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 KBSe falhar
Sem resposta: prazo, conexão, endereço que deixou de valer ou valor secreto que não abriuSe 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.

Texto
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" }
Exemplo
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-Key a 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 read basta.

Ficou com dúvida? Escreva para suporte@synergyconnect.com.br.