Vendor profiles
A vendor profile bundles everything subscription-service needs to talk to one EHR vendor’s HL7 v2 (or proprietary REST) feed: message mappings, vendor-specific quirks, audit-event enrichments, sample messages, and a regression-test suite. A profile is a .tgz drop-in — install one and the engine just works against that vendor.
Where profiles live
The canonical, community-maintained catalog of profiles is the subscription-service-profiles repo. Each profile lives in its own directory:
subscription-service-profiles/├── epic/├── meditech/├── cerner/├── athena/└── _template/Two flavors ship today:
- Community profiles (Apache 2.0) — best-effort, contributed by integrators, maintained on a best-effort cadence
- Certified profiles (commercial) — the same shape, plus a signed regression-test report, indemnity statement, and an upgrade SLA
A non-commercial deployment can ship to production using a community profile; the difference is who’s on the hook when an EHR vendor changes a quirk.
What a profile contains
profiles/epic/├── manifest.yaml # profile metadata + ingest + mappings + quirks + audit├── maps/ # FHIR Mapping Language (.fml) StructureMaps│ ├── hl7v2-ADT-A04-Epic.fml│ ├── hl7v2-ORM-O01-Epic.fml│ └── hl7v2-ORU-R01-Epic.fml├── tests/ # positive + negative regression tests│ ├── adt-a04/│ ├── orm-o01/│ └── oru-r01/└── README.md # human-readable: what versions, what quirks, support contactThe manifest.yaml shape is documented in detail in the engine repo’s architecture doc under “Profile manifest.” A minimal Epic profile:
profile: id: epic version: 2024.1 schemaVersion: 1 vendor: name: Epic Systems productLine: Epic productVersion: "2024.x" fhirVersions: [R4] hl7Versions: [v2.5, v2.5.1, v2.7]
ingest: - id: mllp-default type: hl7v2-mllp port: 2575 facilityResolver: epic-msh-3-facility
mappings: - messageType: ADT^A04 map: maps/hl7v2-ADT-A04-Epic.fml tests: [tests/adt-a04/]
quirks: msh3-format: facility-shortcode-then-pipe empty-pid-strategy: synthesize-from-mrn attachment-encoding: base64-with-rtf-prefix-trim
audit: agent-system: epic enrichments: - addOriginatingUser: pv1.7 - addPatientFacility: msh.4Every quirks and audit key corresponds to a hook the engine recognizes — unknown keys fail the load with a clear error. Add new quirk strategies via a built-in plugin contribution or a side-car JAR.
Non-HL7 profiles
Profiles aren’t limited to MLLP. The ingest block is a typed list; for a vendor like Athena that exposes REST APIs (some FHIR R4, some proprietary), a profile mixes:
athena-native-rest— proprietary polling client (OAuth2 client-credentials, polls forchangedPatients,changedAppointments, etc.)fhir-r4-polling— generic FHIR R4 search polling for the standardized surface
The mapping step for FHIR-source profiles is a normalization (apply US Core slices, strip vendor extensions, stamp provenance) rather than an HL7 transform.
Installing a profile
# Drop the .tgz into the engine's plugin directorycp epic-2024.1.tgz /var/subscription-service/plugins/profiles/
# Validate (without restarting)curl -X POST https://your-host/admin/api/profiles/validate \ -H "Content-Type: application/x-gtar" \ --data-binary @epic-2024.1.tgz
# Restart the engine to loadsystemctl restart subscription-serviceThe admin API also supports zero-downtime reload via POST /admin/api/profiles/reload (Pro tier).
Authoring your own
Start from the _template directory in the profiles repo:
cp -r _template/ myvendor/- Edit
manifest.yamlto declare your vendor + supported message types - Author one StructureMap per message type in
maps/ - Add representative positive + negative sample messages to
tests/ - Run the engine’s profile-test harness:
./gradlew testProfile -Pprofile=myvendor - When all tests pass: package with
./gradlew packageProfile -Pprofile=myvendor→dist/myvendor-1.0.0.tgz - Submit a PR to the catalog repo, or ship the
.tgzprivately
The harness boots the engine against an in-memory HAPI, feeds your sample messages through the configured ingest, applies your maps, and asserts the resulting Bundles match the golden FHIR fixtures under tests/. The same harness runs in CI on every PR to the profiles repo.
When to author vs. wait
If your vendor is in the catalog with a recent profile.version, use it as-is. If the catalog has an older version or your facility uses a non-standard variant (custom MSH-3 format, an in-house OBX-5 convention), fork the existing profile and submit a PR with your delta — vendor profiles are designed to absorb minor facility-specific drift via the quirks block without per-facility forks.
Related
- Plugin SPI — the extension contract a profile implements
- Architecture — where profiles plug into the pipeline
bzimbelman/subscription-service-profiles— the catalog repo