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