Huli FHIR Core Implementation Guide (R4)
0.1.0 - Release

Huli FHIR Core Implementation Guide (R4) - Local Development build (v0.1.0) built by the FHIR (HL7® FHIR® Standard) Build Tools. See the Directory of published versions

Patient Mapping

Patient Mapping: Huli -> FHIR R4

Resource Identity

FHIR path Cardinality Must Support Justification
Patient.id 1..1 PK, always present
Patient.meta.lastUpdated 0..1 Auto-updated trigger
Patient.managingOrganization 0..1 Yes Required; resolves to an Organization reference

Name

FHIR path Cardinality Must Support Justification
Patient.name[official].given[0] 1..* Yes Required; primary given name
Patient.name[official].family 0..1 Yes Nullable, max 100 chars; primary surname
Patient.name[official].extension[second-lastname] 0..1 Nullable, max 100 chars; second surname via extension
Patient.name[official].text 0..1 Yes Concatenation of the given name and both surnames
Patient.name[official].use 0..1 Hardcoded to official for the primary name
Patient.name[nickname].given[0] 0..* Read-only / not accepted on write. Emitted on read as a use=nickname HumanName; the write decoder reads ONLY name[0] (the official name) and ignores any name[nickname]. Set the alias via the app.

Identifiers

FHIR path Cardinality Must Support Justification
Patient.identifier[curp].value 0..1 Yes MX national ID (RENAPO); system=http://www.renapo.gob.mx/curp
Patient.identifier[rfc].value 0..1 MX tax ID (SAT); system=http://www.sat.gob.mx/rfc
Patient.identifier[nss].value 0..1 MX social-security number (IMSS); system=http://www.imss.gob.mx/nss
Patient.identifier[ine].value 0..1 MX voter credential (INE); system=http://www.ine.mx/credencial
Patient.identifier[ced].value 0..1 CR national ID / cédula (TSE); system=http://www.tse.go.cr/cedula
Patient.identifier[dimex].value 0..1 CR foreign-resident ID (Migración); system=http://www.migracion.go.cr/dimex
Patient.identifier[dpi].value 0..1 GT national ID / DPI (RENAP); system=http://www.renap.gob.gt/dpi
Patient.identifier[passport].value 0..1 Passport; system=http://hl7.org/fhir/sid/passport-{CC} (ISO country appended)
Patient.identifier[mr].value 0..* Medical record number; empty system (org-local)

Demographics

FHIR path Cardinality Must Support Justification
Patient.gender 0..1 Yes Mapped: M->male, F->female, I->other, null->unknown
Patient.birthDate 0..1 Date string "YYYY-MM-DD"
Patient.maritalStatus 0..1 Mapped to MaritalStatus ValueSet; requires value mapping
Patient.active 0..1 Yes 1->true, 3/4->false, 5->excluded

Deceased

FHIR path Cardinality Must Support Justification
Patient.deceasedDateTime 0..1 Yes DATE "YYYY-MM-DD"; when present, use dateTime
Patient.deceasedBoolean 0..1 Yes When the patient is deceased but no date of death is recorded, set deceasedBoolean = true

Mapping rule: If a date of death is recorded, emit deceasedDateTime. If the patient is marked deceased without a date, emit deceasedBoolean = true. Otherwise omit deceased[x].

Contact Information

FHIR path Cardinality Must Support Justification
Patient.telecom[].system 0..1 Yes Direct pass-through (already FHIR-shaped)
Patient.telecom[].value 0..1 Yes Direct pass-through
Patient.telecom[].use 0..1 Direct pass-through
Patient.telecom[].rank 0..1 Direct pass-through

Address

FHIR path Cardinality Must Support Justification
Patient.address[].use 0..1 Direct pass-through
Patient.address[].type 0..1 Direct pass-through
Patient.address[].text 0..1 Read-only / not written. Emitted on read; the write decoder does NOT persist address.text (it is dropped — only the structured fields below are read).
Patient.address[].line 0..* Direct pass-through
Patient.address[].city 0..1 Yes Direct pass-through
Patient.address[].district 0..1 Direct pass-through
Patient.address[].state 0..1 Yes Direct pass-through
Patient.address[].postalCode 0..1 Field name differs (snake_case vs camelCase)
Patient.address[].country 0..1 Direct pass-through

Emergency Contacts

FHIR path Cardinality Must Support Justification
Patient.contact[].name.given[0] 0..1 Single string name; the write decoder reads name.given[0] (NOT name.text)
Patient.contact[].relationship[0].text 0..1 Free text relationship
Patient.contact[].telecom[] 0..* Nested ContactPoint array
Patient.contact[].telecom[] (system=email) 0..1 Separate email field -> additional telecom entry

Extensions

FHIR path Cardinality Must Support Justification
Patient.extension[birthPlace] 0..1 Read-only / not accepted on write. Emitted on read as the standard R4 patient-birthPlace extension; the write decoder does NOT read it. The NOM-024 birth-place ficha field is completed via the app, not the FHIR write surface.
Patient.extension[ethnicity] 0..1 Read-only / not accepted on write. Emitted on read; the write decoder does NOT read it. The NOM-024 ethnicity ficha field is completed via the app, not the FHIR write surface.
Patient.meta.tag[] 0..* Free-form tags to meta tags

Fields NOT Mapped (Huli-internal)

Huli concept Reason
Record creation timestamp Not exposed; no standard FHIR path
Deceased-record timestamp Not exposed; when the deceased record was made
Deceased-record author Not exposed; who made the deceased record
Private insurance Carried by the HuliPrivateInsurance Patient extension (see Extensions)
Occupation Not modelled through FHIR in this IG
Blood type Carried by the HuliBloodType Patient extension (see Extensions)
Migration status Not exposed; low interoperability value
Primary-contact flag No FHIR equivalent; ordering implies priority
Search index Not exposed; internal search index

Write-path validation & enforcement

POST and PUT on /fhir/R4/Patient, plus GET /fhir/R4/Patient/{id}, run through the same patient service the Huli app uses — so the public FHIR API enforces exactly the checks the in-app forms enforce, rather than a thinner FHIR-only subset. Behavioral notes for integrators:

  • CURP coherence (MX). When an identifier carries a CURP (system http://www.renapo.gob.mx/curp), the CURP must be a structurally valid RENAPO value AND compose against the patient's name and birth date. A malformed or contradicting CURP is rejected with a 400 OperationOutcome (issue code invalid). A CURP that the app would accept only after an "inappropriate-word" confirmation is rejected with 422 until the patient is created through the app — the FHIR surface has no confirmation channel.
  • Locality coherence (MX). Mexican locality codes inside address / birthPlace are validated against the national municipality/country catalogs; an incoherent locality is rejected with 400.
  • Duplicate identifiers. Creating a patient whose identifier already exists in the organization returns 409 (the same guard the app applies), rather than silently creating a duplicate record.
  • Telecom replace-vs-merge. A PUT that echoes a telecom entry by system + value preserves that contact point's server-owned identity and any verification state — it does NOT wipe it. Clients do not see (and cannot set) the server-owned ContactPoint id; the merge matches on system + value. Omitting a previously-present telecom entry deletes it.
  • Golden-identity link. A patient created with a valid national identifier is linked into the Patient Master Identity Registry at creation time, so FHIR-created patients participate in cross-organization identity resolution like app-created ones.
  • Name charset. Names containing digits or disallowed characters are rejected with 400.
  • Authorization. Every operation requires BOTH the SMART scope (system/Patient.r|s|c|u) AND the corresponding practice permission (patients.view / patients.create / patients.edit) on the api key's user. A valid scope on a key whose user lacks the permission is denied with 403.
  • Read-only projections — NOM-024 ficha boundary. Several elements are emitted on read but silently ignored on write: name[nickname] (alias), extension[birthPlace], extension[ethnicity], and address.text. The write decoder reads only name[0] (the official name) and the structured address fields. Setting birthPlace or ethnicity over FHIR does NOT populate the patient's NOM-024 ficha — those normativa fields are completed through the app, not the FHIR write surface. A PUT/POST that includes these elements succeeds but does not persist them.

These checks run server-side regardless of client; a request that the Huli app UI would reject is rejected identically over FHIR.