AGFA — Ficha de Integração
Integração outbound com o RIS AGFA: após o exame ser laudado, a Public API monta um envelope dadosIntegracao (paciente, exame, médico e corpo do laudo) e envia ao endpoint configurado no cliente.
:::info Classificação
- T2 (outbound): adapter
agfaemPOST /exam/redirect— Mobilemed envia laudo aourldetb_integracao_config. - T1 (inbound): não se aplica — não há rota gateway específica AGFA no código. :::
Identificação
| Campo | Valor |
|---|---|
| Tipo | T2 (adapter outbound) |
adapter_key | agfa |
| Classe | adapters/AgfaAdapter.js |
| Cliente/tenant | Multi-tenant (configurado por tb_integracao) |
| Status | ativo |
| Owner squad | a confirmar |
| Contato técnico cliente | a confirmar |
| Última revisão | 2026-08-14 |
Escopo
Fluxo completo
Rotas envolvidas
| Direção | Método | Rota | acao (middleware) | Função |
|---|---|---|---|---|
| Outbound | POST | /exam/redirect | REDIRECT | Converte laudo e envia ao url do adapter agfa |
O adapter implementa apenas apply(). Não há override de notify(), getRequests nem _validateToken. POST /exam/notify usaria IntegrationAdapter.notify() vazio — não é o contrato deste adapter.
:::caution OpenAPI
Não existe path /exam/agfa (nem equivalente) em swaggerDocs.json. O contrato HTTP do gateway é o genérico Integra exame (POST /exam/redirect).
:::
Ambientes
| Ambiente | Base URL |
|---|---|
| Homologação | https://gateway-homolog.mobilemed.com.br/api-public |
| Produção | https://integracao.mobilemed.com.br/v1 |
Headers obrigatórios no gateway: token, api (mob ou one).
:::caution Ambiente do footer
AgfaAdapter.js carrega modelos com require("../models")("mob") no topo do arquivo. A busca de rodapé (ExameLaudoFooter) sempre usa o banco mob, independentemente do header api.
:::
Configuração (banco)
Registros em tb_integracao + tb_integracao_config (models/IntegrationConfig.js).
| Campo | Valor típico | Observação |
|---|---|---|
adapter | agfa | Deve bater com adapters/index.js |
acao | REDIRECT | Mapeada pelo middleware em POST /exam/redirect |
url | URL do endpoint AGFA do cliente | AgfaAdapter não implementa convertUrlVariable; URL com [#variable#] falharia no redirect() |
reportFormat | HTML, RTF, PDF ou TEXT | Passado a ReportConvertedService.convert() |
tipo_envio | JSON | Payload é objeto JS; redirect() envia como JSON quando tipo_envio === "JSON" |
base64 | conforme cliente | Repassado ao conversor de laudo |
auth_type | TOKEN, BASIC_AUTH ou DEFAULT | Ver seção Autenticação |
automatic_retry | conforme tb_integracao | Usado pelo RetryService no fluxo de redirect |
medico_padrao_nome | string | Usado em medico_executante quando medico_padrao_crm está preenchido |
medico_padrao_crm | {crm}-{uf} | Ex.: 123456-SP — split por -; se vazio, usa study.medico |
additional_settings.version | ex. "2016.4.2.40" | Default no código: "2016.4.2.40" |
additional_settings.site_identifier | string ou null | Default: null |
additional_settings.alternativeMethod | ex. "put" | Método HTTP alternativo no externalRequest (fluxo genérico de redirect()) |
headers_adicionais | JSON em tb_integracao | Headers extras no POST outbound |
Pré-requisito de conteúdo: a empresa precisa de registro em tb_exame_laudo_footer (models/ExameLaudoFooter.js). Placeholders substituídos no HTML: [#laudadoDate] (DD/MM/YYYY) e [#laudadoTime] (HH:mm), a partir de study.ultima_data_laudo em UTC.
Autenticação
Inbound (gateway → Mobilemed)
| Header | Obrigatório | Descrição |
|---|---|---|
token | sim | Token da integração (tb_integracao.token) |
api | sim | Ambiente: mob ou one |
Outbound (Mobilemed → AGFA)
Resolvida em exam.service.js → redirect() (o adapter não monta headers):
auth_type (config ou integração) | Comportamento |
|---|---|
TOKEN + token_name | Header dinâmico: {token_name}: {client_token} |
BASIC_AUTH | Header Authorization: Basic {base64(user:pass)} |
DEFAULT | Herda auth_type / credenciais de tb_integracao |
Credenciais adicionais podem ser injetadas via headers_adicionais (JSON).
Rotação de credenciais: a confirmar (responsável / data).
Contrato
Inbound
Não se aplica.
Outbound — POST /exam/redirect → AgfaAdapter.apply()
Antes de converter o laudo, o adapter:
- Resolve o médico executante (
getDefaultPhysicianInfo). - Se há médico padrão, anexa ao HTML do laudo um bloco centralizado com
study.medico(nome +CRM/{UF} {crm}) — o payload continua com o médico padrão. - Anexa o rodapé da empresa (
addRodapeInfo). - Converte o HTML via
ReportConvertedService.
Payload enviado ao url:
json{"dadosIntegracao": {"exame_id": 1001,"message_datetime": "20260814120000","message_id": "<nanoid 36 chars>","version": "2016.4.2.40","site_identifier": null,"codigo_pedido": "PED-001","accession_number": "2026001001","study_date": "20260810143000","study_description": "RX Torax PA","requested_preocedure_name": "RX Torax PA","patient_id": "PAC001","patient_name": "Paciente Exemplo","patient_birth_date": "19800520","patient_sex": "M","medico_executante": "Dr. Exemplo","medico_crm": "123456","report_sign_datetime": "20260814114500","report_body": "<conteúdo convertido conforme reportFormat>"}}
| Campo | Regra no código |
|---|---|
message_datetime | moment() local, formato YYYYMMDDHHmmss |
message_id | nanoid(36) — novo a cada envio |
version | additional_settings.version ou "2016.4.2.40" |
site_identifier | additional_settings.site_identifier ou null |
study_date | data_realizacao em UTC → YYYYMMDDHHmmss |
patient_birth_date | aniversario_paciente → YYYYMMDD, ou null |
patient_sex | "M" se sexo_paciente_id == 1; senão "F" |
report_sign_datetime | ultima_data_laudo em UTC, ou moment() se nulo |
requested_preocedure_name | Cópia de estudo_descricao — o nome do campo no código contém o typo preocedure |
medico_executante / medico_crm | Médico padrão (crm = parte antes de -) ou study.medico.nome / study.medico.crm |
Erros comuns (outbound)
| Erro | Causa |
|---|---|
TypeError ao acessar htmlFooter.html | Empresa sem registro em tb_exame_laudo_footer (findOne retorna null) |
Falha em study.medico.estado.uf | Associação medico/estado ausente ao montar o bloco HTML do médico padrão |
Erro ao redirecionar exame devido a laudo ainda não processado | Laudo sem pdf_path (fluxo genérico de redirect()) |
BLOCKED_BY_CONFIG | Bloqueio por redirectConditionals em additional_settings |
404 No integration found | Token inválido/inativo ou config acao=REDIRECT ausente |
adapter.convertUrlVariable is not a function | url contém [#variable#] e o AgfaAdapter não implementa o método |
Idempotência / retry
- Exames com
status_id = 5passam pela lógica de retentativa (RetryService) no controllerredirect. automatic_retryemtb_integracaocontrola retentativas automáticas.message_idé gerado comnanoid(36)a cadaapply()— não é idempotente por design.
Operação
| Item | Detalhe |
|---|---|
| Código adapter | mm-pacs-public-api/adapters/AgfaAdapter.js |
| Registro da chave | adapters/index.js → agfa: AgfaAdapter |
| Controller outbound | controllers/exam.controllers.js → redirect |
| Service outbound | services/exam.service.js → redirect() |
| Conversão de laudo | services/reportConverter.service.js |
| Rodapé | models/ExameLaudoFooter.js (tb_exame_laudo_footer) |
| Deploy / runbook | a confirmar |
| Monitoramento | Logs de integração no fluxo de redirect (tipo 42) |
| Escalação | a confirmar |
Evidências
- Homologação: a confirmar (responsável / data)
- Payloads de exemplo acima são anonimizados — não incluir PHI em PRs ou Bitrix
Rastreio
| Item | Valor |
|---|---|
| Tarefa Bitrix | a confirmar |
| PR código | a confirmar |
| PR documentação | a confirmar |
| URL publicada | /docs/interfaceReference/integrations/agfa |
| Ficha no repo | docs/interfaceReference/integrations/agfa/index.md |