Agendamento de consultas por canais externos — chatbot ChatWin no WhatsApp.
Cada rota desta API vira uma "API Externa" (uma ferramenta) no ChatWin. A IA decide sozinha quando chamar cada uma, a partir do texto da Descrição — por isso os textos prontos abaixo importam tanto quanto o código: eles vão direto para o raciocínio do modelo.
| Aba do ChatWin | O que preencher |
|---|---|
| Geral → Nome | o nome em snake_case indicado em cada ferramenta |
| Geral → Descrição | o texto do bloco "Descrição para a IA" — copie inteiro |
| Conexão → Método | POST em todas |
| Conexão → URL | a URL da ferramenta |
| Conexão → Headers customizados | Identificador = código do cliente |
| Autenticação | Bearer Token = chave de integração do cliente |
| Resposta → Exemplo | o JSON de exemplo de cada ferramenta |
| Parâmetros | a tabela de parâmetros de cada ferramenta |
unidade como
Query parameter fixo na aba Conexão. A IA nunca pergunta e nunca erra.unidade como
Parâmetro, e a IA pergunta qual unidade o paciente prefere.
Dois headers, os dois obrigatórios em toda chamada:
Authorization: Bearer <chave de integração do cliente>
Identificador: <código do cliente>
O Identificador é o que define em qual banco a consulta roda. Ele vem
da configuração da ferramenta, nunca da conversa — a IA não vê e não tem como errar de cliente.
Credencial inválida devolve 401. É o único caso, junto com 429 (limite excedido), em que a resposta não é 200.
Toda resposta é HTTP 200, inclusive quando não deu certo. O resultado real vai no
campo situacao. Isso é proposital: o ChatWin trata qualquer resposta não-2xx como "falha
da ferramenta" e a IA perde o conteúdo — deixando de saber a diferença entre "está lotado", "a agenda
não foi liberada" e "esse horário acabou de ser tomado".
Todas as respostas carregam estes campos:
| Campo | Para que serve |
|---|---|
ok | true quando a operação deu certo |
situacao | o código do resultado — é o que a IA lê para decidir o que falar |
msg | frase pronta em português para a IA repassar ao paciente |
hoje | data de hoje na clínica (dd/mm/aaaa) |
agora | hora atual na clínica (hh:mm) |
hoje e agora vão em toda resposta de propósito. O modelo erra data
relativa ("quinta que vem", "semana que vem") com frequência, e o relógio da conversa não é o da
clínica. Instrua a IA a sempre calcular datas a partir do hoje.
| situacao | Significa | O que a IA deve fazer |
|---|---|---|
OK | deu certo | seguir normalmente |
SEM_VAGA | está lotado no período | oferecer outro período |
AGENDA_NAO_ABERTA | a clínica ainda não liberou esse período | não dizer que está lotado — dizer que ainda não abriu e oferecer avisar depois |
NAO_ATENDE_NO_DIA | a unidade não atende nesses dias | oferecer outros dias |
OCUPADO | o horário foi tomado agora há pouco | pedir desculpa e oferecer as alternativas que vieram junto |
HORARIO_INEXISTENTE | esse horário nunca existiu na grade | consultar a disponibilidade de novo antes de falar |
NAO_ENCONTRADO | não achou a consulta | confirmar telefone e data com o paciente |
JA_FEITO | já estava nesse estado | tranquilizar — está tudo certo, não é erro |
AMBIGUO | mais de uma consulta casa com o pedido | ler as candidatas e perguntar qual antes de tentar de novo |
FORA_DO_PRAZO | a consulta já passou | orientar a ligar para a unidade |
DADOS_INVALIDOS | algum parâmetro veio errado | ler a msg e corrigir |
ERRO_TEMPORARIO | falha momentânea | tentar de novo em alguns segundos |
| Nome | Tipo | Obrig. | Descrição (para a IA) |
|---|---|---|---|
unidade | string | sim* | Código da unidade de atendimento |
de | string | não | Primeiro dia do período, no formato dd/mm/aaaa |
ate | string | não | Último dia do período, no formato dd/mm/aaaa |
turno | string | não | manha, tarde ou noite |
* Se o atendente cobre uma unidade só, deixe fora dos parâmetros e configure como query param fixo.
{
"ok": true,
"situacao": "OK",
"hoje": "18/08/2026",
"agora": "14:32",
"unidade": { "id": "1", "nome": "OTICA CENTRO" },
"dias": [
{ "data": "20/08/2026", "semana": "quinta-feira", "horas": ["09:00","09:30","14:00"] },
{ "data": "22/08/2026", "semana": "sábado", "horas": ["08:00"] }
],
"total": 12,
"truncado": true,
"msg": "Tenho 3 horários na quinta-feira (20/08/2026) e mais opções nos dias seguintes."
}
| Nome | Tipo | Obrig. | Descrição (para a IA) |
|---|---|---|---|
unidade | string | sim* | Código da unidade de atendimento |
turno | string | não | manha, tarde ou noite |
quantidade | número | não | Quantas opções devolver (padrão 3) |
{
"ok": true,
"situacao": "OK",
"hoje": "18/08/2026",
"agora": "14:32",
"unidade": { "id": "1", "nome": "OTICA CENTRO" },
"opcoes": [
{ "data": "20/08/2026", "semana": "quinta-feira", "hora": "09:00", "em_dias": 2 },
{ "data": "20/08/2026", "semana": "quinta-feira", "hora": "09:30", "em_dias": 2 }
],
"msg": "A vaga mais próxima é quinta-feira, dia 20/08/2026, às 09:00."
}
| Nome | Tipo | Obrig. | Descrição (para a IA) |
|---|---|---|---|
unidade | string | sim* | Código da unidade de atendimento |
data | string | sim | Data da consulta, no formato dd/mm/aaaa |
hora | string | sim | Horário da consulta, no formato hh:mm |
nome | string | sim | Nome completo de quem será atendido |
telefone | string | sim | Telefone do WhatsApp do paciente, com DDD |
{
"ok": true,
"situacao": "OK",
"hoje": "18/08/2026",
"agora": "14:32",
"referencia": "48211",
"data": "20/08/2026",
"semana": "quinta-feira",
"hora": "09:00",
"unidade": { "id": "1", "nome": "OTICA CENTRO" },
"alternativas": [],
"msg": "Consulta marcada para quinta-feira, 20/08/2026, às 09:00."
}
alternativas do mesmo dia — a IA oferece na mesma frase, sem precisar
de outra chamada.
| Nome | Tipo | Obrig. | Descrição (para a IA) |
|---|---|---|---|
telefone | string | sim | Telefone do WhatsApp do paciente, com DDD |
unidade | string | não | Código da unidade, para filtrar |
{
"ok": true,
"situacao": "OK",
"hoje": "18/08/2026",
"agora": "14:32",
"consultas": [
{ "referencia": "48211", "paciente": "MARIA SOUZA", "data": "20/08/2026",
"semana": "quinta-feira", "hora": "09:00", "situacao": "AGENDADO",
"unidade": { "id": "1", "nome": "OTICA CENTRO" }, "hoje_mesmo": false }
],
"total": 1,
"msg": "Encontrei 1 consulta."
}
| Nome | Tipo | Obrig. | Descrição (para a IA) |
|---|---|---|---|
telefone | string | sim | Telefone do WhatsApp do paciente, com DDD |
referencia | string | não | Código da consulta devolvido antes (preferido) |
data | string | não | Data da consulta, dd/mm/aaaa (use só sem a referência) |
hora | string | não | Horário da consulta, hh:mm (use junto com a data) |
motivo | string | não | Motivo do cancelamento, se o paciente disser |
{
"ok": false,
"situacao": "AMBIGUO",
"hoje": "18/08/2026",
"candidatas": [
{ "referencia": "48211", "paciente": "MARIA SOUZA", "hora": "09:00", "data": "20/08/2026" },
{ "referencia": "48219", "paciente": "ANA SOUZA", "hora": "09:30", "data": "20/08/2026" }
],
"msg": "Encontrei 2 consultas nesse dia. Qual delas?"
}
Os mesmos de cancelar_consulta, sem o motivo.
{
"ok": true,
"situacao": "OK",
"hoje": "18/08/2026",
"referencia": "48211",
"data": "20/08/2026",
"semana": "quinta-feira",
"hora": "09:00",
"paciente": "MARIA SOUZA",
"unidade": { "id": "1", "nome": "OTICA CENTRO" },
"msg": "Presença confirmada para quinta-feira, 20/08/2026, às 09:00."
}
| Nome | Tipo | Obrig. | Descrição (para a IA) |
|---|---|---|---|
telefone | string | sim | Telefone do WhatsApp do paciente, com DDD |
referencia | string | não | Código da consulta atual (preferido) |
data | string | não | Data da consulta ATUAL (use só sem a referência) |
hora | string | não | Horário da consulta ATUAL |
nova_data | string | sim | Nova data, no formato dd/mm/aaaa |
nova_hora | string | sim | Novo horário, no formato hh:mm |
{
"ok": false,
"situacao": "OCUPADO",
"hoje": "18/08/2026",
"mantida": { "data": "20/08/2026", "semana": "quinta-feira", "hora": "09:00", "em_dias": 2 },
"alternativas": [ { "data": "27/08/2026", "hora": "14:30", "em_dias": 9 } ],
"msg": "O horário novo não está mais livre. Sua consulta de 20/08/2026 às 09:00 continua marcada."
}
| Nome | Tipo | Obrig. | Descrição (para a IA) |
|---|---|---|---|
busca | string | não | Bairro, cidade ou nome da unidade que o paciente citou |
unidade | string | não | Código de uma unidade específica |
{
"ok": true,
"situacao": "OK",
"hoje": "18/08/2026",
"unidades": [
{ "id": "1", "nome": "OTICA CENTRO", "endereco": "RUA OLEGARIO MACIEL 100",
"bairro": "CENTRO", "cidade": "UBERLANDIA", "uf": "MG",
"cep": "38400000", "fone": "34 32320000" }
],
"total": 1
}
Dois limites independentes, ambos por minuto:
| Limite | Padrão | Aplicado a |
|---|---|---|
| Por endereço IP | 120/min | todas as chamadas, antes da autenticação |
| Consulta, por cliente | 60/min | consultar_horarios, proximo_horario_livre, minhas_consultas, listar_unidades |
| Escrita, por cliente | 20/min | agendar, cancelar, confirmar, remarcar |
Ao estourar, a resposta é 429 com o header Retry-After em segundos, e
o corpo no mesmo formato das demais (situacao: ERRO_TEMPORARIO). Os limites são
ajustáveis por cliente na configuração da API.
agendar_consulta é seguro repetir (devolve
JA_FEITO), mas cancelar e remarcar não são — repetir pode devolver
NAO_ENCONTRADO mesmo tendo funcionado na primeira vez.
A especificação navegável, com todos os campos e a possibilidade de testar cada rota informando a credencial, está em /swagger.