Agent Bots para agentes de IA

Como um agente de IA externo opera o NooviChat: autentica como um Agent Bot, lê o histórico da conversa, responde, movimenta o CRM, agenda e programa follow-up. Esta página descreve a superfície exata que um token de bot alcança, a permissão que cada rota exige e o corpo de resposta de cada recusa.

Modelo de acesso

Um Agent Bot é um ator não-humano com token próprio. Ele não é um usuário, não ocupa assento, não aparece em listagem de agentes e não pode ser atribuído como responsável de card. O que ele alcança é decidido por três travas independentes, avaliadas nesta ordem. Falhar em qualquer uma encerra a requisição.

TravaPerguntaFalha responde
1. SuperfícieA rota e a ação estão na lista de superfície de bot?401
2. PermissãoO bot carrega ao menos uma das permissões exigidas por aquela superfície?401
3. Vínculo de contaO bot está ligado, por vínculo de inbox ativo, à conta do path?404

Permissão sem vínculo não abre nada

As travas 2 e 3 são independentes. Um bot com pipeline_manage mas sem nenhum vínculo de inbox ativo naquela conta recebe 404 em toda rota da conta — inclusive nas que a permissão dele cobre. E um bot vinculado a uma inbox, porém sem permissão nenhuma, recebe 401 em toda rota. Não existe caminho em que uma compense a outra.

O padrão é não poder nada

A lista de permissões de um bot recém-criado é vazia. Enquanto um administrador da conta não gravar permissões nele, toda chamada autenticada com o token desse bot responde 401 com {"error":"Access to this endpoint is not authorized for bots"}. Atualizar a versão do NooviChat não concede permissão a bot nenhum.

Um bot serve uma conta só

Um Agent Bot pode estar vinculado a várias inboxes, mas todas precisam pertencer à mesma conta. Tentar salvar vínculos em contas diferentes é recusado na gravação. Um agente de IA que atende vários clientes precisa de um bot e um token por conta — não existe token multi-conta.

Autenticação

O token do bot vai no header api_access_token. O NooviChat não lê Authorization: Bearer nesta API: uma requisição que use apenas Authorization é tratada como não autenticada e cai no fluxo de login de usuário, não no de bot.

bash
curl -s "https://chat.seudominio.com/api/v1/accounts/1/conversations" \
  -H "api_access_token: SEU_TOKEN_DE_BOT" \
  -H "Content-Type: application/json"

Header

NomeTipoObrigatorioDescricao
api_access_token(header)stringSimToken do Agent Bot. Identifica o bot e a conta que ele serve.
Content-Type(header)stringNaoapplication/json nas requisições com corpo JSON.

Como o agente descobre o account_id

O account_id não vem do token: ele faz parte do path de toda rota. O agente precisa recebê-lo na configuração. Se chamar com o id de outra conta, a resposta é 404 com {"error":"Resource could not be found"} — a mesma resposta de uma conta inexistente, de propósito, para não revelar quais contas existem.

Descobrir o que você pode

Chame isto antesde planejar uma tarefa. É a única rota liberada a qualquer agent-bot, inclusive um que ainda não tem permissão nenhuma — justamente para que a resposta a "o que eu posso fazer?" nunca seja a mesma negação genérica das outras rotas.

GET/api/v1/accounts/{account_id}/agent_bots/me

Devolve as permissões concedidas, as caixas de entrada vinculadas e a lista de superfícies com o campo granted.

json
{
  "id": 118,
  "name": "Hermes Teste",
  "account_id": 1,
  "permissions": ["conversation_manage", "pipeline_manage", "appointment_manage"],
  "inboxes": [{ "id": 18499, "name": "Vendas Teste" }],
  "pipeline_actions": ["cards_move", "cards_win"],
  "pipeline_actions_effect": "narrowed",
  "pipelines": [{ "id": 15236, "name": "Funil de Vendas" }],
  "endpoints": [
    {
      "surface": "api/v1/accounts/conversations",
      "actions": ["index", "show", "filter", "search", "create", "update"],
      "requires_any_of": ["conversation_manage", "conversation_unassigned_manage"],
      "granted": true
    }
  ]
}

Use granted para planejar, não para adivinhar

granted já cruza as suas permissões com o que cada superfície exige. Se vier false, a tarefa não é possível com este token — o certo é dizer isso a quem pediu e nomear a permissão de requires_any_of, em vez de tentar e falhar. Lembre que inboxes também limita: permissão sem vínculo não abre nada.

Lista vazia significa coisas opostas nestes dois campos

pipeline_actions vazio significa todas as ações que pipeline_manage concede — a lista estreita, ela não é requisito. Já pipelines vazio significa nenhum: o bot não alcança funil algum. Não deduza pela lista; leia pipeline_actions_effect, que responde em uma palavra: unrestricted ou narrowed.

As ações possíveis são cards_move, cards_win, cards_lose, cards_edit, cards_assign (trocar o responsável do card) e pipelines_manage.

Permissões do bot

As permissões de um bot usam o mesmo vocabulário das funções personalizadas de usuário, mais uma exclusiva de bot. Elas ficam em bot_config.permissions, um array de strings. Um valor fora deste catálogo faz a gravação falhar com 422.

PermissãoO que libera
conversation_manageTodas as conversas da conta
conversation_unassigned_manageConversas não atribuídas e as próprias
conversation_participating_manageConversas próprias ou em que participa
conversation_awaiting_reply_viewVer a lista "Sem resposta"
contact_manageCRUD de contatos
report_manageRelatórios
knowledge_base_managePortais e artigos da central de ajuda
pipeline_managePipeline: CRUD e automações
pipeline_viewPipeline: somente leitura
follow_up_manageFollow-up: templates e automações
appointment_manageAtendimentos: agendar, remarcar e configurar (NÃO cancela)
appointment_cancelCancelar atendimento — permissão própria, destrutiva e irreversível (exclusiva de bot)
commercial_analysis_manageAnálise Comercial
conversation_private_notes_viewLer notas internas no histórico da conversa (exclusiva de bot)

report_manage, knowledge_base_manage e commercial_analysis_manage

São permissões válidas de gravar num bot, mas nenhuma superfície de bot as exige hoje. Conceder qualquer uma delas não abre rota alguma para o token do bot. Concedê-las não é erro — é apenas inerte.

Provisionar o bot

Os três passos abaixo exigem um token de administrador da conta. Um bot não consegue se autoprovisionar: as rotas de agent_bots e de inboxes não fazem parte da superfície de bot e respondem 401 para um token de bot.

POST/api/v1/accounts/{account_id}/agent_bots

1. Cria o bot. O token de acesso é devolvido apenas para administradores.

bash
curl -s -X POST "https://chat.seudominio.com/api/v1/accounts/1/agent_bots" \
  -H "api_access_token: TOKEN_DE_ADMIN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Agente Hermes",
    "description": "Agente de IA externo",
    "outgoing_url": "https://meuagente.exemplo.com/hooks/noovichat"
  }'
200Bot criado. Guarde access_token — é o valor do header api_access_token.
json
{
  "id": 7,
  "name": "Agente Hermes",
  "description": "Agente de IA externo",
  "outgoing_url": "https://meuagente.exemplo.com/hooks/noovichat",
  "bot_type": "webhook",
  "bot_config": {},
  "account_id": 1,
  "access_token": "AbCdEf...",
  "secret": "...",
  "system_bot": false
}
PATCH/api/v1/accounts/{account_id}/agent_bots/{id}

2. Concede as permissões. bot_config é substituído inteiro — envie sempre a lista completa.

bash
curl -s -X PATCH "https://chat.seudominio.com/api/v1/accounts/1/agent_bots/7" \
  -H "api_access_token: TOKEN_DE_ADMIN" \
  -H "Content-Type: application/json" \
  -d '{
    "bot_config": {
      "permissions": [
        "conversation_manage",
        "contact_manage",
        "pipeline_manage",
        "follow_up_manage",
        "appointment_manage"
      ]
    }
  }'
422Permissão fora do catálogo.
json
{
  "message": "unknown bot permissions: pipeline_admin",
  "attributes": ["base"]
}
POST/api/v1/accounts/{account_id}/inboxes/{inbox_id}/set_agent_bot

3. Vincula o bot a uma inbox. É este vínculo que faz o bot servir a conta.

Body

NomeTipoObrigatorioDescricao
agent_botintegerSimId do bot. Enviar null remove o vínculo da inbox.
bash
curl -s -X POST "https://chat.seudominio.com/api/v1/accounts/1/inboxes/42/set_agent_bot" \
  -H "api_access_token: TOKEN_DE_ADMIN" \
  -H "Content-Type: application/json" \
  -d '{"agent_bot": 7}'
200Vínculo criado. O corpo é vazio.

Uma inbox tem no máximo um bot

Vincular um segundo bot à mesma inbox substitui o anterior. Desativar ou remover o vínculo derruba o acesso do bot à conta inteira: a partir daí toda rota volta a responder 404, mesmo com o token e as permissões intactos.

Superfície completa

Esta é a lista integral do que um token de bot alcança. Qualquer rota ou ação fora dela responde 401 com {"error":"Access to this endpoint is not authorized for bots"}, independentemente das permissões gravadas. Quando a coluna de permissão lista mais de um valor, basta o bot carregar um deles.

SuperfíciePermissão que abreAções liberadas
Conversasconversation_manage OU conversation_unassigned_manage OU conversation_participating_manageindex, show, filter, search, create, update, meta, toggle_status, toggle_priority, toggle_typing_status, custom_attributes, unread, update_last_seen, mute, unmute, attachments
Mensagens da conversaconversation_manage OU conversation_unassigned_manage OU conversation_participating_manageindex, create
Atribuição de conversaconversation_managecreate
Etiquetas da conversaconversation_manageindex, create
Follow-ups da conversafollow_up_manageindex, show, create, update, count
Contatoscontact_manageindex, show, search, create, update
Funis (pipelines)pipeline_manage OU pipeline_viewindex, show, stages, create, update
Cards do funilpipeline_manage OU pipeline_viewindex, show, create, update, move_to_stage, reorder, recalculate_score, update_qualification_checklist
Status do negóciopipeline_managemark_won, mark_lost, reopen
Catálogo de produtospipeline_manage OU pipeline_viewindex, show, create, update, performance
Oportunidades (razão financeiro)pipeline_manageindex, create, void
Vínculo card ↔ conversapipeline_managecreate
Vínculo card ↔ contatopipeline_managecreate
Atividades do funilpipeline_manage OU pipeline_viewindex, create, timeline
Anexos do cardpipeline_manageindex, create
Responsável do cardpipeline_manageassign
Regras de follow-up do funilfollow_up_manage OU pipeline_manageindex, show, create, update
Agendamentosappointment_manage (destroy exige appointment_cancel)index, show, create, update, confirm, complete, no_show, availability, availability_range, available_professionals, clients, destroy
Profissionaisappointment_manageindex, show, availability
Serviçosappointment_manageindex, show

Apagar está fora da superfície

Nenhuma ação destroy está liberada para bot. Apagar conversa, contato, card, agendamento, follow-up, anexo ou regra responde 401. Não há parâmetro, header ou permissão que habilite isso. Se o seu fluxo depende de remover um registro, ele precisa de um usuário humano ou de um token de administrador.

Cancelar e reenviar follow-up também estão fora

As ações cancel e retry_send de follow-up não pertencem à superfície de bot: respondem 401, inclusive para um follow-up que o próprio bot criou. Além disso, um follow-up com autoria de bot só pode ser cancelado ou reenviado por um administrador da conta — nem o usuário dono da conversa consegue.

Contrato de erros

Os corpos abaixo são literais. As mensagens marcadas como traduzidas seguem o idioma configurado na conta; o status HTTP e as chaves JSON não mudam.

StatusCorpoQuando
401{"error":"Invalid Access Token"}O valor do header api_access_token não corresponde a nenhum token. Não há retry útil.
401{"error":"Access to this endpoint is not authorized for bots"}A rota não está na superfície de bot, OU a ação não está liberada naquela superfície, OU o bot não carrega nenhuma das permissões exigidas por ela. Corrigir exige conceder a permissão no bot — não adianta repetir a chamada.
401{"error":"You are not authorized to do this action"}A rota está na superfície e a permissão existe, mas a policy negou o registro específico (ex.: card fora de um funil que o bot alcança). Vale para as rotas fora do namespace /pipeline/.
403{"error":"forbidden"}Negação de policy nas rotas de Agendamento (appointments).
403{"error":"<mensagem no idioma da conta>"}Negação de policy nas rotas do namespace /pipeline/ e nas regras de follow-up do funil. Em pt-BR: "Você não está autorizado a acessar esta conta".
403{"error":"...","code":"pipeline_board_disabled"}A conta não tem o módulo Pipeline habilitado. O campo code é estável; a mensagem é traduzida.
403{"error":"API access is not enabled for this account"}A conta está com o acesso por API desligado. Nenhuma permissão do bot contorna isso.
404{"error":"Resource could not be found"}A conta do path não existe, está suspensa, o account_id é malformado, OU o bot não serve aquela conta. Também responde assim quando o registro pedido está fora do recorte que o bot enxerga. Não distingue os casos de propósito.
404{"error":"Pipeline card not found"}Card inexistente ou em um funil que não listou este bot. Específico das rotas de status do negócio.
422{"error":"<mensagem>"}Payload rejeitado pela validação. Usado por mensagens, follow-ups e a maior parte das rotas de card.
422{"errors":["<mensagem>", "..."]}Payload rejeitado nas regras de follow-up do funil. Note a chave no plural e o array — não é o mesmo formato acima.
422{"message":"<mensagem>","attributes":["<campo>"]}Payload rejeitado por ActiveRecord::RecordInvalid (ex.: gravar bot_config com permissão desconhecida).

Como um agente deve reagir a cada classe

401 e 403 são estados de configuração: repetir a chamada produz o mesmo resultado. Registre e pare. 404 pode ser configuração (conta errada, vínculo removido) ou recorte de visibilidade (o registro existe mas o bot não o alcança) — as duas não são distinguíveis pela resposta. 422 é o único caso em que reformular o payload tem chance de sucesso.

Atendimento

MétodoRotaPermissão exigida
GET/api/v1/accounts/{account_id}/conversationsconversation_manage | conversation_unassigned_manage | conversation_participating_manage
GET/api/v1/accounts/{account_id}/conversations/metaidem
GET/api/v1/accounts/{account_id}/conversations/search?q=idem
POST/api/v1/accounts/{account_id}/conversations/filteridem
GET/api/v1/accounts/{account_id}/conversations/{display_id}idem
POST/api/v1/accounts/{account_id}/conversationsidem
PATCH/api/v1/accounts/{account_id}/conversations/{display_id}idem
POST/api/v1/accounts/{account_id}/conversations/{display_id}/toggle_statusidem
POST/api/v1/accounts/{account_id}/conversations/{display_id}/toggle_priorityidem
POST/api/v1/accounts/{account_id}/conversations/{display_id}/toggle_typing_statusidem
POST/api/v1/accounts/{account_id}/conversations/{display_id}/custom_attributesidem
POST/api/v1/accounts/{account_id}/conversations/{display_id}/unreadidem
POST/api/v1/accounts/{account_id}/conversations/{display_id}/update_last_seenidem
POST/api/v1/accounts/{account_id}/conversations/{display_id}/muteidem
POST/api/v1/accounts/{account_id}/conversations/{display_id}/unmuteidem
GET/api/v1/accounts/{account_id}/conversations/{display_id}/attachmentsidem
GET/api/v1/accounts/{account_id}/conversations/{display_id}/messagesidem
POST/api/v1/accounts/{account_id}/conversations/{display_id}/messagesidem
POST/api/v1/accounts/{account_id}/conversations/{display_id}/assignmentsconversation_manage
GET/api/v1/accounts/{account_id}/conversations/{display_id}/labelsconversation_manage
POST/api/v1/accounts/{account_id}/conversations/{display_id}/labelsconversation_manage
GET/api/v1/accounts/{account_id}/contactscontact_manage
GET/api/v1/accounts/{account_id}/contacts/search?q=contact_manage
GET/api/v1/accounts/{account_id}/contacts/{id}contact_manage
POST/api/v1/accounts/{account_id}/contactscontact_manage
PATCH/api/v1/accounts/{account_id}/contacts/{id}contact_manage

Recorte de listagem

GET /conversations devolve apenas conversas das inboxes em que o bot tem vínculo ativo. Não existe modo administrador para bot: um bot ligado a uma inbox não enxerga a conta inteira. Já GET /conversations/{display_id} alcança qualquer conversa da conta que o bot serve — listar e abrir têm recortes diferentes.

Ler o histórico da conversa

GET/api/v1/accounts/{account_id}/conversations/{display_id}/messages

Retorna mensagens da conversa. O identificador no path é o display_id da conversa, não o id interno.

Query

NomeTipoObrigatorioDescricao
after(query)integerNaoCursor. Devolve até 100 mensagens com id maior que o valor, em ordem cronológica crescente.
before(query)integerNaoCursor. Devolve até 20 mensagens com id menor que o valor, em ordem cronológica crescente.
bash
curl -s "https://chat.seudominio.com/api/v1/accounts/1/conversations/318/messages" \
  -H "api_access_token: SEU_TOKEN_DE_BOT"
200As mensagens vêm em payload; meta traz contato, responsável e etiquetas.
json
{
  "meta": {
    "labels": ["lead-quente"],
    "additional_attributes": {},
    "contact": { "id": 91, "name": "Ana Souza", "phone_number": "+5511999990000", "type": "contact" },
    "assignee": { "id": 4, "name": "Carlos", "type": "user" },
    "agent_last_seen_at": 1771596000,
    "assignee_last_seen_at": 1771596000
  },
  "payload": [
    {
      "id": 88120,
      "content": "Bom dia, ainda da tempo de agendar?",
      "message_type": 0,
      "content_type": "text",
      "private": false,
      "created_at": 1771595880,
      "conversation_id": 318,
      "inbox_id": 42,
      "sender": { "id": 91, "name": "Ana Souza", "type": "contact" },
      "attachments": []
    },
    {
      "id": 88121,
      "content": "Bom dia, Ana! Da sim.",
      "message_type": 1,
      "content_type": "text",
      "private": false,
      "created_at": 1771595940,
      "conversation_id": 318,
      "inbox_id": 42,
      "sender": { "id": 7, "name": "Agente Hermes", "type": "agent_bot" }
    }
  ]
}

Sem cursor, o agente vê só o fim da conversa

Uma chamada sem after nem before devolve as 20 mensagens mais recentes, e nada indica no corpo que existe mais histórico atrás. Um agente que chama uma única vez e conclui a partir daí está lendo um recorte, não a conversa. Para o histórico inteiro, pagine.

Varredura completa, do início ao fim: comece com after=0 e avance usando o maior id devolvido. Pare quando payload vier vazio ou com menos de 100 itens.

bash
BASE="https://chat.seudominio.com/api/v1/accounts/1/conversations/318/messages"
CURSOR=0

while : ; do
  PAGE=$(curl -s "$BASE?after=$CURSOR" -H "api_access_token: SEU_TOKEN_DE_BOT")
  COUNT=$(echo "$PAGE" | jq '.payload | length')
  [ "$COUNT" -eq 0 ] && break
  echo "$PAGE" | jq -c '.payload[]'
  CURSOR=$(echo "$PAGE" | jq '.payload[-1].id')
  [ "$COUNT" -lt 100 ] && break
done

Limites por forma de paginação

after sozinho: até 100 por página. before sozinho: até 20. after e before juntos: até 1000 mensagens no intervalo [after, before). Sem cursor: 20. Estes tetos são fixos e não há parâmetro de tamanho de página.

Saber o que você ainda não viu

O meta da resposta declara a paginação. Confira sempre — o truncamento é invisível sem estes campos, e o modo after corta jogando fora as mensagens mais recentes: é exatamente o que esconde a mensagem que você acabou de enviar, levando o agente a concluir que a entrega falhou e reenviar ao cliente.

json
"meta": {
  "total_count": 108,        // total real da conversa
  "returned_count": 100,     // quantas vieram nesta pagina
  "has_more_before": false,  // ha mensagens ANTES da mais antiga desta pagina
  "has_more_after": true,    // ha mensagens DEPOIS da mais recente desta pagina
  "oldest_id": 83390,
  "newest_id": 83489
}

has_more_after true significa que falta o fim

Continue paginando enquanto has_more_after for true, usando o newest_id como próximo after. Para confirmar um envio recente, prefira a leitura sem cursor (as 20 mais recentes) em vez de after=0 — com 108 mensagens, after=0 devolve 100 e não inclui a última.

Interpretar cada mensagem

Três campos decidem o significado de uma mensagem. Ignorar qualquer um leva o agente a responder ao próprio texto ou a repetir para o cliente uma nota escrita entre atendentes.

CampoValoresLeitura
message_type0 | 1 | 2 | 30 incoming (o cliente escreveu), 1 outgoing (a empresa escreveu — agente, bot ou automação), 2 activity (evento do sistema: status, atribuição), 3 template. O campo é numérico, não textual.
privatetrue | falsetrue é nota interna: o cliente nunca viu esse texto. Nunca cite o conteúdo de uma nota interna numa resposta ao cliente.
sendertype: contact | user | agent_botQuem escreveu. Compare sender.id com o id do próprio bot para reconhecer as próprias mensagens. Pode vir ausente em mensagens sem remetente (ex.: eventos de sistema).

Notas internas exigem permissão própria

Sem conversation_private_notes_view, as mensagens com private: true são removidas da consulta, antes do limite de página. O bot recebe o histórico completo sem elas e nenhum aviso de que algo foi omitido. Com a permissão, elas chegam marcadas com private: true. Note que uma página pode vir com menos itens do que o teto sem que isso signifique fim do histórico.

Anexos: imagem, áudio e vídeo

Quando a mensagem tem mídia, ela vem em attachments. O campo data_url é uma URL assinada: baixa sem o header api_access_token. É o caminho para o agente enviar a imagem a um modelo de visão, o áudio a um modelo de transcrição ou o vídeo a um extrator de quadros.

json
{
  "id": 88124,
  "message_type": 0,
  "content": "",
  "content_type": "text",
  "private": false,
  "sender": { "id": 91, "name": "Ana Souza", "type": "contact" },
  "attachments": [
    {
      "id": 5512,
      "message_id": 88124,
      "file_type": "image",
      "account_id": 1,
      "extension": "jpg",
      "content_type": "image/jpeg",
      "data_url": "https://chat.seudominio.com/rails/active_storage/blobs/redirect/...",
      "thumb_url": "https://chat.seudominio.com/rails/active_storage/representations/...",
      "file_size": 184322,
      "width": 1280,
      "height": 960
    }
  ]
}

Campos de attachments[]

NomeTipoObrigatorioDescricao
file_typestringSimimage, áudio, vídeo, file, location, fallback, share, story_mention, contact, ig_reel, ig_post, ig_story, embed.
data_urlstringNaoURL assinada do binário. Baixa sem token. Para áudio, aponta para reprodução inline.
thumb_urlstringNaoMiniatura. Só existe para imagem; vazio nos demais tipos.
transcribed_textstringNaoPresente em anexos de áudio. String vazia quando não há transcrição gravada.
coordinates_lat / coordinates_longnumberNaoPresentes quando file_type é location.

Baixe no momento da leitura

Trate data_url como efêmera: ela redireciona para um destino de armazenamento com validade curta. Baixe o binário enquanto processa a mensagem em vez de guardar a URL para usar depois. Um anexo cuja mensagem foi apagada deixa de existir — a URL passa a falhar.

Responder na conversa

POST/api/v1/accounts/{account_id}/conversations/{display_id}/messages

Cria uma mensagem. Sem message_type, a mensagem sai como outgoing (visível ao cliente).

Body

NomeTipoObrigatorioDescricao
contentstringSimTexto da mensagem.
message_typestringNaooutgoing (padrão) ou incoming.
privatebooleanNaotrue grava uma nota interna: o cliente não recebe nada. Padrão false.
content_typestringNaotext (padrão) e demais tipos suportados pelo canal.
template_paramsobjectNaoTemplate aprovado pela Meta, para inbox WhatsApp Cloud fora da janela de 24h.
echo_idstringNaoIdentificador do cliente, devolvido no payload para correlação.
bash
curl -s -X POST "https://chat.seudominio.com/api/v1/accounts/1/conversations/318/messages" \
  -H "api_access_token: SEU_TOKEN_DE_BOT" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Consigo encaixar voce amanha as 14h. Confirma?",
    "message_type": "outgoing",
    "private": false
  }'
200A mensagem criada. sender.type é agent_bot — a autoria fica registrada no bot, não numa pessoa.
json
{
  "id": 88130,
  "content": "Consigo encaixar você amanha as 14h. Confirma?",
  "message_type": 1,
  "content_type": "text",
  "private": false,
  "conversation_id": 318,
  "inbox_id": 42,
  "created_at": 1771596300,
  "sender": { "id": 7, "name": "Agente Hermes", "type": "agent_bot" }
}
422Falha de validação ou de despacho no canal.
json
{ "error": "<mensagem>" }

Nota interna é o caminho para raciocínio visível à equipe

Enviar com private: true grava uma nota que só a equipe lê. É o lugar correto para o agente registrar diagnóstico, dúvida ou justificativa de uma ação sem expor isso ao cliente. Uma nota interna nunca é entregue pelo canal.

Quando um agente marca o bot numa nota

Em uma nota interna, o agente pode digitar @ e escolher o bot na aba Botspara pedir algo sobre aquela conversa — "refaça o orçamento", "me passa um resumo". O bot recebe, na sua outgoing_url, um evento próprio, agent_bot_mentioned:

  • só o bot marcado recebe, e só se a nota foi escrita por uma pessoa;
  • só pode ser marcado o bot que atende a caixa de entrada da conversa (o mesmo vínculo que libera a escrita nela) — os outros aparecem na lista como "Não atende esta caixa de entrada";
  • notas internas continuam fora do message_created: sem a menção, o bot não recebe a nota.
json
{
  "event": "agent_bot_mentioned",
  "mentioned_agent_bot": { "id": 7, "name": "Agente Hermes" },
  "instruction": "refaça o orçamento com 10% de desconto",
  "id": 88131,
  "content": "[@Agente Hermes](mention://agent_bot/7/Agente%20Hermes) refaça o orçamento com 10% de desconto",
  "private": true,
  "message_type": "outgoing",
  "sender": { "id": 3, "name": "Ana", "type": "user" },
  "conversation": { "id": 318, "inbox_id": 42 },
  "inbox": { "id": 42, "name": "WhatsApp Vendas" },
  "account": { "id": 1, "name": "Minha Empresa" }
}

instruction é a nota em texto puro, sem a menção ao bot; outras menções (agentes, times) ficam como @Nome. conversation, inbox e sender têm o mesmo formato do message_created. Para responder, use Responder na conversa com o token do bot.

Nota por padrão; cliente só com pedido explícito

Quem pediu é a equipe, não o cliente. Responda com private: true e só envie ao cliente (private: false) quando a nota pedir isso explicitamente ("envie ao cliente…"). O NooviChat aceita os dois — a decisão é do bot.

Transferir para um humano

POST/api/v1/accounts/{account_id}/conversations/{display_id}/assignments

Atribui a conversa a um agente humano ou a uma equipe. Exige conversation_manage.

Body

NomeTipoObrigatorioDescricao
assignee_idintegerNaoId do usuário que recebe a conversa.
team_idintegerNaoId da equipe, quando a atribuição for por time.
bash
curl -s -X POST "https://chat.seudominio.com/api/v1/accounts/1/conversations/318/assignments" \
  -H "api_access_token: SEU_TOKEN_DE_BOT" \
  -H "Content-Type: application/json" \
  -d '{"assignee_id": 4}'

O bot não lista agentes nem equipes

As rotas /agents e /teams estão fora da superfície de bot e respondem 401. Um agente que precisa transferir tem que receber os ids de destino na configuração — não há como descobri-los com o token do bot.

CRM / Pipeline

MétodoRotaPermissão exigida
GET/api/v1/accounts/{account_id}/pipelinespipeline_manage | pipeline_view
GET/api/v1/accounts/{account_id}/pipelines/{id}pipeline_manage | pipeline_view
GET/api/v1/accounts/{account_id}/pipelines/{id}/stagespipeline_manage | pipeline_view
POST/api/v1/accounts/{account_id}/pipelinespipeline_manage
PATCH/api/v1/accounts/{account_id}/pipelines/{id}pipeline_manage
GET/api/v1/accounts/{account_id}/pipeline_cardspipeline_manage | pipeline_view
GET/api/v1/accounts/{account_id}/pipeline_cards/{id}pipeline_manage | pipeline_view
POST/api/v1/accounts/{account_id}/pipeline_cardspipeline_manage
PATCH/api/v1/accounts/{account_id}/pipeline_cards/{id}pipeline_manage
POST/api/v1/accounts/{account_id}/pipeline_cards/{id}/move_to_stagepipeline_manage
POST/api/v1/accounts/{account_id}/pipeline_cards/{id}/recalculate_scorepipeline_manage
PATCH/api/v1/accounts/{account_id}/pipeline_cards/{id}/update_qualification_checklistpipeline_manage
POST/api/v1/accounts/{account_id}/pipeline_cards/reorderpipeline_manage
POST/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/deal_status/mark_wonpipeline_manage
POST/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/deal_status/mark_lostpipeline_manage
POST/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/deal_status/reopenpipeline_manage
GET/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/opportunitiespipeline_manage
POST/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/opportunitiespipeline_manage
POST/api/v1/accounts/{account_id}/pipeline/opportunities/{id}/voidpipeline_manage
POST/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/conversationspipeline_manage
POST/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/contactspipeline_manage
PATCH/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/assignpipeline_manage
GET/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/attachmentspipeline_manage
POST/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/attachmentspipeline_manage
GET/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/timelinepipeline_manage | pipeline_view
GET/api/v1/accounts/{account_id}/pipeline/activitiespipeline_manage | pipeline_view
POST/api/v1/accounts/{account_id}/pipeline/activitiespipeline_manage
GET/api/v1/accounts/{account_id}/pipeline/productspipeline_manage | pipeline_view
GET/api/v1/accounts/{account_id}/pipeline/products/performancepipeline_manage | pipeline_view
GET/api/v1/accounts/{account_id}/pipeline/products/{id}pipeline_manage | pipeline_view

A permissão concede; a lista por funil apenas restringe

Quem autoriza o bot no Pipeline é a permissão concedida na tela do robô: pipeline_view lê funis e cards, pipeline_manage move e fecha negócio. Sem uma das duas, nenhuma rota de card responde — a permissão padrão de um bot novo é lista vazia, então atualizar a versão do NooviChat não concede nada a ninguém.

A configuração do funil tem uma lista opcional de bots. Quando ela está vazia, o funil é acessível a qualquer bot que tenha a permissão — mesma semântica da lista de agentes humanos. Quando está preenchida, ela restringe: só os bots listados alcançam aquele funil. Use-a quando quiser limitar um bot a um funil específico; não é necessária para o caso comum.

Mudou na 4.17.0.2. Até a versão anterior essa lista era um requisito: um bot com pipeline_manage num funil que não o listasse recebia lista vazia e 404, mesmo com a permissão marcada na tela. Se a sua integração depende de restringir por funil, confira que a lista está preenchida — antes ela era obrigatória e agora é opcional.

Listar funis e ler cards usam o mesmo critério

GET /pipelines e GET /pipeline_cards passam pelo mesmo recorte: permissão do bot, mais a lista por funil quando ela estiver preenchida. Um funil que aparece na listagem devolve os cards dele.

Mudou na 4.17.0.2. Antes os dois usavam critérios diferentes e a listagem de funis era mais permissiva que a leitura de cards — um funil aparecia na lista e não devolvia card algum. Se a sua integração deduzia acesso a partir da listagem, aquele descompasso não existe mais.

Achar o card da conversa

GET/api/v1/accounts/{account_id}/pipeline_cards?conversation_display_id={display_id}

Lista os cards vinculados a uma conversa. É o caminho para o agente ligar o que está lendo ao negócio em andamento.

Query

NomeTipoObrigatorioDescricao
conversation_display_id(query)integerNaodisplay_id da conversa. O mesmo id usado nas rotas de conversa.
include_linked_conversations(query)booleanNaotrue também traz cards em que essa conversa é um vínculo secundário, não o principal.
pipeline_id(query)integerNaoRestringe a um funil.
contact_id(query)integerNaoRestringe a um contato.
limit(query)integerNaoPadrão 50, máximo 500. Valores acima são reduzidos ao teto.(default: 50)
bash
curl -s "https://chat.seudominio.com/api/v1/accounts/1/pipeline_cards?conversation_display_id=318&include_linked_conversations=true" \
  -H "api_access_token: SEU_TOKEN_DE_BOT"
200Lista vazia significa: não há card, ou o bot não tem permissão de Pipeline, ou os cards estão em funis cuja lista de bots não inclui este.
json
{
  "payload": [
    {
      "id": 4412,
      "title": "Ana Souza - Consulta",
      "pipeline_id": 3,
      "pipeline_stage": "negociação",
      "conversation_display_id": 318,
      "contact_id": 91,
      "value": "980.0",
      "priority": "high",
      "won_at": null,
      "lost_at": null
    }
  ]
}

Mover o card de etapa

POST/api/v1/accounts/{account_id}/pipeline_cards/{id}/move_to_stage

Move o card. Etapas de ganho e de perda têm tratamento próprio.

Body

NomeTipoObrigatorioDescricao
pipeline_stagestringSimChave da etapa de destino, conforme GET /pipelines/{id}/stages.
expected_versionintegerSimOBRIGATÓRIO para agent bot desde a v4.17.0.6. O stage_version que você leu do card. O servidor compara e move na mesma transação: se alguém moveu o card entre a sua leitura e a sua escrita, responde 409 e NÃO move, em vez de sobrescrever. Requisições autenticadas como usuário podem omitir.
won_valuestringNaoValor fechado, quando a etapa de destino for de ganho.
won_notestringNaoObservação do fechamento.
bash
# 1. leia o card para saber a versão da etapa
curl -s "https://chat.seudominio.com/api/v1/accounts/1/pipeline_cards/4412" \
  -H "api_access_token: SEU_TOKEN_DE_BOT" | jq .stage_version
# -> 7

# 2. mova enviando a versão que você leu
curl -s -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipeline_cards/4412/move_to_stage" \
  -H "api_access_token: SEU_TOKEN_DE_BOT" \
  -H "Content-Type: application/json" \
  -d '{"pipeline_stage": "negociacao", "expected_version": 7}'
200O card atualizado.
403O bot não tem autoridade para o desfecho pedido. Fechar como ganho ou como perdido exige pipeline_manage; só pipeline_view move entre etapas comuns, mas não fecha negócio.
json
{ "error": "<mensagem da policy>" }
409Alguém moveu o card entre a sua leitura e a sua escrita. O card NÃO foi movido. Aqui current_version e current_stage VÊM na resposta, ao contrário do 422: eles são o diagnóstico da corrida que acabou de acontecer. Use-os para entender o que mudou, mas tome a decisão de novo — o destino que você escolheu valia para a etapa antiga. Não reenvie a mesma versão.
json
{
  "error": "Pipeline card stage changed since the version you sent",
  "reason": "stage_version_conflict",
  "expected_version": 7,
  "current_version": 8,
  "current_stage": "proposta"
}
422Agent bot que não enviou expected_version. A resposta NÃO traz a versão atual, de propósito: o pedido foi recusado antes de comparar qualquer coisa, então não há aqui uma versão sobre a qual você tenha decidido. Faça um GET novo, decida de novo com o que leu e mande essa versão. Repetir com um número tirado do corpo de erro é 'última escrita vence' com passos a mais — e passa a responder 200, então nada denuncia que a decisão foi tomada sobre um estado que já não existe.
json
{
  "error": "expected_version is required for agent bots on this endpoint",
  "reason": "expected_version_required",
  "requires_fresh_read": true
}
422Etapa ausente ou inexistente no funil.
json
{
  "error": "Invalid stage 'negociando'",
  "available_stages": ["lead", "qualificado", "negociação", "Ganho", "Perdido"]
}
409O card já estava fechado como ganho.
json
{ "error": "<mensagem>" }

Ganho e perda têm portas diferentes para bot

Ganho e perda passaram a ter o mesmo tratamento: ambos são autorizados pela policy e exigem pipeline_manage, tanto por move_to_stage quanto pelas rotas deal_status/mark_won e deal_status/mark_lost. Até a versão 4.17.0.1, mover para uma etapa de perda por move_to_stage era recusado com 403 por uma regra fixa no código; essa recusa não existe mais. Se a sua política é que a IA nunca encerre um negócio — ganho ou perdido — conceda apenas pipeline_view, que move o card entre etapas comuns sem poder fechá-lo.

Fechar a venda: produto, quantidade e valor

Um bot com pipeline_manage não fecha apenas o card — ele registra a venda no razão financeiro, escolhendo produto do catálogo e definindo os valores praticados. O catálogo vem de GET /pipeline/products, que responde para pipeline_view e pipeline_manage. Depois do fechamento, o razão do card é lido em GET /pipeline/cards/{card_id}/opportunities.

O corpo aceito é o mesmo documentado em API · Pipeline na seção de mark_won — pipeline_product_id, quantity, unit_value, ou o array items para vender mais de um produto na mesma negociação. Não repetimos a tabela aqui de propósito: um contrato descrito em dois lugares vira dois contratos.

Três recusas que aparecem só quando você tenta

  • won_value é obrigatório quando você envia quantity e unit_value. Não é redundância: é conferência entre o total que o agente calculou e o que o servidor calcula. Divergiu, a venda é recusada com 422 em vez de gravar um número que ninguém decidiu.
  • O funil precisa ter Ganho/Perdido habilitado. Sem isso a resposta é 422 com "Etapas Ganho/Perdido estão desabilitadas para este pipeline" — configuração do funil, não permissão do bot.
  • Produto inexistente, inativo ou de outra conta é recusado por item, com o índice na mensagem (items[1].pipeline_product_id was not found).

A resposta diz o que FICOU gravado, não o que você pediu

O retorno de mark_won traz um bloco opportunity com pipeline_product_id, quantity, unit_value, total_value, currency e items. Compare com o que enviou. O motivo é concreto: durante uma atualização gradual, uma instância ainda na versão anterior aceita pipeline_product_id, ignora em silêncio e responde sucesso — a venda entraria sem vínculo de produto e o razão é append-only. Com o eco, o agente detecta em vez de descobrir no relatório.

bash
# 1. escolher o produto
curl -s "https://chat.seudominio.com/api/v1/accounts/1/pipeline/products" \
  -H "api_access_token: SEU_TOKEN_DE_BOT"

# 2. fechar com produto e valor praticado
curl -s -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipeline/cards/19800/deal_status/mark_won" \
  -H "api_access_token: SEU_TOKEN_DE_BOT" \
  -H "Content-Type: application/json" \
  -d '{"pipeline_product_id": 2, "quantity": 3, "unit_value": 2500.50, "won_value": 7501.50, "won_note": "fechado pelo agente"}'

# 3. conferir o razão do card
curl -s "https://chat.seudominio.com/api/v1/accounts/1/pipeline/cards/19800/opportunities" \
  -H "api_access_token: SEU_TOKEN_DE_BOT"

Registrar o que aconteceu

POST/api/v1/accounts/{account_id}/pipeline/activities

Cria uma atividade no card. O id do card vai no corpo, não no path.

Body

NomeTipoObrigatorioDescricao
pipeline_card_idintegerSimCard que recebe a atividade. Deve estar em funil que o bot alcança — ver a regra de permissão e lista por funil acima.
activity_typestringNaoTipo da atividade conforme o catálogo do funil.
titlestringNaoTítulo.
descriptionstringNaoDescrição livre.
201Atividade criada.
422Validação recusada.
json
{ "errors": ["<mensagem>"] }

Catálogo de produtos: leitura funciona, escrita não

GET /pipeline/products e GET /pipeline/products/performance respondem normalmente para um bot com pipeline_view ou pipeline_manage. Já POST e PATCH em /pipeline/products não funcionam para bot: a autorização de escrita do catálogo exige uma função personalizada persistida, que um bot não tem, e a resposta é 403 mesmo com pipeline_manage. Trate o catálogo como somente leitura para agentes de IA.

Agendamento

Toda a superfície de Agendamento exige appointment_manage — uma única permissão cobre ler agenda, consultar disponibilidade e criar. Nesta área a negação de policy responde 403 com {"error":"forbidden"}, e não 401.

MétodoRotaPermissão exigida
GET/api/v1/accounts/{account_id}/appointmentsappointment_manage
GET/api/v1/accounts/{account_id}/appointments/{id}appointment_manage
POST/api/v1/accounts/{account_id}/appointmentsappointment_manage
PATCH/api/v1/accounts/{account_id}/appointments/{id}appointment_manage
POST/api/v1/accounts/{account_id}/appointments/{id}/confirmappointment_manage
POST/api/v1/accounts/{account_id}/appointments/{id}/completeappointment_manage
POST/api/v1/accounts/{account_id}/appointments/{id}/no_showappointment_manage
GET/api/v1/accounts/{account_id}/appointments/availabilityappointment_manage
GET/api/v1/accounts/{account_id}/appointments/availability_rangeappointment_manage
GET/api/v1/accounts/{account_id}/appointments/available_professionalsappointment_manage
GET/api/v1/accounts/{account_id}/appointments/clientsappointment_manage
DELETE/api/v1/accounts/{account_id}/appointments/{id}appointment_cancel
GET/api/v1/accounts/{account_id}/professionalsappointment_manage
GET/api/v1/accounts/{account_id}/professionals/{id}appointment_manage
GET/api/v1/accounts/{account_id}/professionals/{id}/availabilityappointment_manage
GET/api/v1/accounts/{account_id}/servicesappointment_manage
GET/api/v1/accounts/{account_id}/services/{id}appointment_manage

Consultar horários livres

GET/api/v1/accounts/{account_id}/appointments/availability

Horários livres de um profissional em uma data.

Query

NomeTipoObrigatorioDescricao
professional_id(query)integerSimProfissional consultado.
date(query)stringSimData no formato YYYY-MM-DD.
service_id(query)integerNaoQuando informado, a duração do slot vem do serviço e ignora duration_minutes.
duration_minutes(query)integerNaoDuração desejada, usada quando não há service_id.
bash
curl -s "https://chat.seudominio.com/api/v1/accounts/1/appointments/availability?professional_id=12&service_id=5&date=2026-08-25" \
  -H "api_access_token: SEU_TOKEN_DE_BOT"
200Slots em ISO 8601, no fuso configurado na conta.
json
{
  "data": {
    "date": "2026-08-25",
    "professional_id": 12,
    "slots": ["2026-08-25T09:00:00-03:00", "2026-08-25T14:00:00-03:00"]
  }
}
404Profissional inexistente na conta.
json
{ "error": "<mensagem>" }

Grade de vários dias

GET /appointments/availability_range responde por um intervalo em vez de um dia, até o teto de 42 dias. Use essa rota em vez de repetir a consulta diária.

Criar o agendamento

POST/api/v1/accounts/{account_id}/appointments

Cria um agendamento. Campos fora da lista abaixo fazem a requisição falhar com 422 — não são ignorados.

Body (dentro da chave appointment)

NomeTipoObrigatorioDescricao
contact_idintegerSimContato atendido.
professional_idintegerSimProfissional.
service_idintegerSimServiço prestado.
scheduled_atstringSimInício, em ISO 8601 com fuso.
ends_atstringNaoFim. Se omitido, deriva da duração do serviço.
notesstringNaoObservações.
partner_idintegerNaoParceiro vinculado.
conversation_display_idintegerNaoLiga o agendamento à conversa que o originou.
pipeline_card_idintegerNaoLiga o agendamento ao card do funil.
custom_attributesobjectNaoAtributos livres.
bash
curl -s -X POST "https://chat.seudominio.com/api/v1/accounts/1/appointments" \
  -H "api_access_token: SEU_TOKEN_DE_BOT" \
  -H "Content-Type: application/json" \
  -d '{
    "appointment": {
      "contact_id": 91,
      "professional_id": 12,
      "service_id": 5,
      "scheduled_at": "2026-08-25T14:00:00-03:00",
      "conversation_display_id": 318,
      "notes": "Agendado pelo agente de IA a partir da conversa."
    }
  }'
201Agendamento criado.

Atualizar só aceita quatro campos

PATCH /appointments/{id} aceita apenas scheduled_at, notes, partner_id e custom_attributes. Enviar ends_at, trocar de profissional ou de serviço pelo update responde 422. Remarcar é mudar scheduled_at; trocar de serviço exige cancelar e criar de novo — e cancelar está fora da superfície de bot.

Ciclo de vida disponível ao bot

confirm, complete e no_show estão na superfície de bot. cancel e apagar não estão: respondem 401. Um agente que precise desmarcar deve escalar para um humano.

Follow-up

Há dois caminhos, com contratos diferentes. O follow-up individual é uma mensagem programada com texto livre naquela conversa. A regra de etapa é uma cadência do funil: quando um card entra em determinada etapa, o follow-up nasce sozinho.

Follow-up individual

POST/api/v1/accounts/{account_id}/conversations/{display_id}/follow-ups

Agenda uma mensagem para o futuro nesta conversa. Exige follow_up_manage.

Body (dentro da chave follow_up)

NomeTipoObrigatorioDescricao
contentstringSimTexto que será enviado.
scheduled_atstringSimQuando enviar, em ISO 8601 com fuso. Precisa estar no futuro (tolerância de 1 minuto).
titlestringNaoTítulo interno.
inbox_idintegerNaoInbox de envio. Quando informada, precisa ser a mesma inbox da conversa.
follow_up_template_idintegerNaoTemplate de follow-up da conta.
template_paramsobjectNaoTemplate aprovado pela Meta, para inbox WhatsApp Cloud.
bash
curl -s -X POST "https://chat.seudominio.com/api/v1/accounts/1/conversations/318/follow-ups" \
  -H "api_access_token: SEU_TOKEN_DE_BOT" \
  -H "Content-Type: application/json" \
  -d '{
    "follow_up": {
      "content": "Oi Ana! Passando para confirmar seu horario de amanha as 14h.",
      "scheduled_at": "2026-08-24T18:00:00-03:00",
      "title": "Confirmacao D-1"
    }
  }'
201Criado. O texto volta na chave message, não em content, e as datas são epoch em segundos.
json
{
  "id": 771,
  "message": "Oi Ana! Passando para confirmar seu horário de amanha as 14h.",
  "scheduled_at": 1771966800,
  "title": "Confirmação D-1",
  "inbox_id": 42,
  "conversation_id": 318,
  "created_at": 1771596400,
  "status": "pending"
}
422Data no passado, inbox divergente da conversa ou conteúdo ausente.
json
{ "error": "Scheduled at must be in the future" }

O bot só enxerga os próprios follow-ups

GET .../follow-ups e GET .../follow-ups/count devolvem apenas os follow-ups criados por este bot. Follow-ups agendados por pessoas da equipe ou por outro bot na mesma conversa não aparecem, e count reflete o mesmo recorte. Um agente que verifica já existe follow-up agendado? por essa rota está perguntando eu já agendei? — não alguém já agendou?.

Regra de follow-up por etapa

POST/api/v1/accounts/{account_id}/pipelines/{pipeline_id}/follow-up-rules

Cria uma cadência disparada por mudança de etapa. Exige follow_up_manage OU pipeline_manage.

Body (dentro da chave pipeline_follow_up_rule)

NomeTipoObrigatorioDescricao
to_stagestringSimEtapa de destino que dispara a regra.
from_stagestringNaoEtapa de origem. Ausente ou null significa qualquer origem.
content_modestringNaotemplate (padrão) ou ai. Outro valor é recusado.(default: template)
follow_up_template_idintegerNaoObrigatório na criação quando content_mode é template.
ai_instructionstringNaoObrigatório quando content_mode é ai. É a instrução de redação.
delay_minutesintegerNaoEspera antes do envio. 0 envia assim que possível.(default: 0)
send_windowobjectNaoJanela de envio: { enabled, start, end, days }. Adia o disparo para dentro da janela.
sender_idintegerNaoUsuário remetente. Exclusivo com sender_agent_bot_id.
sender_agent_bot_idintegerNaoBot remetente. O bot precisa servir a conta. Exclusivo com sender_id.
enabledbooleanNaoLiga ou desliga a regra.(default: true)
bash
curl -s -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipelines/3/follow-up-rules" \
  -H "api_access_token: SEU_TOKEN_DE_BOT" \
  -H "Content-Type: application/json" \
  -d '{
    "pipeline_follow_up_rule": {
      "to_stage": "negociacao",
      "from_stage": null,
      "content_mode": "ai",
      "ai_instruction": "Retome a conversa citando o servico discutido e ofereca dois horarios.",
      "delay_minutes": 1440,
      "sender_agent_bot_id": 7,
      "send_window": { "enabled": true, "start": "08:00", "end": "18:00", "days": [1,2,3,4,5] }
    }
  }'
201Regra criada.
json
{
  "id": 55,
  "pipeline_id": 3,
  "follow_up_template_id": null,
  "sender_id": null,
  "sender_agent_bot_id": 7,
  "sender_agent_bot_name": "Agente Hermes",
  "content_mode": "ai",
  "ai_instruction": "Retome a conversa citando o serviço discutido e ofereca dois horários.",
  "from_stage": null,
  "to_stage": "negociação",
  "delay_minutes": 1440,
  "send_window": { "enabled": true, "start": "08:00", "end": "18:00", "days": [1,2,3,4,5] },
  "enabled": true,
  "conditions": {},
  "created_at": 1771596500,
  "updated_at": 1771596500
}
422Chave no plural e valor em array — formato diferente do follow-up individual.
json
{ "errors": ["Ai instruction nao pode ficar em branco"] }
403O funil restringe visibilidade e não inclui este ator.
json
{ "error": "<mensagem no idioma da conta>" }

Os dois modos de conteúdo

template renderiza um template de follow-up da conta no momento em que a regra dispara. ai não carrega template: a redação acontece na hora do envio, a partir de ai_instruction e do estado da conversa naquele momento — é por isso que o texto não fica congelado no instante em que o card mudou de etapa. Não existe um terceiro modo.

Regra só dispara com conversa vinculada

A regra é ignorada quando o card não tem conversa vinculada, quando a conversa apontada não existe naquela conta, ou quando ela não tem inbox. Nesses casos não há erro em resposta alguma: a regra foi criada com 201, o card muda de etapa com 200, e o follow-up simplesmente não nasce — o motivo fica apenas no log do servidor. Antes de confiar numa regra, confirme que os cards do funil têm conversation_display_id preenchido. Para vincular, use POST /pipeline/cards/{card_id}/conversations ou grave conversation_display_id no card.

Autoria de bot no follow-up gerado

Com sender_agent_bot_id apontando para o próprio bot, os follow-ups nascidos daquela regra ficam com autoria de bot — e passam a aparecer nas listagens que o bot faz. Sem esse campo, a autoria cai na cadeia responsável do card, depois responsável da conversa, depois administrador, e o bot não verá esses follow-ups.

Não suportado

Itens abaixo não existem hoje. Não há parâmetro, header ou permissão que os habilite.

  • Authorization: Bearer nesta API. O único header aceito é api_access_token.
  • Token de bot válido para mais de uma conta.
  • Qualquer ação de apagar (destroy) em qualquer recurso.
  • Cancelar ou reenviar follow-up, cancelar agendamento, apagar card ou conversa.
  • Criar ou alterar produto do catálogo do Pipeline (responde 403 mesmo com pipeline_manage).
  • Mover card para etapa de perda por move_to_stage (responde 403).
  • Listar agentes, equipes, inboxes, webhooks, automações, relatórios ou central de ajuda com token de bot.
  • Autoprovisionamento: um bot não cria bots, não altera as próprias permissões e não vincula a si mesmo a uma inbox.
  • Parâmetro de tamanho de página no histórico de mensagens.
  • Distinguir, pela resposta, um 404 de conta errada de um 404 de recurso fora do recorte de visibilidade.