Registering a patient

Turn an external patient record into a stored Patient. You will authenticate, discover the Mexican address codes the NOM-024 model needs — country, municipality, locality, and the address-source provenance — through the terminology service, then POST the patient with a LATAM-shaped name, CURP/RFC identifiers, and the discovered address. One scope carries the whole flow: system/Patient.cru for the write, which also grants the read the MX terminology ValueSets and CodeSystem are gated behind.

The address codes are the reason for the discovery steps. A Mexican organization rejects an address whose municipality or locality codes are inconsistent or absent, so the terminology expansions below hand you values that the write will accept. A non-MX organization can skip the MX address discovery entirely and send a plain address.

Audience

You integrate clinical systems and onboard patients from an external EHR, a registration portal, or a referral intake into a Mexican clinic. You have already run your first authenticated search, you read a Bundle without a viewer, and you know what a FHIR reference and an extension are. You want to take a patient from an external record to a 201 Created.

You'll need

  • A bearer token from HuliPractice (Practice Settings → Integrations → API Keys), or a SMART Backend Services access token. See Bearer Tokens for provisioning and POST /auth/token for the token exchange — this recipe assumes you already hold one.
  • This one scope on that token:
    • system/Patient.cru — create Patient (.cru also grants read +search). The MX terminology resources — the mx-country / mx-municipality / mx-locality ValueSets and the address-source CodeSystem — enforce a per-url system/Patient.rs at the handler, which .cru includes. So this one grant covers discovery and the write.
  • curl, or Node, Python, Java, or Go if you prefer a language client.

End state

You hold a 201 Created whose body is the stored Patient — with a server-assigned id, the patient's full apellido reconstructed from family plus the second-lastname extension, CURP/RFC identifiers under their national systems, and an address carrying the MX state code you discovered. The patient is then resolvable by name or identifier through Patient search.

Steps

1. Export the token

export HULI_TOKEN="<paste your bearer token here>"

A non-MX organization can skip to step 3 and send a plain address (line, city, state, postalCode, country) with no MX codes.

2. (MX path) Discover the address codes

A Mexican address is built from catalog codes, not free text. Expand three ValueSets in order — each narrows the next — then read the address-source CodeSystem for the provenance code.

Country

GET/fhir/R4/ValueSet/$expand?url=https://fhir.huli.ai/r4/ValueSet/mx-country

The expansion returns expansion.contains[], each a {system, code, display}. Pick the country code you need; for a Mexican address that is the code whose display is México.

curl "https://api.huli.ai/fhir/R4/ValueSet/\$expand?url=https://fhir.huli.ai/r4/ValueSet/mx-country" \
  -H "Authorization: Bearer $HULI_TOKEN" \
  -H "Accept: application/fhir+json"
const url = new URL('https://api.huli.ai/fhir/R4/ValueSet/$expand');
url.searchParams.set('url', 'https://fhir.huli.ai/r4/ValueSet/mx-country');

const resp = await fetch(url, {
  headers: {
    Authorization: `Bearer ${process.env.HULI_TOKEN}`,
    Accept: 'application/fhir+json',
  },
});

const vs = await resp.json();
// expansion.contains[].code is the catalog code; .display is the human name.
const country = vs.expansion?.contains?.find((c: { display: string }) => c.display === 'México');
console.log(country?.code, country?.display);
import os
import requests

resp = requests.get(
    "https://api.huli.ai/fhir/R4/ValueSet/$expand",
    params={"url": "https://fhir.huli.ai/r4/ValueSet/mx-country"},
    headers={
        "Authorization": f"Bearer {os.environ['HULI_TOKEN']}",
        "Accept": "application/fhir+json",
    },
    timeout=30,
)
vs = resp.json()
# expansion.contains[].code is the catalog code; .display is the human name.
country = next(c for c in vs["expansion"]["contains"] if c["display"] == "México")
print(country["code"], country["display"])

A representative country expansion:

{
  "resourceType": "ValueSet",
  "url": "https://fhir.huli.ai/r4/ValueSet/mx-country",
  "status": "active",
  "expansion": {
    "total": 1,
    "contains": [
      {
        "system": "https://fhir.huli.ai/r4/CodeSystem/mx-country",
        "code": "1",
        "display": "México"
      }
    ]
  }
}

Municipality

The municipality expansion requires the state code. State codes follow the published Mexican catalog (for example Jalisco is 14); pass the one your patient lives in.

GET/fhir/R4/ValueSet/$expand?url=https://fhir.huli.ai/r4/ValueSet/mx-municipality&state=14
curl "https://api.huli.ai/fhir/R4/ValueSet/\$expand?url=https://fhir.huli.ai/r4/ValueSet/mx-municipality&state=14&_count=100" \
  -H "Authorization: Bearer $HULI_TOKEN" \
  -H "Accept: application/fhir+json"

Omitting state is a 400 — the catalog is too large to expand unscoped. Carry the municipality code you pick into the next expansion.

Locality

The locality expansion requires both state and municipality. Use filter to prefix-match a locality name and keep the page small.

GET/fhir/R4/ValueSet/$expand?url=https://fhir.huli.ai/r4/ValueSet/mx-locality&state=14&municipality=39&filter=Guadalajara
curl "https://api.huli.ai/fhir/R4/ValueSet/\$expand?url=https://fhir.huli.ai/r4/ValueSet/mx-locality&state=14&municipality=39&filter=Guadalajara" \
  -H "Authorization: Bearer $HULI_TOKEN" \
  -H "Accept: application/fhir+json"

Address source

Read the address-source CodeSystem to obtain the provenance code that records the address came from the Mexican normative catalog. The CodeSystem exposes read only (no $expand).

GET/fhir/R4/CodeSystem/address-source
curl "https://api.huli.ai/fhir/R4/CodeSystem/address-source" \
  -H "Authorization: Bearer $HULI_TOKEN" \
  -H "Accept: application/fhir+json"
{
  "resourceType": "CodeSystem",
  "url": "https://fhir.huli.ai/r4/CodeSystem/address-source",
  "status": "active",
  "content": "complete",
  "concept": [
    {
      "code": "mx-normativo-nom024",
      "display": "NOM-024 normative address source"
    }
  ]
}

The single member mx-normativo-nom024 is the provenance value. In the IG's address model the source is carried under the system https://huli.io/fhir/CodeSystem/address-source — note that this provenance system string differs from the https://fhir.huli.ai/r4/CodeSystem/address-source canonical you just read; the read endpoint resolves the code, the IG names the system the stored value uses. See the FHIR Implementation Guide for the exact address-source binding.

3. POST the Patient

Assemble the discovered values into the create body. The name carries the first surname in family and the maternal/second surname in the second-lastname extension on the same name element. Identifiers go under their published national systems (see the note below). The address maps the discovered state code onto address.state, the municipality/locality onto city and district, and the rest of the street address onto line.

The body below is the comprehensive form — every field the create decoder honors on a Patient write, not a minimal example. Required fields are flagged inline; everything else is optional. The Full field reference after the example lists each field, whether the decoder reads it on write, and what it maps to. A minimal write needs only name[0].given[0]; everything else enriches the record.

POST/fhir/R4/Patient
curl -i -X POST https://api.huli.ai/fhir/R4/Patient \
  -H "Authorization: Bearer $HULI_TOKEN" \
  -H "Content-Type: application/fhir+json" \
  -H "Accept: application/fhir+json" \
  -d '{
    "resourceType": "Patient",
    "active": true,
    "name": [
      {
        "use": "official",
        "family": "Hernández",
        "given": ["Carlos"],
        "extension": [
          {
            "url": "https://fhir.huli.ai/r4/StructureDefinition/second-lastname",
            "valueString": "Ramírez"
          }
        ]
      }
    ],
    "gender": "male",
    "birthDate": "1985-07-20",
    "maritalStatus": {
      "coding": [
        { "system": "http://terminology.hl7.org/CodeSystem/v3-MaritalStatus", "code": "M" }
      ]
    },
    "identifier": [
      {
        "use": "official",
        "system": "http://www.renapo.gob.mx/curp",
        "value": "HERC850720HJCRMR04",
        "type": {
          "coding": [
            { "system": "http://terminology.hl7.org/CodeSystem/v2-0203", "code": "CURP" }
          ]
        }
      },
      {
        "use": "official",
        "system": "http://www.sat.gob.mx/rfc",
        "value": "HERC850720AB1",
        "type": {
          "coding": [
            { "system": "http://terminology.hl7.org/CodeSystem/v2-0203", "code": "RFC" }
          ]
        }
      }
    ],
    "telecom": [
      { "system": "phone", "value": "5533112244", "use": "mobile", "rank": 1 },
      { "system": "email", "value": "carlos.hernandez@example.com", "use": "home" }
    ],
    "address": [
      {
        "use": "home",
        "type": "physical",
        "line": ["Calle Morelos 408, Col. Americana"],
        "city": "Guadalajara",
        "district": "Guadalajara",
        "state": "14",
        "postalCode": "44160",
        "country": "MX"
      }
    ],
    "contact": [
      {
        "relationship": [{ "text": "Madre" }],
        "name": { "given": ["María Ramírez"] },
        "telecom": [{ "system": "phone", "value": "5599887766", "use": "mobile" }]
      }
    ],
    "extension": [
      {
        "url": "https://fhir.huli.ai/r4/StructureDefinition/huli-blood-type",
        "valueCode": "O+"
      },
      {
        "url": "https://fhir.huli.ai/r4/StructureDefinition/huli-private-insurance",
        "extension": [
          { "url": "provider", "valueString": "Seguros Monterrey" },
          { "url": "policyNumber", "valueString": "POL-99812" },
          { "url": "certificateNumber", "valueString": "CERT-44120" }
        ]
      }
    ]
  }'
const patient = {
  resourceType: 'Patient',
  active: true, // optional — false maps to an inactive record; absent defaults to active
  name: [
    {
      use: 'official',
      family: 'Hernández', // first surname
      given: ['Carlos'], // given[0] is the only strictly required field
      extension: [
        {
          // The maternal/second surname rides this extension on the name element.
          url: 'https://fhir.huli.ai/r4/StructureDefinition/second-lastname',
          valueString: 'Ramírez',
        },
      ],
    },
  ],
  gender: 'male', // male | female | other (unknown is accepted but stored as empty)
  birthDate: '1985-07-20',
  maritalStatus: {
    // coding[0].code is read verbatim (S/M/D/W/P/U/L); display is ignored
    coding: [{ system: 'http://terminology.hl7.org/CodeSystem/v3-MaritalStatus', code: 'M' }],
  },
  identifier: [
    {
      use: 'official',
      system: 'http://www.renapo.gob.mx/curp', // CURP — published in the FHIR IG
      value: 'HERC850720HJCRMR04',
      type: { coding: [{ system: 'http://terminology.hl7.org/CodeSystem/v2-0203', code: 'CURP' }] },
    },
    {
      use: 'official',
      system: 'http://www.sat.gob.mx/rfc', // RFC — published in the FHIR IG
      value: 'HERC850720AB1',
      type: { coding: [{ system: 'http://terminology.hl7.org/CodeSystem/v2-0203', code: 'RFC' }] },
    },
  ],
  telecom: [
    // system + value read verbatim; use + rank optional
    { system: 'phone', value: '5533112244', use: 'mobile', rank: 1 },
    { system: 'email', value: 'carlos.hernandez@example.com', use: 'home' },
  ],
  address: [
    {
      use: 'home', // optional
      type: 'physical', // optional
      line: ['Calle Morelos 408, Col. Americana'],
      city: 'Guadalajara',
      district: 'Guadalajara',
      state: '14', // the mx-country/state code discovered in step 2
      postalCode: '44160',
      country: 'MX',
    },
  ],
  contact: [
    // emergency / guardian contact — only name.given[0], relationship[0].text, and telecom are read
    {
      relationship: [{ text: 'Madre' }],
      name: { given: ['María Ramírez'] },
      telecom: [{ system: 'phone', value: '5599887766', use: 'mobile' }],
    },
  ],
  extension: [
    {
      // blood type — valueCode stored verbatim
      url: 'https://fhir.huli.ai/r4/StructureDefinition/huli-blood-type',
      valueCode: 'O+',
    },
    {
      // private insurance — provider required, policyNumber / certificateNumber optional
      url: 'https://fhir.huli.ai/r4/StructureDefinition/huli-private-insurance',
      extension: [
        { url: 'provider', valueString: 'Seguros Monterrey' },
        { url: 'policyNumber', valueString: 'POL-99812' },
        { url: 'certificateNumber', valueString: 'CERT-44120' },
      ],
    },
  ],
};

const resp = await fetch('https://api.huli.ai/fhir/R4/Patient', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.HULI_TOKEN}`,
    'Content-Type': 'application/fhir+json',
    Accept: 'application/fhir+json',
  },
  body: JSON.stringify(patient),
});

if (resp.status === 201) {
  const created = (await resp.json()) as { id: string };
  console.log('registered', created.id);
} else {
  const outcome = (await resp.json()) as { issue: { diagnostics: string }[] };
  // The HPB- code is the prefix of issue[0].diagnostics — split on ': '.
  const [code] = outcome.issue[0].diagnostics.split(': ', 1);
  console.log(resp.status, code, outcome.issue[0].diagnostics);
}
import os
import requests

patient = {
    "resourceType": "Patient",
    "active": True,  # optional — False maps to an inactive record
    "name": [
        {
            "use": "official",
            "family": "Hernández",  # first surname
            "given": ["Carlos"],  # given[0] is the only strictly required field
            "extension": [
                {
                    # The maternal/second surname rides this extension on the name element.
                    "url": "https://fhir.huli.ai/r4/StructureDefinition/second-lastname",
                    "valueString": "Ramírez",
                }
            ],
        }
    ],
    "gender": "male",  # male | female | other
    "birthDate": "1985-07-20",
    "maritalStatus": {
        # coding[0].code read verbatim (S/M/D/W/P/U/L); display ignored
        "coding": [{"system": "http://terminology.hl7.org/CodeSystem/v3-MaritalStatus", "code": "M"}]
    },
    "identifier": [
        {
            "use": "official",
            "system": "http://www.renapo.gob.mx/curp",  # CURP — published in the FHIR IG
            "value": "HERC850720HJCRMR04",
            "type": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/v2-0203", "code": "CURP"}]},
        },
        {
            "use": "official",
            "system": "http://www.sat.gob.mx/rfc",  # RFC — published in the FHIR IG
            "value": "HERC850720AB1",
            "type": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/v2-0203", "code": "RFC"}]},
        },
    ],
    "telecom": [
        # system + value read verbatim; use + rank optional
        {"system": "phone", "value": "5533112244", "use": "mobile", "rank": 1},
        {"system": "email", "value": "carlos.hernandez@example.com", "use": "home"},
    ],
    "address": [
        {
            "use": "home",  # optional
            "type": "physical",  # optional
            "line": ["Calle Morelos 408, Col. Americana"],
            "city": "Guadalajara",
            "district": "Guadalajara",
            "state": "14",  # the mx-country/state code discovered in step 2
            "postalCode": "44160",
            "country": "MX",
        }
    ],
    "contact": [
        # emergency / guardian contact — only name.given[0], relationship[0].text, telecom read
        {
            "relationship": [{"text": "Madre"}],
            "name": {"given": ["María Ramírez"]},
            "telecom": [{"system": "phone", "value": "5599887766", "use": "mobile"}],
        }
    ],
    "extension": [
        {
            # blood type — valueCode stored verbatim
            "url": "https://fhir.huli.ai/r4/StructureDefinition/huli-blood-type",
            "valueCode": "O+",
        },
        {
            # private insurance — provider required, policyNumber / certificateNumber optional
            "url": "https://fhir.huli.ai/r4/StructureDefinition/huli-private-insurance",
            "extension": [
                {"url": "provider", "valueString": "Seguros Monterrey"},
                {"url": "policyNumber", "valueString": "POL-99812"},
                {"url": "certificateNumber", "valueString": "CERT-44120"},
            ],
        },
    ],
}

resp = requests.post(
    "https://api.huli.ai/fhir/R4/Patient",
    headers={
        "Authorization": f"Bearer {os.environ['HULI_TOKEN']}",
        "Content-Type": "application/fhir+json",
        "Accept": "application/fhir+json",
    },
    json=patient,
    timeout=30,
)

if resp.status_code == 201:
    print("registered", resp.json()["id"])
else:
    outcome = resp.json()
    # The HPB- code is the prefix of issue[0].diagnostics — split on ": ".
    code = outcome["issue"][0]["diagnostics"].split(": ", 1)[0]
    print(resp.status_code, code, outcome["issue"][0]["diagnostics"])
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	// family carries the first surname; the second-lastname extension carries
	// the maternal surname. Identifier systems are the IG-published CURP/RFC URLs;
	// address.state is the mx-country/state code discovered in step 2. Only
	// name.given[0] is strictly required — every other field below is optional
	// enrichment the create decoder honors.
	body := []byte(`{
		"resourceType": "Patient",
		"active": true,
		"name": [{
			"use": "official",
			"family": "Hernández",
			"given": ["Carlos"],
			"extension": [{
				"url": "https://fhir.huli.ai/r4/StructureDefinition/second-lastname",
				"valueString": "Ramírez"
			}]
		}],
		"gender": "male",
		"birthDate": "1985-07-20",
		"maritalStatus": {
			"coding": [{"system": "http://terminology.hl7.org/CodeSystem/v3-MaritalStatus", "code": "M"}]
		},
		"identifier": [
			{
				"use": "official",
				"system": "http://www.renapo.gob.mx/curp",
				"value": "HERC850720HJCRMR04",
				"type": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/v2-0203", "code": "CURP"}]}
			},
			{
				"use": "official",
				"system": "http://www.sat.gob.mx/rfc",
				"value": "HERC850720AB1",
				"type": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/v2-0203", "code": "RFC"}]}
			}
		],
		"telecom": [
			{"system": "phone", "value": "5533112244", "use": "mobile", "rank": 1},
			{"system": "email", "value": "carlos.hernandez@example.com", "use": "home"}
		],
		"address": [{
			"use": "home",
			"type": "physical",
			"line": ["Calle Morelos 408, Col. Americana"],
			"city": "Guadalajara",
			"district": "Guadalajara",
			"state": "14",
			"postalCode": "44160",
			"country": "MX"
		}],
		"contact": [{
			"relationship": [{"text": "Madre"}],
			"name": {"given": ["María Ramírez"]},
			"telecom": [{"system": "phone", "value": "5599887766", "use": "mobile"}]
		}],
		"extension": [
			{
				"url": "https://fhir.huli.ai/r4/StructureDefinition/huli-blood-type",
				"valueCode": "O+"
			},
			{
				"url": "https://fhir.huli.ai/r4/StructureDefinition/huli-private-insurance",
				"extension": [
					{"url": "provider", "valueString": "Seguros Monterrey"},
					{"url": "policyNumber", "valueString": "POL-99812"},
					{"url": "certificateNumber", "valueString": "CERT-44120"}
				]
			}
		]
	}`)

	req, err := http.NewRequest(http.MethodPost,
		"https://api.huli.ai/fhir/R4/Patient", bytes.NewReader(body))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("HULI_TOKEN"))
	req.Header.Set("Content-Type", "application/fhir+json")
	req.Header.Set("Accept", "application/fhir+json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	out, err := io.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}

	switch resp.StatusCode {
	case http.StatusCreated:
		fmt.Printf("201 registered\n%s\n", out)
	case http.StatusBadRequest: // HPB-00101 structural / CURP-composition / MX locality coherence
		fmt.Printf("400 validation\n%s\n", out)
	case http.StatusUnprocessableEntity: // CURP needs in-app confirmation (no FHIR channel)
		fmt.Printf("422 needs in-app confirmation\n%s\n", out)
	default:
		fmt.Printf("%d\n%s\n", resp.StatusCode, out)
	}
}

Run a minimal patient write against your sandbox — a create needs only name[0].given[0], so this is the smallest body the decoder accepts:

POST/fhir/R4/Patient

Set your sandbox token above to run this request.

A 201 Created returns the stored Patient with a server-assigned id. The family plus the second-lastname extension round-trip, the identifiers round-trip under their systems, and the address carries the state code you sent.

Full field reference

Every field the Patient create decoder reads on write. "Honored" means the create decoder maps the field into the stored record; fields not listed (or marked ignored) are accepted but not persisted from your input. Only name[0].given[0] is required.

FieldReq?Honored on writeNotes
name[0].given[0]requiredyesFirst given name — the only strictly required field (ValidatePatient).
name[0].familyoptionalyesFirst (paternal) surname.
name[0].extension[] …/second-lastname valueStringoptionalyesMaternal / second surname on the same name element.
name[0].useoptionalignoredRead endpoint always emits official.
activeoptionalyestrue → active record, false → inactive; absent defaults to active.
genderoptionalyesmale/female/other map to M/F/I. unknown validates but stores empty.
birthDateoptionalyesYYYY-MM-DD.
maritalStatus.coding[0].codeoptionalyesOne of S/M/D/W/P/U/L (v3-MaritalStatus). display is ignored.
identifier[].systemoptionalyesResolved against the published system list (CURP/RFC/etc.). An unrecognized system is a 400.
identifier[].valueoptionalyesRequired when an identifier entry is present.
identifier[].type / useoptionalignoredType is re-derived from the resolved system on read.
telecom[].systemoptionalyesphone / email / etc.
telecom[].valueoptionalyesThe number or address.
telecom[].useoptionalyeshome/mobile/work.
telecom[].rankoptionalyesPreference order (integer).
address[].line[]optionalyesStreet address lines.
address[].cityoptionalyesCarries the locality for an MX address.
address[].districtoptionalyesCarries the municipality for an MX address.
address[].stateoptionalyesThe MX state code discovered in step 2.
address[].postalCodeoptionalyes
address[].countryoptionalyes
address[].use / typeoptionalyeshome/work; physical/postal.
contact[].name.given[0]optionalyesEmergency / guardian contact name.
contact[].relationship[0].textoptionalyesFree-text relationship label.
contact[].telecom[]optionalyessystem/value/use per telecom above.
extension[] …/huli-blood-type valueCodeoptionalyesBlood type, e.g. O+.
extension[] …/huli-private-insuranceoptionalyesNested provider (required within the block), policyNumber, certificateNumber.
managingOrganizationoptionalignoredServer stamps the token's organization; a supplied reference is validated as a UUID but not used to reassign.
deceasedDateTimeoptionalignored on createDeceased status is set through the dedicated deceased flow, not the create decoder.

What to verify

  • HTTP status is 201. 201 Created
  • The response body's resourceType is Patient and it carries a server-assigned id — a UUID you did not send.
  • name[0].family is the first surname and the second-lastname extension on the same name element carries the maternal surname — read both to reconstruct the full apellido.
  • Each identifier[].system round-trips unchanged (http://www.renapo.gob.mx/curp for CURP), proof the system was recognized, not dropped.
  • address[0].state is the MX state code you discovered, and gender is the FHIR token (male/female/other) you sent.

What can go wrong

All errors return a FHIR OperationOutcome, never a bare string — {severity, code, diagnostics}, with no details object. Branch on the HTTP status and issue[0].code (the FHIR IssueType); the Huli code is the prefix of issue[0].diagnostics, split on ": " to extract it. Structural problems — a missing required field, malformed JSON, a bad date — surface as HPB-00101.

400 Bad Request HPB-00101structural validation. A required field is missing

or malformed: no given name, a birthDate that is not YYYY-MM-DD, a gender outside male/female/other, or an identifier under an unrecognized system. Send a valid given[0], a recognized identifier system (the IG-published CURP/RFC URLs), and a well-formed date.

400 Bad Request CURP composition validation. A CURP whose internal composition

is inconsistent — the date segment, the sex letter, or the check digit not matching the rest — is rejected even when the string is the right length. Compose the CURP correctly from the patient's own data, or omit it and add it once verified.

400 Bad Request inconsistent MX locality codes. A Mexican organization rejects an

address whose municipality or locality is incoherent with the state — a municipality that does not belong to the state you sent, or a locality outside the municipality — validated against the national municipality/country catalogs. This is why step 2 discovers the codes against the catalog; re-run the step 2 expansions to pick a consistent state → municipality → locality chain.

422 Unprocessable Entity CURP needs in-app confirmation. A CURP that the app would accept

only after an "inappropriate-word" confirmation is rejected until the patient is created through the app — the FHIR surface has no confirmation channel. Create the patient in HuliPractice, or omit the CURP and add it there.

A representative 400 body:

{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "invalid",
      "diagnostics": "HPB-00101: Validation error"
    }
  ]
}

Next recipes

  • Booking an appointment end-to-end — with the patient registered, discover a service, practitioner, room, and free slot, then POST the Appointment for them.
  • Run your first authenticated Patient search — resolve the patient you just created by name or identifier (CURP/RFC) to confirm it persisted and to fetch its id later.
  • Authenticate as a SMART Backend Service — swap the admin bearer token for client_credentials + private_key_jwt (RS384, 5-minute access token) when you ship the onboarding flow server-to-server.