Skip to main content

Módulo API Pública (Integrações) — visão geral

Este módulo é a borda pública do Portal 2.0: a API que sistemas de fora do Portal (RIS/PACS de terceiro, sistemas de parceiro, aplicações de integração) chamam para ler dados clínicos e organizacionais. Ele é fundamentalmente diferente dos demais módulos desta documentação — que descrevem a API consumida pelo próprio frontend do Portal. Aqui quem chama é uma aplicação, não uma pessoa logada, e a credencial é um token de máquina (client_credentials), não uma sessão de usuário.

O módulo vive em dois pacotes do backend (NestJS), com responsabilidades separadas:

  • integration-platform — o control plane: cadastro de adapters, instâncias de integração, segredos cifrados e concessão de rotas (capabilities). É onde um administrador configura o que uma integração pode fazer.
  • public-api-edge — o data plane: as rotas HTTP que o parceiro efetivamente chama (/v1/exams, /v1/tenants, /v1/units) e a infraestrutura transversal que protege essas rotas (autenticação de máquina, resolução de capability, rate limit, formato de erro RFC 7807).

Por que dois pacotes. O control plane é administrado por uma pessoa autenticada no Portal (JwtAuthenticationGuard, o mesmo token de sessão de usuário dos demais módulos). O data plane é chamado por uma aplicação externa autenticada por token de máquina (PublicApiMachineAuthGuard). Separar os pacotes impede que o serviço público (exposto à internet) carregue ou consiga invocar a escrita do control plane — o comentário no código é explícito: "o Edge não carrega — nem consegue chamar — a escrita do control plane" (integration-platform.module.ts).

Dois deployables, duas audiências​

O mesmo pacote integration-platform é composto de duas formas diferentes em dois processos Nest distintos:

DeployableBootstrapPorta (dev)Quem chamaGuard
API principal (app/main)IntegrationPlatformModule.withControlPlane(...)MAIN_API_PORT (default 3000)Administrador de tenant/plataforma, autenticado no PortalJwtAuthenticationGuard (sessão de usuário)
API pública (app/public-api)PublicApiEdgeModule.withDomainModules(...) + IntegrationPlatformModule.withDataPlaneAccess(...)PUBLIC_API_PORT (default 3002)Aplicação parceira (RIS/PACS/sistema externo)PublicApiMachineAuthGuard (token de máquina)

withDataPlaneAccess não registra nenhum controller — só expõe a porta IntegrationPlatformPublicApi (leitura de autorização) e o RouteCapabilityDirectory para o Edge consumir. Toda escrita administrativa (withControlPlane) só existe no processo da API principal. Swagger da API pública: http://localhost:3002/docs (JSON em /docs-json).

Arquitetura​

Autenticação: token de máquina, não sessão de usuário​

Quem chama a API pública não faz login. A aplicação parceira recebe, no cadastro (feito pelo administrador via Identity, fora deste módulo), um oauthClientId e um clientSecret, e troca os dois pelo mesmo endpoint OAuth2 do Portal — POST /oauth/token com grant_type=client_credentials — documentado em Token — API. O token resultante é um JWT de máquina: carrega bindingType (PLATFORM/TENANT/UNIT), tenantId/unitId e escopos, mas não representa nenhum usuário humano.

Em toda requisição à API pública, PublicApiMachineAuthGuard (public-api-edge/shared/auth/public-api-machine-auth.guard.ts):

  1. Autentica o Bearer token via IdentityMachineAccessApi.authenticateMachineAccessToken (implementado no módulo Identity — fora do escopo desta documentação). Um token de usuário comum, um esquema Basic, um JWT malformado ou uma sessão de cliente já revogada são todos 401.
  2. Lê a capability exigida pela rota (@RequiresRouteCapability(...), um decorator por endpoint). Uma rota sem esse decorator falha com PUBLIC_API_ROUTE_CAPABILITY_MISSING (403) — nenhuma rota nova entra no ar sem declarar explicitamente que capability ela exige.
  3. Chama IntegrationPlatformPublicApi.resolveAuthorizedPassiveRoute({ capabilityKey, oauthClientId, unitId }), que resolve, nesta ordem: a integração ligada a esse oauthClientId está ativa → a capability existe no catálogo compilado → a instância tem um grant habilitado para essa capability → (se um unitId foi pedido na rota) esse unitId está dentro do alcance organizacional da instância. Qualquer falha nessa cadeia é INTEGRATION_PLATFORM_ROUTE_CAPABILITY_NOT_GRANTED (403) — a mensagem não distingue "grant ausente" de "grant desabilitado" de "instância não existe": deny-by-default sem vazar configuração de outro tenant.
  4. Grava o resultado (PublicApiMachineContext) na requisição — o controller da rota nunca repete essa resolução.

Ver Autenticação e autorização de máquina para o detalhamento completo, incluindo rate limit por perfil e o formato de erro.

Superfície de dados hoje​

A API pública expõe, hoje, apenas leitura:

DomínioRotasPágina
ExamesGET /v1/exams, GET /v1/exams/:examIdExames
Tenants e unidadesGET /v1/tenants, GET /v1/tenants/:tenantId, GET /v1/tenants/:tenantId/units, GET /v1/units/:unitId, GET /v1/units/:unitId/modulesTenants e unidades

A confirmar — responsável: time de Integration Platform; data: 24/09/2026. O catálogo compilado de capabilities (RouteCapabilityCatalog) já declara chaves de escrita sem rota correspondente implementada em nenhum controller encontrado neste levantamento: public-api.organization.tenant.create, public-api.organization.tenant.update, public-api.organization.unit.create, public-api.organization.unit.update, public-api.exam.update, public-api.report.read e public-api.report.update. São capabilities reservadas para rotas futuras — nenhuma delas é chamável hoje. Ver Concessões de rota.

Como uma integração é montada (control plane)​

Uma integração só atende requisições depois de quatro passos administrativos, todos via API principal (porta 3000, sessão de usuário):

  1. Cadastrar o adapter (opcional, reaproveitável entre instâncias) e publicar uma versão — Adapters.
  2. Criar a instância de integração, com um vínculo organizacional (PLATFORM, TENANT ou UNIT) e, quando fizer sentido, um segredo cifrado por alias — Instâncias de integração e Segredos cifrados.
  3. Conceder as capabilities que essa instância pode usar — Concessões de rota.
  4. Ativar a instância — só então ela responde no data plane.

Compatibilidade com a API legada​

O Portal 1.0 expôs, por anos, uma API pública em Express (mm-pacs-public-api) consumida por integradores como Clinux, MRIS, MV, OneLaudos, Philips Tasy, Pixeon Arya, Pixxel, Qure, Sarah e Totvs. O pacote public-api-edge mantém um inventário compilado dessas ~120 rotas antigas (legacy-compatibility), cada uma já mapeada para a capability canônica que deve substituí-la — mas, no código atual, nenhuma delas está ativa: toda chamada a um caminho legado responde 404. Ver Compatibilidade com a API legada.

A confirmar — responsável: time de Integration Platform; data: 24/09/2026. O pacote external-integration aparece importado em app/main/main.module.ts junto deste módulo. Ele não faz parte do escopo lido neste levantamento (que cobriu apenas public-api-edge e integration-platform) — não é possível confirmar aqui se ele sobrepõe, precede ou substitui parte do cadastro de "aplicações" descrito no Bitrix (F-002) para a API antiga.

Observabilidade e formato de erro​

Toda resposta de erro da API pública segue RFC 7807 Problem Details (application/problem+json), com type, title, status, code, detail, instance, requestId e (quando há tracing distribuído) traceId — nunca stack trace. Toda requisição recebe um X-Execution-Id e um X-Request-Id de correlação, e a API responde em pt-BR por padrão, com Content-Language/Accept-Language para inglês e espanhol. Cada chamada é registrada em log estruturado com o operationId canônico, a capability exigida e o integrationInstanceId — nunca com o payload de negócio.

Páginas deste módulo​

PáginaCobre
Autenticação e autorização de máquinaToken de máquina, resolução de capability, rate limit por perfil, formato de erro
AdaptersPOST /v1/integration-platform/adapters, publicação/aprovação/revogação de versão
Instâncias de integraçãoCiclo de vida da instância, vínculo organizacional, vínculo de OAuth client e de versão de adapter
Segredos cifradosCriação, rotação e revogação de segredo por alias — AES-256-GCM
Concessões de rotaCatálogo de capabilities, grants por instância, de-para declarativo de resposta
ExamesGET /v1/exams, GET /v1/exams/:examId
Tenants e unidadesGET /v1/tenants, GET /v1/tenants/:tenantId, .../units, GET /v1/units/:unitId, .../modules
Compatibilidade com a API legadaInventário das ~120 rotas do Portal 1.0 e seu mapeamento (pendente) para a API atual

:::tip OpenAPI A documentação interativa da API pública (schemas + "Try it out") está disponível em /docs no processo app/public-api (local: http://localhost:3002/docs). As rotas administrativas do control plane aparecem no Swagger da API principal (local: http://localhost:3000/docs). :::