Skip to main content

Ouvir e listar áudios do exame — API

Cobre a consulta dos áudios de ditado já anexados a um exame: a listagem paginada (para exibir a lista de gravações, mais recente primeiro) e a busca de um áudio específico por id (para tocar um item isolado). As duas rotas exigem a mesma permissão e devolvem, para cada áudio, um link temporário de download.

Funcionamento​

Listar (GET /exams/{examId}/audios): autentica o ator e verifica a permissão exam:list-audio no escopo do exame; valida page/limit; busca no banco apenas os áudios ativos (não excluídos) daquele exame, ordenados do mais recente para o mais antigo; para cada um, gera um link de download pré-assinado e devolve a lista.

Buscar por id (GET /exams/{examId}/audios/{audioId}): mesma permissão da listagem; busca o áudio pelo audioId e confirma que ele pertence ao examId da rota — um audioId que existe mas pertence a outro exame é tratado como não encontrado, sem revelar que o registro existe em outro lugar; gera o link de download e devolve o item.

Endpoints​

MétodoRotaDescrição
GET/v1/exams/{examId}/audiosLista os áudios ativos do exame, paginado
GET/v1/exams/{examId}/audios/{audioId}Busca um áudio específico do exame

Versão: v1

Swagger:

Rota (Dev): http://localhost:3000/v1/exams/{examId}/audios

Lógica de decisão das duas rotas (validações e permissão → desfechos):

Permissões​

RotaGuardsPermissão exigida
GET /exams/{examId}/audiosJwtAuthenticationGuard, AuthorizationGuardexam:list-audio (PermissionName.EXAM_LIST_AUDIO), escopo resource sobre o exame (enforcement: rls)
GET /exams/{examId}/audios/{audioId}JwtAuthenticationGuard, AuthorizationGuardexam:list-audio (mesma permissão da listagem — não existe uma permissão separada para "ver um áudio")

Sem token válido → 401 UNAUTHENTICATED. Sem a permissão no escopo do exame → 403 FORBIDDEN_ACTION. Um exame fora do escopo do ator (oculto por RLS) resulta em lista vazia (na listagem) ou 404 (na busca por id) — não em 403.

Headers​

HeaderObrigatórioDescrição
AuthorizationSimBearer <access_token>

Path parameters​

NomeTipoObrigatórioDescrição
examIduuidSimIdentificador do exame (nas duas rotas)
audioIduuidSim (só na busca por id)Identificador do áudio

Query parameters​

NomeTipoObrigatórioDefaultDescrição
pageinteiroNão1Página da listagem (mínimo 1)
limitinteiroNão20Itens por página (mínimo 1, máximo 100)

page/limit só se aplicam à rota de listagem.

Body​

Nenhuma das duas rotas recebe corpo.

Response​

200 — listagem:

json
{
"data": [
{
"id": "0199....-uuid",
"examId": "0198....-uuid",
"unitId": "0197....-uuid",
"audioName": "ditado-torax.webm",
"path": "tenants/.../units/.../diagnosis/exam-audios/0199....audio",
"link": "https://storage.exemplo.com/.../0199....audio?X-Amz-Expires=300&...",
"createdAt": "2026-09-24T12:00:00.000Z"
}
]
}

200 — busca por id:

json
{
"data": {
"id": "0199....-uuid",
"examId": "0198....-uuid",
"unitId": "0197....-uuid",
"audioName": "ditado-torax.webm",
"path": "tenants/.../units/.../diagnosis/exam-audios/0199....audio",
"link": "https://storage.exemplo.com/.../0199....audio?X-Amz-Expires=300&...",
"createdAt": "2026-09-24T12:00:00.000Z"
}
}

Mapa de campos​

CampoTipoValores possíveisDefault
iduuididentificador do áudio—
examIduuidexame ao qual o áudio pertence—
unitIduuidunidade proprietária do exame no momento da gravação—
audioNamestring | nullnome informado no upload ou nome original do arquivo; null se nenhum dos dois estava disponívelnull
pathstring | nullcaminho interno no storage (tenants/{tenantId}/units/{unitId}/diagnosis/exam-audios/{uuid}.audio)null
linkstring (uri) | nullURL de download pré-assinada, válida por 300 segundos a partir da resposta; recalculada a cada consulta, nunca é armazenadanull
createdAtdata ISO-8601data/hora da gravação—

Erros​

Classe de erroerrorCodeStatusQuando ocorre
UnauthenticatedErrorUNAUTHENTICATED401token ausente, inválido ou expirado
ForbiddenActionFORBIDDEN_ACTION403ator sem exam:list-audio no escopo do exame
(validação de payload)BAD_REQUEST400examId/audioId não são uuid válidos, ou page/limit fora da faixa aceita
DiagnosisExamAudioNotFoundErrorEXAM_AUDIO_NOT_FOUND404(só na busca por id) áudio inexistente, excluído, sem path persistido, ou pertencente a outro examId

Regras de negócio​

IDRegraComportamento esperado
RN-01Listagem só devolve áudios ativosregistros com exclusão lógica (deletedAt preenchido) nunca aparecem — o filtro é automático no find/findOne do TypeORM sobre uma entidade com @DeleteDateColumn
RN-02Ordenação é sempre do mais recente para o mais antigoorder: { createdAt: 'DESC' }, sem opção de inverter
RN-03Paginação tem limites fixospage mínimo 1 (default 1); limit entre 1 e 100 (default 20)
RN-04Busca por id valida o vínculo com o exame da rotaum audioId de outro exame devolve 404 EXAM_AUDIO_NOT_FOUND, igual a um id inexistente — não distingue os dois casos na resposta
RN-05O link de download é gerado na hora, não persistidocada chamada gera uma nova URL pré-assinada (300 segundos de validade); duas consultas seguidas ao mesmo áudio devolvem links diferentes

Compliance​

Órgão / normaExigênciaComo a rota atende
LGPDacesso ao dado de voz restrito e com expiraçãoa rota exige permissão específica no exame; o link devolvido expira em 300s, não é um link público permanente
HIPAAcontrole de acesso baseado em papelpermissão exam:list-audio avaliada por exame, mais RLS no banco
ANVISA (indireto)integridade do conteúdo consultadoa listagem não permite alterar o áudio; a exclusão lógica preserva o registro em vez de apagá-lo silenciosamente

A confirmar — responsável: time de Diagnosis; data: 24/09/2026. Não foi confirmado neste levantamento se a reprodução do áudio (o acesso efetivo à URL pré-assinada) gera um evento de auditoria próprio — o código do módulo audita a gravação e a exclusão (diagnosis.exam.audio-added / -removed), mas não foi localizado um evento equivalente para a leitura/reprodução.

Variáveis de ambiente​

Nenhuma variável de ambiente é específica destas rotas. A validade do link (PRESIGNED_AUDIO_LINK_EXPIRY_SECONDS, 300 segundos) é uma constante compartilhada com a rota de gravação — ver Gravar áudio.

Tempo médio de resposta​

A confirmar — responsável: time de Diagnosis; data: 24/09/2026. Não há medição publicada; não foi executado neste levantamento.

Requisitos não funcionais​

RequisitoDefinição
IdempotênciaSim — as duas rotas são leituras, sem efeito colateral no banco (a geração do link pré-assinado não grava nada)
PaginaçãoSim, só na listagem (page/limit, ver acima)
Rate limitNão há @Throttle nestas rotas
CacheNão
AuditoriaNão confirmada para a leitura (ver ressalva em Compliance)

Relacionado​

  • 🖥️ Tela: A confirmar — responsável: time de frontend; data: 24/09/2026. (a pasta docs/portal2/interface ainda não tem página de áudio nesta branch; este levantamento cobriu apenas o backend)
  • 📂 Módulo: Áudio
  • ⏮️ Etapa anterior: Gravar áudio
  • ▶️ Próximo passo: Excluir áudio