Spec: confirmação de agenda via WhatsApp (ClinicAll)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Gustavo
2026-08-11 16:02:37 -03:00
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.