Concessões de rota (route grants) — API
Habilita, para uma instância de integração específica, uma capability do catálogo público — o
mecanismo que decide se essa integração pode chamar uma rota da API pública, com que perfil de rate
limit, e com que de-para declarativo de resposta. Sem um grant habilitado, a instância não acessa
nenhuma rota, mesmo estando ACTIVE.
Funcionamento
O catálogo de capabilities
RouteCapabilityCatalog é um catálogo compilado em código, não uma tabela — configuração não
cria rota nova, só habilita uma capability que já existe no catálogo. Cada RouteCapability tem:
- uma chave (
public-api.<domínio>.<ação>, ex.public-api.exam.read); - um owner (
DIAGNOSIS,ORGANIZATION,REPORTouINTEGRATION_PLATFORM); - um efeito (
READouWRITE); - uma ou mais operações canônicas (
método+operationId+path, sempre sob/v1/) — agrupadas quando representam a mesma permissão (ex. ler uma unidade e listar as unidades de um tenant são a mesma leitura).
Capabilities do catálogo
| Chave | Efeito | Owner | Operações | Rota implementada hoje? |
|---|---|---|---|---|
public-api.organization.tenant.create | WRITE | ORGANIZATION | POST /v1/tenants | Não |
public-api.organization.tenant.read | READ | ORGANIZATION | GET /v1/tenants, GET /v1/tenants/{tenantId} | Sim |
public-api.organization.tenant.update | WRITE | ORGANIZATION | PATCH /v1/tenants/{tenantId} | Não |
public-api.organization.unit.create | WRITE | ORGANIZATION | POST /v1/tenants/{tenantId}/units | Não |
public-api.organization.unit.read | READ | ORGANIZATION | GET /v1/tenants/{tenantId}/units, GET /v1/units/{unitId} | Sim |
public-api.organization.unit.update | WRITE | ORGANIZATION | PATCH /v1/units/{unitId} | Não |
public-api.organization.module.read | READ | ORGANIZATION | GET /v1/units/{unitId}/modules | Sim |
public-api.exam.read | READ | DIAGNOSIS | GET /v1/exams, GET /v1/exams/{examId} | Sim |
public-api.exam.update | WRITE | DIAGNOSIS | PATCH /v1/exams/{examId} | Não |
public-api.report.read | READ | REPORT | GET /v1/reports/{reportId} | Não |
public-api.report.update | WRITE | REPORT | PATCH /v1/reports/{reportId} | Não |
A confirmar — responsável: time de Integration Platform; data: 24/09/2026. As capabilities marcadas "Não" nesta tabela não têm nenhum controller correspondente em
public-api-edgeneste levantamento — são reservas de superfície futura, não rotas quebradas.public-api.report.readcorresponde, no Bitrix, à feature legada F-004 — Recuperar laudo assinado digitalmente, ainda não reescrita na API atual.
Habilitar uma chave fora do catálogo é sempre rejeitado (UnknownRouteCapabilityError, 422) — a
configuração nunca inventa uma superfície pública nova.
O grant
Um IntegrationPlatformRouteGrant liga (integrationInstanceId, capabilityKey) a um status
(ENABLED/DISABLED), um rateProfile (STANDARD por default) e, opcionalmente, um de-para
declarativo de resposta (ver seção abaixo).
- Habilitar (
PUT): cria o grant se não existir, ou atualizarateProfile/mapping se já existir. É idempotente: repetir com os mesmos valores devolve o mesmo grant e não gera outro evento de auditoria. - Desabilitar (
DELETE): marcaDISABLEDsem apagar o registro — desabilitar de novo é no-op. Não existe grant "removido"; existe habilitado ou desabilitado. - Listar: devolve todos os grants da instância, com o
ownerde cada capability resolvido pelo catálogo.
Como uma chamada é autorizada
ResolveIntegrationPlatformRouteGrantService.resolveAuthorizedCapability é o que
PublicApiMachineAuthGuard chama a cada requisição da API pública (ver
Autenticação e autorização de máquina). A ordem é fixa e
deny-by-default, sem revelar qual passo falhou:
- a
capabilityKeyexiste no catálogo compilado; - a instância (resolvida pelo
oauthClientIddo token) existe e estáACTIVE; - existe um grant para
(instância, capability)e ele estáENABLED.
Só então a chamada segue para o alcance de unidade (resolvido separadamente, ver Instâncias de integração) e para o controller da rota.
De-para declarativo de resposta
Um grant pode carregar um mapping declarativo (DeclarativeResponseMapping) que transforma a
resposta canônica no formato que aquele parceiro específico espera — sem que a rota canônica saiba
disso. É deliberadamente burro: uma lista ordenada de passos, cada um uma operação fechada, nunca
uma expressão ou condicional. No pior caso (campo ausente), o passo é ignorado — aplicar um mapping
não pode falhar de formas interessantes.
| Operação | Efeito | Exige |
|---|---|---|
RENAME | renomeia um campo (from → to); se from não existir, não faz nada | from, to |
SELECT | mantém só os campos listados | fields (≥ 1) |
OMIT | remove os campos listados | fields (≥ 1) |
CONSTANT | define um campo com um valor fixo | to, value |
DEFAULT | preenche to só se estiver ausente/null; um valor falsy existente é preservado | to, value |
ENUM | traduz o valor de from por uma tabela; valor fora da tabela fica como está | from, values |
FORMAT | DATE_ONLY (primeiros 10 caracteres), LOWERCASE, UPPERCASE ou TRIM (default) sobre um campo string | from, format |
A especificação é validada na configuração (ao habilitar o grant), não na requisição — o parceiro nunca descobre um mapping inválido só quando chama a rota de verdade.
Endpoints
| Método | Rota | Descrição |
|---|---|---|
| PUT | /v1/integration-platform/instances/:instanceId/route-grants/:capabilityKey | Habilita (ou atualiza) a capability para a instância |
| DELETE | /v1/integration-platform/instances/:instanceId/route-grants/:capabilityKey | Desabilita a capability |
| GET | /v1/integration-platform/instances/:instanceId/route-grants | Lista os grants da instância |
Versão: v1
Swagger: Integration Platform — Route grants · Rota (Dev): http://localhost:3000/v1/integration-platform/instances/:instanceId/route-grants
Estas rotas rodam na API principal (porta 3000) — ver visão geral do módulo.
Permissões
| Rota | Guards | Perfil / escopo exigido |
|---|---|---|
| Todas deste grupo | JwtAuthenticationGuard | vínculo TENANT/UNIT da instância: assertCanManageTenantIntegrations; vínculo PLATFORM: assertCanManagePlatformIntegrations |
Headers
| Header | Obrigatório | Descrição |
|---|---|---|
Authorization | Sim | Bearer <token de sessão de usuário> |
Path parameters
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
instanceId | uuid | Sim | Instância que recebe a concessão |
capabilityKey | string | Sim | Chave do catálogo, ex. public-api.exam.read |
Body
EnableIntegrationPlatformRouteGrantRestRequest (PUT):
json{"rate_profile": "STANDARD","response_mapping": [{ "operation": "RENAME", "from": "accession_number", "to": "accessionNumber" },{ "operation": "OMIT", "fields": ["patient"] },{ "operation": "CONSTANT", "to": "source", "value": "mobilemed" }]}
| Campo | Tipo | Obrigatório | Validação |
|---|---|---|---|
rate_profile | enum STANDARD/INTERACTIVE/BULK | Não | default STANDARD |
response_mapping | array de passos | Não | ver tabela de operações acima; validado inteiro na escrita |
Response
200 — IntegrationPlatformRouteGrantRestResponse:
json{"grant_id": "0198f3a4-...-uuid","integration_instance_id": "0198f3a4-...-uuid","capability_key": "public-api.exam.read","owner_domain": "DIAGNOSIS","rate_profile": "STANDARD","response_mapping": null,"status": "ENABLED"}
Erros
| Classe de erro | errorCode | Status | Quando ocorre |
|---|---|---|---|
UnknownRouteCapabilityError | INTEGRATION_PLATFORM_UNKNOWN_ROUTE_CAPABILITY | 422 | capabilityKey fora do catálogo compilado |
RouteCapabilityNotGrantedError | INTEGRATION_PLATFORM_ROUTE_CAPABILITY_NOT_GRANTED | 403/404 (conforme rota) | desabilita uma capability sem grant existente; ou (no data plane) chamada sem grant habilitado |
InvalidResponseMappingError | INTEGRATION_PLATFORM_RESPONSE_MAPPING_INVALID | 400 | especificação de response_mapping malformada |
Regras de negócio
| ID | Regra | Comportamento esperado |
|---|---|---|
| RN-01 | Configuração nunca cria rota | só uma capabilityKey já compilada no catálogo pode virar grant |
| RN-02 | Habilitar é idempotente | repetir com os mesmos valores não gera evento de auditoria duplicado |
| RN-03 | Grant nunca é apagado | desabilitar marca DISABLED; o histórico do grant permanece |
| RN-04 | Deny-by-default sem distinguir motivo | grant ausente e grant desabilitado produzem a mesma resposta ao chamador da API pública |
| RN-05 | Mapping é validado na configuração | um response_mapping inválido nunca chega a ser aplicado numa resposta real |
| RN-06 | Mapping nunca falha "de forma interessante" | campo ausente é ignorado pelo passo, nunca lança erro em tempo de requisição |
Compliance
| Órgão / norma | Exigência | Como a rota atende |
|---|---|---|
| LGPD | minimização — expor só o necessário a cada parceiro | SELECT/OMIT no de-para permitem remover campos (ex. dados de paciente) da resposta a um parceiro específico |
| ANVISA (indireto) | rastreabilidade de quem concedeu o quê | evento de auditoria integration_platform.route_grant.enabled/disabled, com snapshot antes/depois |
Variáveis de ambiente
Nenhuma específica desta rota.
Requisitos não funcionais
| Requisito | Definição |
|---|---|
| Idempotência | Sim para habilitar com os mesmos valores; desabilitar repetido também é no-op |
| Paginação | Não — lista todos os grants da instância |
| Rate limit | O rate_profile do grant é o que a API pública aplica na chamada real — ver Autenticação e autorização de máquina |
| Auditoria | Sim — evento por habilitação/desabilitação, via outbox assinado |
Relacionado
- 🖥️ Tela: Não se aplica — administração feita hoje só via API/Swagger neste levantamento.
- 📂 Módulo: API Pública (Integrações)
- 🔗 Onde o grant é consultado a cada requisição: Autenticação e autorização de máquina
- 🔗 Rotas de dados hoje cobertas: Exames, Tenants e unidades