Skip to content

Universal Ingestion API

The Universal Ingestion API enables healthcare systems to submit FHIR R4-compliant data to the HAMI platform. This endpoint accepts FHIR Bundles containing patient records, appointments, encounters, observations, conditions, and other clinical resources.

All API requests must include an API key in the request headers.

X-API-KEY: your_api_key_here

URL: https://api.hami.health/api/g/{groupId}/ingestion/fhir/v4

Method: POST

Content-Type: application/json

Path Parameters:

  • {groupId} - Your organization’s unique group identifier
HeaderTypeRequiredDescription
X-API-KEYstringYesYour API authentication key
Content-TypestringYesMust be application/json

The request body must be a valid FHIR R4 Bundle with type transaction. The Bundle can contain multiple entries with different resource types.

  • Patient - Patient demographics and identifiers
  • Appointment - Scheduled appointments
  • Encounter - Clinical encounters
  • Observation - Vital signs, lab results, and other observations
  • Condition - Patient diagnoses and conditions
  • Procedure - Medical procedures
  • MedicationRequest - Medication prescriptions
  • AllergyIntolerance - Patient allergies
  • DiagnosticReport - Diagnostic test reports
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{
"fullUrl": "urn:uuid:unique-identifier",
"resource": {
// FHIR Resource content
},
"request": {
"method": "POST",
"url": "ResourceType"
}
}
]
}
FieldTypeDescriptionExample
transactionIdstringUnique identifier for the ingestion transaction"t3zL38PrPDaFp2fLL4DvtY"
{
"transactionId": "t3zL38PrPDaFp2fLL4DvtY"
}
Terminal window
curl --location 'https://api.hami.health/api/g/my-group/ingestion/fhir/v4' \
--header 'X-API-KEY: your_api_key_here' \
--header 'Content-Type: application/json' \
--data '{
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{
"fullUrl": "urn:uuid:f6ff3181-2799-47a0-9b8e-ad5569eb91b0",
"resource": {
"resourceType": "Patient",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">Patient: Ali Khan, Male, DOB: 2004-05-23</div>"
},
"identifier": [
{
"system": "http://hospital.org/mrn",
"value": "24250053463",
"type": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"code": "MR",
"display": "Medical record number"
}
],
"text": "MRN"
}
}
],
"name": [
{
"family": "Khan",
"given": ["Ali"]
}
],
"gender": "male",
"birthDate": "2004-05-23",
"telecom": [
{
"system": "phone",
"use": "mobile",
"value": "+923001234313"
}
]
},
"request": {
"method": "POST",
"url": "Patient"
}
},
{
"fullUrl": "urn:uuid:39f640a9-e304-4c13-8b40-07ece7a03c15",
"resource": {
"resourceType": "Appointment",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">Appointment: Booked from 2024-05-23 10:00 to 11:00 UTC</div>"
},
"status": "booked",
"start": "2024-05-23T10:00:00Z",
"end": "2024-05-23T11:00:00Z",
"participant": [
{
"actor": {
"reference": "urn:uuid:f6ff3181-2799-47a0-9b8e-ad5569eb91b0"
},
"status": "accepted"
}
]
},
"request": {
"method": "POST",
"url": "Appointment"
}
},
{
"fullUrl": "urn:uuid:73ca6eec-0a49-429d-8c4d-86c8ccb8a365",
"resource": {
"resourceType": "Encounter",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">Encounter: In-progress ambulatory visit</div>"
},
"status": "in-progress",
"class": {
"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
"code": "AMB",
"display": "ambulatory"
},
"subject": {
"reference": "urn:uuid:f6ff3181-2799-47a0-9b8e-ad5569eb91b0"
},
"appointment": [
{
"reference": "urn:uuid:39f640a9-e304-4c13-8b40-07ece7a03c15"
}
],
"period": {
"start": "2024-05-23T10:05:00Z"
}
},
"request": {
"method": "POST",
"url": "Encounter"
}
},
{
"fullUrl": "urn:uuid:825d9822-cbe3-4805-9094-adcbf44e86f8",
"resource": {
"resourceType": "Condition",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">Condition: Active confirmed encounter diagnosis of Vomiting</div>"
},
"subject": {
"reference": "urn:uuid:f6ff3181-2799-47a0-9b8e-ad5569eb91b0"
},
"encounter": {
"reference": "urn:uuid:73ca6eec-0a49-429d-8c4d-86c8ccb8a365"
},
"clinicalStatus": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/condition-clinical",
"code": "active"
}
]
},
"verificationStatus": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/condition-ver-status",
"code": "confirmed"
}
]
},
"category": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/condition-category",
"code": "encounter-diagnosis"
}
]
}
],
"code": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "422400008",
"display": "Vomiting"
}
],
"text": "Vomiting"
},
"recordedDate": "2024-05-23T10:10:00Z"
},
"request": {
"method": "POST",
"url": "Condition"
}
},
{
"fullUrl": "urn:uuid:40275738-6dfc-45f8-bacf-f3a702bf1720",
"resource": {
"resourceType": "Observation",
"meta": {
"profile": ["http://hl7.org/fhir/StructureDefinition/bp"]
},
"status": "final",
"category": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/observation-category",
"code": "vital-signs"
}
]
}
],
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "85354-9",
"display": "Blood pressure panel with all children optional"
}
],
"text": "Blood Pressure"
},
"subject": {
"reference": "urn:uuid:f6ff3181-2799-47a0-9b8e-ad5569eb91b0"
},
"encounter": {
"reference": "urn:uuid:73ca6eec-0a49-429d-8c4d-86c8ccb8a365"
},
"effectiveDateTime": "2024-05-23T10:12:00Z",
"performer": [
{
"reference": "Practitioner/practitioner-1",
"display": "Dr. Example"
}
],
"component": [
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "8480-6",
"display": "Systolic blood pressure"
}
],
"text": "Systolic blood pressure"
},
"valueQuantity": {
"value": 79,
"unit": "mmHg",
"system": "http://unitsofmeasure.org",
"code": "mm[Hg]"
}
},
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "8462-4",
"display": "Diastolic blood pressure"
}
],
"text": "Diastolic blood pressure"
},
"valueQuantity": {
"value": 45,
"unit": "mmHg",
"system": "http://unitsofmeasure.org",
"code": "mm[Hg]"
}
}
]
},
"request": {
"method": "POST",
"url": "Observation"
}
}
]
}'

The Patient resource should include:

  • Identifiers: MRN or other patient identifiers with appropriate systems
  • Name: Family name and given names
  • Gender: male, female, other, or unknown
  • Birth Date: ISO 8601 date format (YYYY-MM-DD)
  • Telecom: Contact information (phone, email)

For vital signs observations, use the standard FHIR vital signs profiles:

Vital SignLOINC CodeUCUM Unit
Blood Pressure85354-9mm[Hg] (component-based)
Heart Rate8867-4/min
Respiratory Rate9279-1/min
Body Temperature8310-5Cel or [degF]
Body Weight29463-7kg or [lb_av]
Body Height8302-2cm or [in_i]
Oxygen Saturation2708-6 or 59408-5%

FHIR Bundles support two types of references:

  1. URN-based references (for resources in the same bundle):

    "reference": "urn:uuid:f6ff3181-2799-47a0-9b8e-ad5569eb91b0"
  2. Logical references (for existing resources):

    "reference": "Patient/patient-123"

All datetime fields must use ISO 8601 format with UTC timezone:

  • Format: YYYY-MM-DDTHH:mm:ssZ
  • Example: 2024-05-23T10:12:00Z

Returned when the FHIR Bundle is malformed or invalid.

{
"error": "Bad Request",
"message": "Invalid FHIR Bundle: resourceType must be 'Bundle'",
"statusCode": 400
}

Returned when the API key is missing or invalid.

{
"error": "Unauthorized",
"message": "Invalid or missing API key",
"statusCode": 401
}

Returned when the FHIR Bundle fails validation.

{
"error": "Unprocessable Entity",
"message": "FHIR validation error: Patient.birthDate must be a valid date",
"statusCode": 422,
"details": {
"resourceType": "Patient",
"field": "birthDate",
"issue": "Invalid date format"
}
}

Returned when an unexpected error occurs on the server.

{
"error": "Internal Server Error",
"message": "An unexpected error occurred while processing the FHIR Bundle",
"statusCode": 500
}

API requests are rate-limited to ensure system stability:

  • Rate Limit: 100 requests per minute per API key
  • Burst Limit: 10 requests per second
  • Bundle Size Limit: Maximum 100 entries per bundle

When rate limits are exceeded, the API returns a 429 Too Many Requests response:

{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Please try again later.",
"statusCode": 429,
"retryAfter": 60
}
  • Keep bundles under 100 entries for optimal performance
  • For large datasets, split into multiple bundles and send sequentially
  • Store the returned transactionId for each bundle for tracking and debugging
  • Use URN-based references (urn:uuid:...) for resources within the same bundle
  • Ensure all referenced resources are included in the bundle or exist in the system
  • Use valid UUIDs for URN-based references

Before sending data to the API:

  1. Validate your FHIR Bundle against the FHIR R4 specification
  2. Ensure all required fields are present
  3. Use standard terminologies (LOINC, SNOMED CT, etc.)
  4. Include appropriate resource profiles and meta tags

Implement robust error handling:

  • Retry on network failures with exponential backoff
  • Log all transactionId values for successful submissions
  • Handle validation errors by correcting and resubmitting
  • Monitor rate limits and implement throttling

The API is idempotent when using consistent identifiers:

  • Use the same fullUrl UUIDs for the same resources
  • Duplicate submissions with identical data will not create duplicates
  • Store and reuse transaction IDs for reference

If you’re migrating from HL7 v2 messages, here are common mappings:

HL7 v2FHIR ResourceFHIR Field
PID segmentPatientPatient resource
PID-3Patientidentifier
PID-5Patientname
PID-7PatientbirthDate
PID-8Patientgender
PID-13Patienttelecom (phone)
OBX segmentObservationObservation resource
OBX-3Observationcode
OBX-5Observationvalue[x]
OBX-6ObservationvalueQuantity.unit
# HL7 v2 OBX segment to FHIR Observation
obx_segment = "OBX|1|NM|8867-4^Heart rate^LN||129|/min|||||F"
# Transforms to:
observation = {
"resourceType": "Observation",
"status": "final",
"code": {
"coding": [{
"system": "http://loinc.org",
"code": "8867-4",
"display": "Heart rate"
}]
},
"valueQuantity": {
"value": 129,
"unit": "/min",
"system": "http://unitsofmeasure.org",
"code": "/min"
}
}
  1. Collect Clinical Data: Gather patient data from your EHR or clinical system
  2. Transform to FHIR: Convert data to FHIR R4 format following the specification
  3. Create Bundle: Package resources into a FHIR Bundle with type transaction
  4. Validate: Validate the bundle against FHIR R4 schema and profiles
  5. Submit: POST the bundle to the ingestion endpoint
  6. Store Transaction ID: Save the returned transactionId for tracking
  7. Monitor: Track ingestion status and handle any errors

To validate your FHIR Bundles before submission:

  • FHIR Validator: https://fhir.github.io/validator/
  • HAPI FHIR: Java-based FHIR validation library
  • Python: fhir.resources Python package
  • Node.js: @types/fhir and validation libraries

For additional support or questions about the Universal Ingestion API: