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].
| 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 |
| 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.