Spec: confirmação de agenda via WhatsApp (ClinicAll)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
49e9cb9bca
commit
01ca9d5e34
@@ -0,0 +1,200 @@
|
|||||||
|
# Design — Confirmação de Agenda via WhatsApp (ClinicAll)
|
||||||
|
|
||||||
|
**Data:** 2026-08-11
|
||||||
|
**Status:** Aprovado pelo usuário, aguardando plano de implementação.
|
||||||
|
|
||||||
|
## Objetivo
|
||||||
|
|
||||||
|
Mesmo padrão já implementado para o Klingo
|
||||||
|
(`docs/superpowers/specs/2026-08-11-confirmacao-agenda-klingo-design.md`),
|
||||||
|
agora para o **ClinicAll**: permitir que o paciente confirme presença
|
||||||
|
numa consulta agendada via WhatsApp. Diferente do Klingo, o ClinicAll
|
||||||
|
não tem uma API de "Telefonia" separada — os endpoints de confirmação
|
||||||
|
fazem parte da mesma `TCalendarController` já usada no MVP de
|
||||||
|
agendamento (`docs/superpowers/specs/2026-08-11-mcp-server-nativo-n8n-clinicall-design.md`),
|
||||||
|
verificados diretamente no manual oficial (`docs/API Clinicall.pdf`,
|
||||||
|
versão 1.1.16).
|
||||||
|
|
||||||
|
## Relação com o trabalho anterior
|
||||||
|
|
||||||
|
- Reaproveita o **MCP Server standalone de Mensageria (Z-PRO)**
|
||||||
|
(`docs/n8n-agendamento-runbook.md`, seção 17) para o disparo do
|
||||||
|
lembrete — mesma decisão do Klingo, assumindo que o cliente ClinicAll
|
||||||
|
de exemplo também usaria Z-PRO/GChat como canal de WhatsApp.
|
||||||
|
- É um MCP **novo e separado** do MCP de agendamento do ClinicAll
|
||||||
|
(seção 14.3 do runbook) — mesmo `TCalendarController`/`TDomainController`,
|
||||||
|
mas endpoints diferentes (confirmação de presença, não criação de
|
||||||
|
agendamento), mesmo espírito de separação de responsabilidades já
|
||||||
|
usado no Klingo (MCP de Telefonia separado do MCP de Agendamento).
|
||||||
|
- Mesma pendência de autenticação já documentada no MVP de agendamento
|
||||||
|
do ClinicAll: o manual não documenta nenhum header de autenticação
|
||||||
|
por requisição para `TCalendarController`/`TDomainController`.
|
||||||
|
**Decisão idêntica à já tomada**: `authentication: none` em todos os
|
||||||
|
nodes HTTP, nada inventado.
|
||||||
|
|
||||||
|
## Escopo: 5 tools do MCP Server "ClinicAll - Confirmação de Agenda"
|
||||||
|
|
||||||
|
| Tool | Endpoint | Uso |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `listar_agendamentos_do_dia` | `GET /TCalendarController/agendamento` (param: `data`) | Base do lembrete proativo — todas as marcações de uma data (livres e agendadas) |
|
||||||
|
| `buscar_agendamento_por_cpf` | `GET/POST /TCalendarController/agendamentoSemPaciente` (params: `cpf`, `dataNascimento`) | O agente usa isso pra achar a consulta pendente do paciente — **precisa de CPF e data de nascimento**, diferente do Klingo (que só pedia CPF) |
|
||||||
|
| `confirmar_presenca` | `POST /TCalendarController/AgendamentoFlags` | Ação central — atualiza `estadoAgenda`/`situacaoAgenda` da marcação |
|
||||||
|
| `consultar_estados_agenda` | `GET /TDomainController/estadoAgenda` | Tabela de domínio — valores exatos aceitos em `estadoAgenda` (ex.: `"CONFIRMADO PELA TELEFONISTA"`, `"NAO CONFIRMADO"`, `"FALTOU"`) |
|
||||||
|
| `consultar_situacoes_agenda` | `GET /TDomainController/situacaoAgenda` | Tabela de domínio — valores exatos aceitos em `situacaoAgenda` (ex.: `"Agendado"`, `"Presente"`, `"Atrasado"`) |
|
||||||
|
|
||||||
|
**Diferença estrutural real em relação ao Klingo**: o Klingo aceita um
|
||||||
|
código curto de 1 letra (`C`/`S`/`N`) pra confirmar. O ClinicAll exige
|
||||||
|
uma **string exata e longa** em dois campos separados
|
||||||
|
(`estadoAgenda`/`situacaoAgenda`), cujos valores válidos só existem
|
||||||
|
documentados nas tabelas de domínio — por isso `consultar_estados_agenda`
|
||||||
|
e `consultar_situacoes_agenda` entram como tools de apoio (não
|
||||||
|
opcionais): sem elas, o agente teria que adivinhar a string exata, o
|
||||||
|
que violaria a disciplina de não inventar valores. Não há tool
|
||||||
|
equivalente a NPS ou consulta de bloqueios — o manual do ClinicAll não
|
||||||
|
documenta nada parecido para este módulo; ficam fora, sem inventar.
|
||||||
|
|
||||||
|
## Arquitetura
|
||||||
|
|
||||||
|
Três peças, mesmo padrão do Klingo:
|
||||||
|
|
||||||
|
```
|
||||||
|
1) MCP Server "ClinicAll - Confirmação de Agenda" (servidor + 5 sub-workflows)
|
||||||
|
[MCP Server Trigger] ──Tools──┬── listar_agendamentos_do_dia ──▶ GET /TCalendarController/agendamento
|
||||||
|
├── buscar_agendamento_por_cpf ──▶ GET/POST /TCalendarController/agendamentoSemPaciente
|
||||||
|
├── confirmar_presenca ──▶ POST /TCalendarController/AgendamentoFlags
|
||||||
|
├── consultar_estados_agenda ──▶ GET /TDomainController/estadoAgenda
|
||||||
|
└── consultar_situacoes_agenda ──▶ GET /TDomainController/situacaoAgenda
|
||||||
|
|
||||||
|
2) Workflow proativo "Lembrete de Confirmação - ClinicAll (exemplo)"
|
||||||
|
[Cron 8h diário] → [ClinicAll: listar_agendamentos_do_dia (amanhã)]
|
||||||
|
→ [Loop por agendamento] → [Mensageria ZPRO: enviar_texto]
|
||||||
|
|
||||||
|
3) Workflow de exemplo "Confirmação de Agenda - GChat - ClinicAll+Z-PRO (exemplo)"
|
||||||
|
[Webhook Z-PRO/GChat] → [AI Agent] → resposta ao paciente
|
||||||
|
tools: MCP ClinicAll - Confirmação de Agenda (buscar_agendamento_por_cpf, confirmar_presenca,
|
||||||
|
consultar_estados_agenda, consultar_situacoes_agenda)
|
||||||
|
memória: Postgres Chat Memory (mesmo padrão dos outros exemplos)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sem cliente ClinicAll real disponível: mesma disciplina do Klingo —
|
||||||
|
estrutura completa, validada (`n8n_validate_workflow`, 0 erros
|
||||||
|
esperados), sem teste de ponta a ponta.
|
||||||
|
|
||||||
|
## Componentes
|
||||||
|
|
||||||
|
### MCP Server "ClinicAll - Confirmação de Agenda"
|
||||||
|
|
||||||
|
Mesmo esqueleto de sub-workflow dos outros MCP Servers deste projeto:
|
||||||
|
`Execute Workflow Trigger` → `Config ClinicAll` (`Set` com `baseUrl`
|
||||||
|
placeholder) → montagem de query/body → `HTTP Request`
|
||||||
|
(`authentication: none`, `neverError: true` + `fullResponse: true`) →
|
||||||
|
classificação de resultado (mesma taxonomia genérica dos outros
|
||||||
|
providers: `AUTHENTICATION_ERROR` 401/403, `NOT_FOUND` 404,
|
||||||
|
`VALIDATION_ERROR` 400/422, `CLINICALL_API_ERROR` demais — o manual não
|
||||||
|
documenta código de erro específico pra estes endpoints).
|
||||||
|
|
||||||
|
- **`listar_agendamentos_do_dia`**: parâmetro `data` (obrigatório,
|
||||||
|
conforme documentado — "Mandatório data da agenda"). URL:
|
||||||
|
`{{baseUrl}}/datasnap/rest/TCalendarController/agendamento/<data>`
|
||||||
|
(mesmo endpoint base já usado no MVP de agendamento com `PUT` para
|
||||||
|
conciliar; aqui usado com `GET` para listar).
|
||||||
|
- **`buscar_agendamento_por_cpf`**: parâmetros `cpf` e `dataNascimento`
|
||||||
|
(ambos obrigatórios, conforme documentado). Endpoint:
|
||||||
|
`{{baseUrl}}/datasnap/rest/TCalendarController/agendamentoSemPaciente`.
|
||||||
|
**Ambiguidade real do manual**: o endpoint documenta métodos `GET,
|
||||||
|
POST` sem esclarecer se os parâmetros vão como query string (GET) ou
|
||||||
|
corpo (POST) — o exemplo do manual não mostra a requisição, só a
|
||||||
|
resposta. Decisão desta etapa: implementar como `GET` com os dois
|
||||||
|
parâmetros como query string (`?cpf=...&dataNascimento=...`), por
|
||||||
|
ser o padrão mais comum nos demais endpoints GET documentados deste
|
||||||
|
mesmo manual (todos os outros GETs do ClinicAll usam query string,
|
||||||
|
nunca body) — mas isso é uma suposição não verificada contra a API
|
||||||
|
real, registrada explicitamente como tal.
|
||||||
|
- **`confirmar_presenca`**: parâmetros `idAgenda` (obrigatório),
|
||||||
|
`situacaoAgenda` (obrigatório, string exata — ex.: `"Agendado"`),
|
||||||
|
`estadoAgenda` (obrigatório, string exata — ex.: `"CONFIRMADO PELA
|
||||||
|
TELEFONISTA"`), `confirmadoPor` (opcional — nome de quem confirmou,
|
||||||
|
útil quando não é o próprio paciente respondendo), `usuarioConfirmou`,
|
||||||
|
`dataConfirmacao`, `horarioFlag`, `usuarioFlag` (estes quatro
|
||||||
|
opcionais, preenchidos com dados fixos identificando a automação —
|
||||||
|
ex.: `usuarioConfirmou: "bot-whatsapp"` — já que não há usuário
|
||||||
|
humano operando). Endpoint:
|
||||||
|
`{{baseUrl}}/datasnap/rest/TCalendarController/AgendamentoFlags`.
|
||||||
|
- **`consultar_estados_agenda`**: sem parâmetro obrigatório (aceita
|
||||||
|
`nome` opcional de busca, conforme documentado). Endpoint:
|
||||||
|
`{{baseUrl}}/datasnap/rest/TDomainController/estadoAgenda`.
|
||||||
|
- **`consultar_situacoes_agenda`**: mesmo padrão, endpoint
|
||||||
|
`{{baseUrl}}/datasnap/rest/TDomainController/situacaoAgenda`.
|
||||||
|
|
||||||
|
### Workflow proativo "Lembrete de Confirmação - ClinicAll (exemplo)"
|
||||||
|
|
||||||
|
```
|
||||||
|
[Cron Trigger, 8h diário] → [ClinicAll: listar_agendamentos_do_dia]
|
||||||
|
(data = amanhã, calculada via expressão)
|
||||||
|
→ [Loop] → [Mensageria ZPRO: enviar_texto]
|
||||||
|
(number = telefoneCelular do paciente dentro do objeto Paciente
|
||||||
|
aninhado, body = mensagem com nomePaciente + dataAgenda/horaAgenda)
|
||||||
|
```
|
||||||
|
|
||||||
|
Mesmo padrão do Klingo: chamada direta via `Execute Workflow` (sem IA,
|
||||||
|
fluxo determinístico). Nota: no ClinicAll, o telefone do paciente vem
|
||||||
|
aninhado dentro do objeto `Paciente` de cada item da lista
|
||||||
|
(`item.Paciente.telefoneCelular`), diferente do Klingo onde `celular`
|
||||||
|
era um campo direto do item — refletir isso na extração de dados.
|
||||||
|
Itens com `situacaoAgenda: "Livre"` (sem paciente vinculado) devem ser
|
||||||
|
filtrados antes do envio — só mandar lembrete pra marcações com
|
||||||
|
`idPaciente` preenchido. Texto fixo nesta rodada (sem Google Docs — é
|
||||||
|
exemplo/esqueleto, sem gestor real). Fica **inativo** de propósito —
|
||||||
|
sem clínica real, ativar geraria só chamadas com erro.
|
||||||
|
|
||||||
|
### Workflow de exemplo "Confirmação de Agenda - GChat - ClinicAll+Z-PRO (exemplo)"
|
||||||
|
|
||||||
|
Réplica estrutural do padrão já usado no Klingo e no Z-PRO (webhook →
|
||||||
|
AI Agent → memória Postgres), sem cliente real associado.
|
||||||
|
|
||||||
|
- **Webhook**: mesmo contrato Z-PRO/GChat já documentado (seção 6 do
|
||||||
|
runbook).
|
||||||
|
- **AI Agent**: `systemMessage` fixo instruindo o agente a: entender se
|
||||||
|
a mensagem é uma resposta de confirmação/recusa; se for, pedir **CPF
|
||||||
|
e data de nascimento** do paciente (diferente do Klingo, que só
|
||||||
|
precisava do CPF) e usar `buscar_agendamento_por_cpf` para achar a
|
||||||
|
marcação pendente; consultar `consultar_estados_agenda` e
|
||||||
|
`consultar_situacoes_agenda` para obter as strings exatas válidas
|
||||||
|
antes de decidir o que enviar (nunca inventar um valor de estado);
|
||||||
|
chamar `confirmar_presenca` com `estadoAgenda: "CONFIRMADO PELA
|
||||||
|
TELEFONISTA"` (paciente confirmou) ou `estadoAgenda: "NAO
|
||||||
|
CONFIRMADO"`/`"FALTOU"` conforme o caso, mantendo `situacaoAgenda:
|
||||||
|
"Agendado"` (a marcação continua agendada, só o estado de confirmação
|
||||||
|
muda); responder confirmando o registro ao paciente.
|
||||||
|
- **Tools**: MCP ClinicAll - Confirmação de Agenda — `buscar_agendamento_por_cpf`,
|
||||||
|
`confirmar_presenca`, `consultar_estados_agenda`,
|
||||||
|
`consultar_situacoes_agenda` conectadas (todas as 4 exceto
|
||||||
|
`listar_agendamentos_do_dia`, que só serve ao workflow proativo).
|
||||||
|
- **Memória**: `Postgres Chat Memory`, mesmo padrão da seção 16/18 do
|
||||||
|
runbook, credencial placeholder (sem cliente real).
|
||||||
|
|
||||||
|
Fica **inativo** de propósito, mesma razão do workflow proativo.
|
||||||
|
|
||||||
|
## Fora de escopo (adiado)
|
||||||
|
|
||||||
|
- NPS / avaliação pós-consulta — não documentado no manual do
|
||||||
|
ClinicAll para este módulo.
|
||||||
|
- Consulta de bloqueios de agenda — não documentado no manual do
|
||||||
|
ClinicAll para este módulo (existe `flagEstadoAgenda: "B" —
|
||||||
|
BLOQUEADO` como estado de uma marcação individual, mas não um
|
||||||
|
endpoint de consulta de bloqueios como no Klingo).
|
||||||
|
- Resolver a ambiguidade de GET vs. body em `agendamentoSemPaciente`
|
||||||
|
contra a API real — fica pendente até haver clínica real pra testar.
|
||||||
|
- Configuração via Google Docs no workflow de exemplo — sem gestor
|
||||||
|
real associado.
|
||||||
|
- Teste de ponta a ponta contra a API real do ClinicAll — sem clínica
|
||||||
|
real disponível, mesma limitação já registrada no MVP de agendamento.
|
||||||
|
|
||||||
|
## Testes
|
||||||
|
|
||||||
|
Sem clínica ClinicAll real disponível: validação estrutural apenas
|
||||||
|
(`n8n_validate_workflow`, 0 erros esperados em todos os workflows).
|
||||||
|
Teste real de ponta a ponta fica pendente para quando houver uma
|
||||||
|
clínica ClinicAll real — nesse momento, resolver primeiro a
|
||||||
|
autenticação (mesma pendência do MVP de agendamento) e a ambiguidade
|
||||||
|
de `agendamentoSemPaciente` antes de qualquer teste de confirmação.
|
||||||
Reference in New Issue
Block a user