Follow-ups e retornos

Automatize a retomada de conversas com fluxos versionados e registre retornos prometidos ao contato.

Há três objetos diferentes. Fluxos são grafos versionados de espera, condição e envio, em que cada contato entra por uma inscrição. Retornos avulsos (/ai/followups/promises) são um único retorno agendado para um contato. As promessas do turno (GET /ai/promises) são o registro, somente leitura, do que o agente disse que faria; ficam em Acompanhamento.

Para o agente inscrever contatos, ligue followup.enabled na versão, liste os fluxos em followup.flow_ids e habilite as ferramentas followups.*.

Fluxos

Nós: trigger, wait, condition, ai_classify, match_reply, repeat, action, end. O grafo tem 2 a 60 nós e até 120 arestas; cada aresta tem condition always, branch (branch_id), class_match ou cond_result. Esperas vão de 5 minutos a 90 dias. Para começar de um modelo pronto, use from-model e leia o grafo gerado.

GET /ai/followup-flows

Escopo agents:read

Query customer_id. Fluxos com rascunho (graph), settings, status e published_version_id.

POST /ai/followup-flows

Escopo agents:write

CampoTipo e limitesUso
name1–80Nome do fluxo.
graph{nodes, edges}Grafo do rascunho.
settings.trigger{kind, params?, cancel_on_reply?}manual, webhook, stage_change {stage_id}, silence {threshold_minutes 5–10.080}, case_opened, appointment_no_show. conversation_end não inicia envios.
settings.handoff_policypause | cancel | allowO que fazer quando uma pessoa assume a conversa.
settings.send_start_hour · send_end_hour0–23 · 1–24Janela de envio; início antes do fim.
settings.send_days1–7 dias, 0 = domingoDias de envio.
settings.timezonefuso IANAFuso da janela.
settings.purpose · environmentutility|marketing · live|sandboxCategoria e ambiente do envio.
settings.trigger_agent_idUUID ou null, opcionalAgente responsável pelos gatilhos automáticos.

Exemplo validado em teste contra o schema da rota e a checagem de publicação:

{
  "customer_id": "11111111-1111-4111-8111-111111111111",
  "name": "Retomada em 4 horas",
  "graph": {
    "nodes": [
      {
        "id": "inicio",
        "type": "trigger",
        "label": "Início",
        "position": {
          "x": 0,
          "y": 0
        },
        "config": {}
      },
      {
        "id": "espera",
        "type": "wait",
        "label": "Esperar 4 horas",
        "position": {
          "x": 240,
          "y": 0
        },
        "config": {
          "mode": "fixed",
          "duration_ms": 14400000
        }
      },
      {
        "id": "lembrete",
        "type": "action",
        "label": "Lembrete",
        "position": {
          "x": 480,
          "y": 0
        },
        "config": {
          "mode": "text",
          "body": "Oi! Conseguiu ver a proposta que enviamos?"
        }
      },
      {
        "id": "fim",
        "type": "end",
        "label": "Fim",
        "position": {
          "x": 720,
          "y": 0
        },
        "config": {
          "outcome": "exhausted"
        }
      }
    ],
    "edges": [
      {
        "id": "e1",
        "source": "inicio",
        "target": "espera",
        "condition": {
          "type": "always"
        }
      },
      {
        "id": "e2",
        "source": "espera",
        "target": "lembrete",
        "condition": {
          "type": "always"
        }
      },
      {
        "id": "e3",
        "source": "lembrete",
        "target": "fim",
        "condition": {
          "type": "always"
        }
      }
    ]
  },
  "settings": {
    "trigger": {
      "kind": "manual",
      "cancel_on_reply": true
    },
    "handoff_policy": "pause",
    "send_start_hour": 9,
    "send_end_hour": 20,
    "send_days": [
      1,
      2,
      3,
      4,
      5
    ],
    "timezone": "America/Sao_Paulo",
    "purpose": "utility",
    "environment": "live"
  }
}

Responde 201 com o fluxo em rascunho.

GET /ai/followup-flows/from-model

Escopo agents:read

Modelos prontos: id, name, summary, journey, trigger_description, requires_stage e max_messages.

POST /ai/followup-flows/from-model

Escopo agents:write

Corpo {customer_id, model_id, name?, stage_id?}. stage_id é obrigatório nos modelos com requires_stage (422 stage_required). Responde 201 com o fluxo criado.

GET /ai/followup-flows/{id}

Escopo agents:read

Query customer_id.

PUT /ai/followup-flows/{id}

Escopo agents:write

Corpo {customer_id, expected_revision, name, graph, settings}. Salva o rascunho; a versão publicada não muda.

PATCH /ai/followup-flows/{id}

Escopo agents:write

Corpo {customer_id, expected_revision, status} com active, paused ou archived. Ativar exige versão publicada (422 publish_required).

POST /ai/followup-flows/{id}/publish

Escopo agents:write

Corpo {customer_id, expected_revision}. Valida alcance, caminhos até o fim, saídas cobertas, ciclos sem espera, templates e janela dos agentes que usam o fluxo. Com erro, responde 422 invalid_graph e validation_errors (node_id, code, message, branch_id).

GET /ai/followup-flows/{id}/versions

Escopo agents:read

Query customer_id. Versões publicadas, da mais nova à mais antiga.

POST /ai/followup-flows/{id}/rollback

Escopo agents:write

Corpo {customer_id, expected_revision, version_id}. Publica de novo uma versão anterior, com as mesmas validações.

Inscrições

GET /ai/followups/enrollments

Escopo agents:read

Query customer_id, flow_id, contact_id, status e limit (1–200, padrão 100). Status: active, waiting_reply, dormente, paused_handoff, paused_manual, completed, cancelled, dead.

POST /ai/followups/enrollments

Escopo agents:write

{
  "customer_id": "11111111-1111-4111-8111-111111111111",
  "flow_id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
  "contact_id": "88888888-8888-4888-8888-888888888888",
  "conversation_id": "99999999-9999-4999-8999-999999999999",
  "operation_key": "66666666-6666-4666-8666-666666666666",
  "agent_id": "44444444-4444-4444-8444-444444444444"
}

agent_id e appointment_id são opcionais. Repetir a operation_key devolve a mesma inscrição. Responde 201.

GET /ai/followups/enrollments/{id}

Escopo agents:read

Query customer_id. enrollment, events e effects (envios, classificações e planos de horário, com status, texto, template e consumo).

POST /ai/followups/enrollments/{id}/control

Escopo agents:write

CampoTipo e limitesUso
actionpause | resume | postpone | skip | cancelAção sobre a inscrição.
due_atISO com fusoNova data em postpone.
branch_idaté 64Saída a seguir em skip, quando o nó tem mais de uma.
noteaté 1.000Registro opcional.

Corpo com customer_id e expected_revision da inscrição. Envio em andamento ou incerto bloqueia a alteração (409 dispatch_busy).

Efeitos

POST /ai/followups/effects/{id}/decision

Escopo agents:write · chave criada por um Administrador

Corpo {customer_id, approve}. Aprova ou recusa um envio que aguarda decisão. Retorna {ok, id, status}. Pela API, a chave precisa ter sido criada por um Administrador (senão 403 forbidden); no painel, qualquer membro da conta (Operador ou Administrador) decide. A aprovação perde o efeito se, antes do envio, quem aprovou sair da conta ou a chave for revogada: o envio fica rejected com approval_revoked.

POST /ai/followups/effects/{id}/resolve

Escopo agents:write · chave criada por um Administrador

Concilia um envio com resultado incerto. outcome: sent, rejected ou cancelled; note com a evidência (10–2.000); wamid opcional. Retorna {resolved: true}. Pela API, a chave precisa ter sido criada por um Administrador (senão 403 forbidden); no painel, qualquer membro da conta (Operador ou Administrador) concilia.

{
  "customer_id": "11111111-1111-4111-8111-111111111111",
  "outcome": "sent",
  "note": "Mensagem conferida no WhatsApp Manager às 10h12.",
  "wamid": "wamid.HBgNNTUxMTk5OTk5ODg4OBUCABIYIDNBMEY"
}

Retornos avulsos

Um retorno avulso agenda uma única retomada do contato pelo agente. Na hora marcada, dentro da janela de 24 horas, o agente retoma a conversa. Fora dela, outside_window decide: alert avisa a equipe e template envia um template aprovado.

GET /ai/followups/promises

Escopo agents:read

Query customer_id, status, q (até 100), contact_id, cursor e limit (1–100, padrão 30). Paginação por cursor: a resposta é {data: {data: [...], next_cursor}}.

POST /ai/followups/promises

Escopo agents:write · chave criada por um Administrador

CampoTipo e limitesUso
contact_id · conversation_id · agent_idUUIDO agente precisa estar publicado, ativo e responder pelo canal da conversa.
operation_keyUUIDRepetir devolve o mesmo retorno com 200.
reason1–500Por que retornar.
promise1–1.000O que foi prometido.
context_snapshotaté 4.000, opcionalContexto para o agente.
promised_atISO com fusoEntre 5 minutos e 180 dias. Ajustado à janela do agente.
outside_windowalert | template, padrão alertFora da janela de 24 horas.
template_id · template_variablesobrigatório com templateTemplate aprovado no número da conversa e valores de todas as variáveis.
{
  "customer_id": "11111111-1111-4111-8111-111111111111",
  "contact_id": "88888888-8888-4888-8888-888888888888",
  "conversation_id": "99999999-9999-4999-8999-999999999999",
  "agent_id": "44444444-4444-4444-8444-444444444444",
  "operation_key": "66666666-6666-4666-8666-666666666666",
  "reason": "Cliente pediu retorno após falar com o sócio",
  "promise": "Retomar a proposta da cadeira ergonômica",
  "promised_at": "2026-10-02T10:00:00-03:00",
  "outside_window": "alert"
}

Responde 201 com o retorno (due_at é o horário efetivo). Um contato tem no máximo um retorno pendente: outro retorna 409 already_pending.

GET /ai/followups/promises/{id}

Escopo agents:read

Query customer_id. O retorno com contact, agent, run (turno disparado, com skip_reason) e alert.

POST /ai/followups/promises/{id}/cancel

Escopo agents:write · chave criada por um Administrador

Corpo {customer_id, reason?}. Repetir é idempotente. Retorno já disparado: 409 already_terminal.

POST /ai/followups/promises/{id}/resolve

Escopo agents:write · chave criada por um Administrador

Concilia o template de um retorno com status unknown. Corpo {customer_id, outcome: sent|rejected, note (10–2.000), wamid?}. Mesma regra de papel da conciliação de efeitos: chave criada por Administrador pela API; qualquer membro no painel.

Status de retorno: scheduled, turn_enqueued, template_pending, template_dispatching, alerted, completed, skipped, failed, unknown, cancelled.

Fila

GET /ai/followups/queue

Escopo agents:read

Inscrições e retornos numa lista só. Query customer_id, kind (enrollment ou promise), flow_id, status, q, contact_id, cursor e limit (1–100, padrão 30). Diferente das demais listas de IA, usa cursor, não page/per_page:

{
  "data": {
    "data": [
      {
        "kind": "promise",
        "id": "13131313-1313-4131-8131-131313131313",
        "contact_id": "88888888-8888-4888-8888-888888888888",
        "contact_name": "Ana",
        "contact_phone": "+5592988887777",
        "conversation_id": "99999999-9999-4999-8999-999999999999",
        "flow_id": null,
        "flow_name": null,
        "agent_id": "44444444-4444-4444-8444-444444444444",
        "agent_name": "Atendimento",
        "status": "scheduled",
        "node_id": null,
        "next_fire_at": "2026-10-02T13:00:00.000Z",
        "reason": "Cliente pediu retorno após falar com o sócio",
        "updated_at": "2026-09-25T13:00:00.000Z",
        "created_at": "2026-09-25T13:00:00.000Z",
        "last_error": null
      }
    ],
    "next_cursor": "eyJiIjoxLCJrIjoi..."
  }
}

Cursor inválido ou antigo: 422 invalid_cursor; recomece sem cursor.

StatusCódigoQuando
409revision_conflictFluxo ou inscrição alterados.
409dispatch_busyHá envio em andamento ou incerto.
409already_pending · already_terminalRetorno duplicado ou já encerrado.
409idempotency_conflictoperation_key reutilizada com outros dados.
409promise_not_unknownConciliação de retorno que não está incerto.
422invalid_graphGrafo inválido; veja validation_errors.
422promised_at_in_past · promised_at_out_of_window · no_send_windowHorário do retorno inválido.
422agent_unavailable · channel_owner_changed · conversation_closedAgente ou conversa não podem receber o retorno.
422template_required · template_unavailable · invalid_template_valuesTemplate ausente, não aprovado ou incompleto.
422incompatible_send_windowsFluxo e agente sem horário em comum.

Visão geral do Agente de IA · Próxima: Acompanhamento