Uuid Toolkit

When to Use a UUID for a FHIR Resource ID vs a Natural Identifier

Every FHIR resource has a logical id. It can be a UUID, a sequential integer, a natural business key, or anything else that satisfies the id datatype rules. The choice is not academic. A resource id is what every reference in the graph points at, what every downstream cache keys on, and what every audit log records. Getting the choice right saves a category of incidents that are almost impossible to fix once the resource is out. The site's fullUrl UUID spinner turns out v4 UUIDs ready to drop into Bundle.entry.fullUrl. For the wider setting, Double Shot Reviews has more.

The Two Real Options

  • UUID — cryptographically random 128-bit identifier
  • Natural business key — MRN, order number, external system's id

Everything else — sequential integer, hash of content, timestamp — is a variant of one of these two.

Why UUIDs Win More Often Than You'd Guess

  • Cross-system uniqueness without coordination
  • No accidental information leak in the id itself
  • Safe to generate at the edge before the server sees the resource
  • Enables transaction Bundles with urn:uuid: cross-entry references
  • Cannot be guessed by attackers walking the id space

Each of these is a real benefit. Together they are why most modern FHIR systems default to UUIDs for internal ids.

When Natural Keys Win

  • The identity is externally-owned and stable (federal EIN, NPI number)
  • Downstream systems already dispatch on the natural key
  • Human-readable ids are a hard requirement
  • The id space is naturally small and coordinated

Even here, the safer pattern is often to use a UUID for the resource id and carry the natural key in identifier[]. For the distinction, [the difference between resource id and identifier[]](/uuid-toolkit/resource-id-vs-identifier-array/) is the entry.

What Not To Use

  • Auto-increment integers — leaks record count, cross-tenant collision risk
  • Timestamps — collisions on concurrent creates
  • Content hashes — every update changes the id
  • Randomly-drawn short strings — collision probability climbs fast

Every one of these produces silent bugs. Prefer UUIDs.

The FHIR id Rules

Resource.id has to match the FHIR id datatype:

  • Length 1 to 64 characters
  • Characters from A-Z, a-z, 0-9, -, .

A UUID formatted with hyphens (36 characters including four hyphens) fits. Base32 encodings work too. Custom alphabets have to be checked against the datatype.

Why UUIDs Play Well With Transaction Bundles

Transaction Bundles let entries reference each other via urn:uuid:... placeholders. The server rewrites those URNs to real ids atomically at commit. That workflow is only clean when the placeholders are UUIDs.

For the mechanic, urn:uuid: in Bundle.entry.fullUrl and why it matters is the entry.

The v4 vs v7 Question

UUID v4 is fully random. UUID v7 embeds a timestamp for sortability. Both are cryptographically strong when generated properly. For the trade-off in a FHIR context, UUID v4 vs v7: is the tradeoff meaningful for FHIR? covers it.

Default to v4 unless you have a specific reason to prefer v7.

The Migration Problem

Teams migrating from sequential ids to UUIDs face a real transition: every existing reference has to be rewritten, every cache invalidated, every audit log mapped. For the pattern, UUID hygiene for teams migrating from sequential IDs is the entry.

The Short Version

UUIDs for internal ids, natural keys in identifier[] when they exist externally. Do not encode information in the id. Do not use auto-increment. Prefer v4 unless v7 has a specific job to do. The spinner is where the v4 generation happens.

Halftone-comic diagram of a UUID resource id vs a natural identifier in identifier[] side by side with FHIR trade-offs annotated, with dot pattern shading and hot-pink accents on cream paper

Sources