Sinalizar achado crítico e suspeita técnica — API
Permite a um usuário autorizado marcar ou desmarcar duas flags clínicas independentes de um exame: achado crítico (um resultado que exige atenção imediata) e suspeita técnica (uma dúvida sobre a qualidade técnica do exame). As duas flags têm o mesmo formato de rota, o mesmo modelo de resposta e o mesmo mecanismo de permissão — mas são estados separados: marcar uma não afeta a outra. Nenhuma das duas cria um comentário; ver Comentar exame para o recurso de texto livre.
Funcionamento
Ler o estado atual (GET /exams/:id/critical-finding ou /technical-suspicion): devolve o
valor booleano atual da flag, quando ela mudou pela última vez (since) e quem mudou (by). A
leitura do achado crítico também registra um evento de auditoria
(diagnosis.exam.critical-finding-read); a leitura da suspeita técnica não gera esse evento —
divergência confirmada no código (ver mais abaixo).
Marcar/desmarcar (PUT/DELETE /exams/:id/critical-finding ou /technical-suspicion):
- Autentica o access token e autoriza a permissão específica da flag
(
exam:flag-critical-findingouexam:flag-technical-suspicion) com escopo RLS sobre oiddo exame — sem acesso ao exame ou à unidade, a resposta é 404. - Busca o exame (com lock de escrita) e valida o corpo:
reasoné opcional, mas se enviado tem no máximo 500 caracteres. - Se o valor pedido (
truenoPUT,falsenoDELETE) já é o valor atual da flag, a operação é idempotente: devolve o estado atual sem gravar nada de novo, sem gerar um novo evento de auditoria. - Caso contrário, grava a mudança (quem marcou/desmarcou e quando), registra o evento de
auditoria correspondente (
diagnosis.exam.critical-finding-changedoudiagnosis.exam.technical-suspicion-changed, com oreasonna metadata) e agenda a reprojeção do item de worklist do exame.
Descobrir capacidades (GET /exams/:id/capabilities): rota auxiliar usada para o front decidir
se mostra as ações de sinalização, sem precisar tentar a chamada e tratar um 403. Devolve, entre
outras capacidades do exame não relacionadas a este documento (canAssignTeam,
canRevealPatientIdentity), os campos canFlagCriticalFinding e canFlagTechnicalSuspicion.
Endpoints
| Método | Rota | Descrição |
|---|---|---|
| GET | /v1/exams/:id/capabilities | Indica quais ações clínicas o ator pode executar no exame |
| GET | /v1/exams/:id/critical-finding | Lê o estado atual da flag de achado crítico |
| PUT | /v1/exams/:id/critical-finding | Marca o exame como achado crítico |
| DELETE | /v1/exams/:id/critical-finding | Desmarca o achado crítico |
| GET | /v1/exams/:id/technical-suspicion | Lê o estado atual da flag de suspeita técnica |
| PUT | /v1/exams/:id/technical-suspicion | Marca o exame como suspeita técnica |
| DELETE | /v1/exams/:id/technical-suspicion | Desmarca a suspeita técnica |
Versão: v1
Swagger: tag Diagnosis — Exam clinical flags · Rota (Dev): http://localhost:3000/v1/exams/{id}/critical-finding
Lógica de decisão das sete rotas (achado crítico e suspeita técnica seguem o mesmo desenho):
Permissões
| Rota | Guards | Permissão / escopo |
|---|---|---|
GET /exams/:id/capabilities | JwtAuthenticationGuard, AuthorizationGuard | exam:read, RLS pelo id |
GET /exams/:id/critical-finding | idem | exam:read, RLS pelo id |
PUT/DELETE /exams/:id/critical-finding | idem | exam:flag-critical-finding, RLS pelo id, checado de novo na camada de serviço |
GET /exams/:id/technical-suspicion | idem | exam:read, RLS pelo id |
PUT/DELETE /exams/:id/technical-suspicion | idem | exam:flag-technical-suspicion, RLS pelo id, checado de novo na camada de serviço |
Um usuário sem acesso à unidade do exame recebe 404, e um usuário com acesso ao exame mas sem
a permissão de flag recebe 403 — os dois comportamentos são confirmados pelo teste de ponta a
ponta diagnosis-exam-clinical-flags.e2e.spec.ts (um ator só com exam:read recebe 403 ao tentar
PUT; um ator de outra unidade recebe 404).
Divergência confirmada no código. A rota de descoberta
GET /exams/:id/capabilitiescalculacanFlagTechnicalSuspicionchecando a permissãoexam:mark-clinical(DiagnosisExamAccessPolicy.canFlagTechnicalSuspicion), mas a rota que de fato altera a flag (PUT/DELETE /exams/:id/technical-suspicion) exigeexam:flag-technical-suspicion(DiagnosisExamAccessPolicy.canFlagTechnicalSuspicionInUnit, chamada dentro do serviço). São permissões diferentes. Na prática, isso pode fazercapabilitiesrespondercanFlagTechnicalSuspicion: falsepara quem temexam:flag-technical-suspicionmas nãoexam:mark-clinical(ou o inverso).exam:mark-clinicalé a permissão de uma funcionalidade distinta (GET/PUT /exams/:id/clinical-markings, fora do escopo deste documento). Trate a resposta decapabilitiesparacanFlagTechnicalSuspicioncomo não confiável até esta divergência ser corrigida ou explicada pelo time de Diagnóstico.A confirmar — responsável: time de Diagnóstico; data: 24/09/2026.
Headers
| Header | Obrigatório | Descrição |
|---|---|---|
Authorization | Sim | Bearer <access_token> |
Content-Type | Sim (em PUT) | application/json |
Path parameters
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id | uuid | Sim | Identificador do exame |
Query parameters
Nenhum.
Body
PUT /exams/:id/critical-finding ou /technical-suspicion — SetDiagnosisCriticalFindingRequest / SetDiagnosisTechnicalSuspicionRequest:
json{ "reason": "Achado compatível com pneumotórax hipertensivo, comunicado ao plantão." }
| Campo | Tipo | Obrigatório | Validação |
|---|---|---|---|
reason | string | Não | @MaxLength(500) — não confunda com o texto de um comentário; fica só na metadata do evento de auditoria, não vira um registro em diagnosis_exam_comments |
GET, DELETE e GET /capabilities não recebem corpo. Um DELETE com corpo é ignorado (a rota
não declara @Body()).
Response
200 — DiagnosisExamClinicalFlagEnvelopeResponse (mesmo formato para as duas flags):
json{"data": {"value": true,"since": "2026-09-24T12:00:00.000Z","by": "0196cf9a-9e32-7d69-a38d-7360192a811f"}}
Ao desmarcar (DELETE), a resposta volta a { "value": false, "since": null, "by": null }.
200 — GET /exams/:id/capabilities:
json{"data": {"canAssignTeam": true,"canFlagCriticalFinding": true,"canFlagTechnicalSuspicion": false,"canRevealPatientIdentity": false}}
Erros
| Classe de erro | errorCode | Status | Quando ocorre |
|---|---|---|---|
| (guard de autenticação) | UNAUTHENTICATED | 401 | token ausente, inválido ou expirado |
DiagnosisExamNotFoundError | EXAM_NOT_FOUND | 404 | exame não existe ou está fora do escopo do ator (unidade diferente) |
ForbiddenAction | FORBIDDEN_ACTION | 403 | ator tem acesso ao exame mas não tem a permissão específica da flag (exam:flag-critical-finding ou exam:flag-technical-suspicion) |
| (validação de payload) | BAD_REQUEST | 400 | reason maior que 500 caracteres |
Regras de negócio
| ID | Regra | Comportamento esperado |
|---|---|---|
| RN-01 | Achado crítico e suspeita técnica são independentes | marcar/desmarcar uma não altera a outra (confirmado lendo o serviço: não há chamada cruzada entre changeCriticalFinding e changeTechnicalSuspicion) |
| RN-02 | PUT/DELETE são idempotentes por valor | repetir a mesma operação (já marcado → marcar de novo) devolve 200 com o estado atual, sem gerar novo evento de auditoria nem novo registro de "desde quando" |
| RN-03 | since/by refletem a última mudança real | desmarcar zera os dois (since: null, by: null); marcar grava o usuário autenticado e o instante do servidor |
| RN-04 | reason não é comentário | o motivo informado no PUT só é gravado na metadata do evento de auditoria; não cria linha em diagnosis_exam_comments nem aparece na listagem de comentários do exame |
| RN-05 | Leitura do achado crítico é auditada; leitura da suspeita técnica não é | confirmado no controller (criticalFinding() passa por ReadDiagnosisExamAuditService.readCriticalFinding, que grava diagnosis.exam.critical-finding-read; technicalSuspicion() chama o serviço de flags diretamente, sem auditoria de leitura) |
| RN-06 | Alterar a flag reprojeta o worklist do exame | is_critical_finding/is_technical_suspicion do item de worklist (GET /worklist) são recalculados de forma assíncrona, não nesta resposta |
Compliance
| Órgão / norma | Exigência | Como a rota atende |
|---|---|---|
| LGPD | base legal de tratamento por profissional de saúde; minimização | reason é de preenchimento opcional e limitado a 500 caracteres; a flag em si não expõe dado de paciente |
| HIPAA | trilha de quem marcou/desmarcou, quando e por quê | eventos diagnosis.exam.critical-finding-changed / technical-suspicion-changed carregam before/after, reason e o ator, consultáveis via Audit |
| ANVISA (indireto) | rastreabilidade de uma marcação clínica no prontuário | since/by preservam quem fez a última mudança; o histórico completo de mudanças fica no log de auditoria (append-only), não apenas o estado atual |
Variáveis de ambiente
Nenhuma variável de ambiente específica destas rotas foi identificada neste levantamento.
Tempo médio de resposta
A confirmar — responsável: time de Diagnóstico; data: 24/09/2026. Não há medição publicada; não
foi executado neste levantamento.
Requisitos não funcionais
| Requisito | Definição |
|---|---|
| Idempotência | Sim, por valor — PUT/DELETE repetidos com o mesmo valor-alvo não geram novo evento nem erro |
| Rate limit | Não identificado @Throttle específico nestas rotas neste levantamento |
| Cache | Não |
| Auditoria | Sim, exceto na leitura da suspeita técnica (ver RN-05) |
Divergências e lacunas confirmadas
- Desenho totalmente diferente do legado. No sistema antigo (Bitrix, card F-003), achado
crítico e suspeita técnica eram registrados como um comentário com texto prefixado
(
"Achado Critico: "/"Motivo da remoção do Achado Crítico: ") que também alterava a flag do exame numa única chamada (POST /exame/achadocritico,POST /exame/suspeitaTecnica). O código atual não tem prefixo de texto, não cria comentário e usa rotasPUT/DELETEdedicadas com um camporeasonseparado. Trate a especificação legada (BR-CMT-040aBR-CMT-046) como substituída pelo comportamento atual. - Regra de exclusão mútua não existe mais. A regra legada de que marcar achado crítico desliga
automaticamente a suspeita técnica (
BR-CMT-092) não tem equivalente no código atual — as duas flags são independentes (RN-01). Se o produto ainda espera essa exclusão mútua, é uma lacuna a levar ao time de Diagnóstico, não um comportamento a documentar como existente. - Flags de duplicado/original não fazem mais parte desta rota. O legado propagava
is_original/is_duplicadojunto com o achado crítico (BR-CMT-042); no código atual esses campos existem emdiagnosis/examcomo parte de uma capacidade separada (marcação de exame duplicado/original), sem relação comcritical-finding— fora do escopo deste documento. - Divergência de permissão em
capabilities— ver o quadro na seção Permissões acima (canFlagTechnicalSuspicionchecado comexam:mark-clinical, mas a rota de escrita exigeexam:flag-technical-suspicion).
Relacionado
- 💬 Comentar exame — recurso de texto livre, independente destas flags
- 📋 Audit — consultar logs — como ler os eventos
diagnosis.exam.critical-finding-*etechnical-suspicion-* - 📂 Módulo: Comment
- 🖥️ Tela:
A confirmar — responsável: time de frontend; data: 24/09/2026.(fora do escopo deste levantamento, que cobriu apenas o backend)