FHIR gives the client and the server both the option of picking a resource id. POST to a type endpoint asks the server to assign one. PUT to an instance endpoint carries the client's choice. That flexibility is deliberate, and choosing where to generate the UUID has real trade-offs. Edge generation is fast and offline-friendly; server generation is centralized and audit-friendly. The site's fullUrl UUID spinner lets the client generate its own. For the wider FHIR framing, the FHIR review hub has more.
Edge Generation
The client creates the UUID and PUTs the resource at Type/{uuid}. Or, for transaction Bundles, uses urn:uuid: in fullUrl and cross-references. Server accepts or rejects the choice.
Pros:
- Client can create the resource offline and sync later
- Transaction Bundle cross-references work without a pre-flight round trip
- No dependency on the server for id assignment
- Faster — no server round trip to get an id first
Cons:
- Client has to have a trustworthy RNG
- Server has less control over id conventions
- Duplicate id detection lives on the server anyway
For the transaction-Bundle mechanic, urn:uuid: in Bundle.entry.fullUrl and why it matters is the entry.
Server Generation
The client POSTs to a type endpoint without an id. The server assigns one and returns it in the Location header.
Pros:
- Server controls id conventions
- Server-side audit visibility on every id assignment
- No client dependency on RNG quality
- Central place to enforce id policy across writers
Cons:
- Requires a round trip for every id
- Offline creation is not natively supported
- Transaction Bundles either can't cross-reference or need URNs anyway
The Hybrid Pattern
Some deployments accept client-supplied ids but rewrite them under specific conditions. That is a valid pattern but requires the client to handle the id-change response.
Simplest pattern: pick one strategy per endpoint and stick with it.
When Edge Wins
- Mobile or offline clients — no server round trip
- Batched creates where round trips dominate — one Bundle instead of N POSTs
- Transaction Bundles with cross-references — URNs solve the same problem, cleaner
- Systems where the client's identity is trusted for id generation
For the collision side, collision-free UUIDs in a multi-tenant FHIR deployment is the entry.
When Server Wins
- Regulated environments where every id assignment must be audited server-side
- Deployments where the client is not trusted to seed a good RNG
- Legacy migrations where the server assigns from a namespace the client cannot know
- Environments where the id encodes routing information
Client-Side RNG Quality
If you generate at the edge, verify the client's RNG:
- Node.js — use
crypto.randomUUID()or a maintained UUID library - Browsers — use
crypto.randomUUID()(all modern browsers) - Mobile — use platform-provided secure RNG APIs
- Never —
Math.random()or unseeded PRNGs
For the v4 vs v7 discussion, UUID v4 vs v7: is the tradeoff meaningful for FHIR? covers the version choice.
The Duplicate Case
If the client generates an id that already exists on the server, the PUT either overwrites (upsert) or fails. FHIR PUT is upsert by default — that is a spec-driven behavior worth remembering.
If overwrite is not the desired behavior, add If-None-Match: * to reject on existing.
The Short Version
Edge generation is fast, offline-friendly, and requires trusted RNG. Server generation is centralized, audit-friendly, and requires a round trip. Pick per endpoint. Use crypto.randomUUID() at the edge if you go that way.
Sources
- HL7 canonical FHIR HTTP interactions specification - HL7 canonical FHIR HTTP interactions specification