Written by Technical Team | Last updated 10.10.2025 | 14 minute read
Every clinical workflow starts with the same foundational step: “Who is this patient?” If that identification is wrong, everything that follows—prescribing, referral, discharge summaries, even appointment reminders—carries risk. In England, the source of truth for demographics is the NHS Personal Demographics Service (PDS). The PDS FHIR API is the modern, internet-facing way to access that record using HL7® FHIR® resources, enabling systems to search for a patient and retrieve an authoritative demographic record—name, date of birth, address, contact details, registered GP and the NHS number. It sits on NHS England’s API platform with an OpenAPI-described spec, making it approachable for product teams who prefer well-documented REST contracts.
Crucially, the PDS FHIR API supports two distinct access modes with different capabilities and governance: restricted access mode, which is application-to-application and intentionally limited for safer unattended use cases, and healthcare worker access mode, which enables richer search and update operations while attributing actions to an authenticated user through NHS Identity (CIS2) and role-based access controls. Choosing between these modes isn’t an afterthought; it determines the user journeys you can support and the evidence you must provide during onboarding.
We set out to build a patient lookup module that would run inside a multi-service clinical application used by operational teams across several care settings. From the outset we decided patient search must behave differently depending on the access mode. In restricted mode, our exact requirement was simple: provide a back-office reconciliation workflow that, given high-quality demographic inputs, returns a unique match or an explicit “no/too many matches” signal. In healthcare worker mode, we wanted a richer, user-attributable experience that supports multiple results, on-screen selection and, in certain roles, updates to contact details or other permitted fields. That early decision anchored every UX and technical choice that followed because PDS enforces different guardrails in each mode.
Another early decision was to keep PDS as the system of record for nationally shared demographics and treat any locally cached patient index as a convenience layer, not a replacement. That governance choice is reflected in the API’s own conformance requirements: if you cache demographics locally, you must keep that cache in step with PDS at key moments (start of an episode, before sending patient communications, before admission/discharge, or before retrieving clinical information from Spine services). This protects sensitive patients (where addresses, telecoms and certain relationships must not be displayed) and prevents stale or dangerous data being shown. So we baked synchronisation triggers into our workflow engine, and we surface explicit warnings when PDS indicates sensitive, invalid or superseded records.
The next design pivot involved identity assurance and auditability. In healthcare worker mode, RBAC is not a nice-to-have: if your product supports multiple-match search or updates, you must honour the national RBAC model and attribute calls with the user’s current role (URPID). We designed our sign-in sequence to pin a user’s CIS2 session and role selection to an API token used for PDS calls, ensuring audit trails join up between our app, the platform and PDS. We also avoided showing “matching confidence” scores in the UI, because PDS requires selection decisions to be based on the data, not a numeric score. The result is a UI that nudges clinicians toward deterministic checks—date of birth, address lines, GP practice—rather than over-optimistic heuristics.
A subtle but critical detail is how we treat the NHS number. A PDS lookup is how you verify you’ve got the correct NHS number; in fact, many national APIs expect a verified NHS number obtained via PDS “tracing”. Our module elevates NHS number handling to a first-class concept: when a user enters an NHS number, we call GET /Patient/{nhsNumber} for a definitive read and store the returned FHIR Patient as our golden copy for downstream events. When users don’t have the NHS number, our healthcare worker flow runs a parameterised search that either returns a pick-list or a safe failure state that can be escalated. In restricted mode we deliberately don’t support multiple results because that’s beyond the allowed capability.
Finally, we aligned the user experience to clear states and outcomes. A patient lookup yields one of four things: a unique match (continue), multiple matches (select or refine), too many matches (narrow criteria), or no match (capture evidence and escalate). Each state is visually distinct, keyboard-navigable and accessible. This sounds basic, but in practice it’s the difference between a crisp, safe clinical workflow and one that encourages guesswork. Across both access modes, we prioritised observability—every call has a X-Request-ID for traceability in our logs and in the NHS platform support flow.
Design non-negotiables we adopted:
From a developer’s perspective, the PDS FHIR API feels like a standard FHIR R4 service with a carefully constrained surface tailored to national safety rules. In practice we implement two primary interactions: search (FHIR Patient queries) and retrieve by NHS number (FHIR read on Patient/{id}). In the integration environment, you’ll see the familiar platform base path with a PDS segment and FHIR version—for example, the pattern used in GET https://int.api.service.nhs.uk/personal-demographics/FHIR/R4/Patient/{nhsNumber}. For searches, we issue GET /Patient with parameters such as family, given, birthdate, gender and—where available—postcode. The API implements FHIR search semantics, including comparison prefixes like eq on dates; we found both birthdate=eqYYYY-MM-DD and birthdate=YYYY-MM-DD behave equivalently, consistent with baseline FHIR behaviour.
Headers matter. The platform consistently expects an Authorization: Bearer <access_token> header conveying either application credentials (restricted mode) or user-context (healthcare worker mode) depending on your onboarding. Your requests should also include an apikey (issued during onboarding) and a unique X-Request-ID so that operational teams can correlate logs across your service and NHS England’s API platform. These conventions are used across multiple national APIs and make supportable, auditable integrations possible. We standardised a request wrapper in our client library that enforces the header set, generates a V4 UUID per call, and records all request metadata for troubleshooting.
The two access modes are enforced not only by authentication pattern, but also by capability. In restricted access mode, you can search only where the result is a unique match and you can retrieve a single patient by NHS number; updates and multi-result pick-lists are off the table. In healthcare worker mode, subject to RBAC and legitimate relationship, you can perform broader searches (and your UI must list results in the order returned by PDS), and you can choose to implement updates to certain fields with all the extra conformance responsibilities that entails. Our codebase reflects this with compile-time flags that exclude not-permitted screens from restricted builds, eliminating accidental exposure.
To accelerate development we used the Integration Test (INT) environment backed by official test data packs—spreadsheets of realistic scenarios for unique matches, multiple matches, and “too many matches”. The team wrote a small harness that converts those spreadsheets into fuzzed search inputs (introducing spacing variants, diacritics and common transpositions) to exercise our matching UI. Where we needed edge cases that weren’t in the packs, we liaised with the NHS test data team to extend coverage. This helped us harden the UX for “dirty data” and conflicting attributes before we ever touched production.
A quick word on status and health checking: the integration environment exposes a lightweight _ping endpoint you can call without credentials. We used that for an external monitor to distinguish connectivity issues from authentication issues, reserving authenticated smoke tests for scheduled runs that exercise real searches and retrieves using known test NHS numbers from the packs. This division of labour made alerting both sensitive and specific.
Real-world demographics are messy. The PDS FHIR API codifies the handling of clinically important edge cases, and the case study wouldn’t be complete without showing how we turned those rules into user-centred behaviours.
Sensitive (restricted) records. If PDS designates a record as sensitive, your UI must not display the sensitive flags in a search result list, and—once selected—must avoid showing location-revealing details (addresses, telecoms, related persons, GP or pharmacy) to users who aren’t explicitly entitled to see them. In healthcare worker mode, you should warn the user that the record is sensitive and that location data will be restricted unless their RBAC rights allow otherwise. Our UI shows a neutral “Access controlled” badge and explains why some fields are hidden, reducing support tickets while protecting the individual.
Superseded NHS numbers. PDS can tell you that the NHS number you’ve used has been replaced with a new one. When that happens, and an interactive user is present, the guidance is unambiguous: warn the user that the record is wrongly identified, replace the NHS number if it does not already exist locally, and (where possible) tell the patient their new number. When both the old and new numbers exist locally, keep the older one for the current episode and refer the case to back office for resolution. We implemented an in-app banner with a guided “Replace and continue” action and an automated task to the admin team when reconciliation is needed.
Invalid records. If PDS flags an NHS number as invalid, your system must warn users that the local record is wrongly identified, mark the record so every access shows a persistent warning about potential data anomalies, and de-couple the local record from PDS (cease syncing) until a back-office fix is complete. Our module moves the record into a “quarantine” state that blocks downstream messaging, prevents updates from being sent to PDS, and forces a re-couple workflow once the issue is resolved.
Operational behaviours we enforce for these edge cases:
The technical happy path is only half the story. To make a PDS-backed lookup module boringly reliable, we invested heavily in platform-aligned security, observability, and governance from day one.
Security and authentication. On NHS England’s platform, application-restricted APIs that handle personal data typically use signed JWT (private key JWT) OAuth 2.0 to obtain access tokens, while some less sensitive application-restricted APIs use simple API keys. PDS—given its personal/sensitive nature—follows the signed JWT pattern for application-restricted mode; healthcare worker mode layers CIS2 user authentication and RBAC on top. In both modes, your calls will carry an Authorization: Bearer token and an apikey. We created a single “token broker” service that manages private keys, signs JWTs, caches tokens, and injects headers so individual services remain stateless and simple.
Request correlation and support. Every call from our module includes X-Request-ID and we persist the response’s own request/correlation identifiers alongside the clinical event. This tiny habit pays off when diagnosing a single failed lookup amongst thousands of successes; platform support can jump straight to a server-side trace using our ID. We added structured logging (method, URL path template, latency, status, OperationOutcome details) that’s safe to ship and scrubbed of personal data.
Local patient indexes: useful but disciplined. Many providers maintain a local patient index (LPI) for speed and offline resilience. The PDS conformance rules don’t prevent that—but they do require you to sync at significant events and to handle sensitive, invalid and superseded states exactly as PDS prescribes. We built LPI syncing as a reusable workflow step (call PDS, compare a whitelist of “key fields”, surface any differences to the user), and we used a version ID from PDS to determine synchronisation behaviour. When the user rejects a change to key fields in an update flow, we de-couple and task back office, as the guidance requires. The value of doing it “the PDS way” is simple: every module in our estate now handles edge cases consistently.
Environments, test data and smoke tests. The Integration Test environment and official test data packs let you rehearse everything you’ll see in production—from clean, single matches to multiple and “too many” matches, plus real-world “dirty data”. We automated a daily smoke suite that searches for a rotating subset of those records and retrieves specific NHS numbers, alerting our team if anything deviates. For bare-metal availability we poll the integration _ping endpoint to separate pure connectivity issues from authentication/authorisation faults.
User experience and human factors. Safety guidance in healthcare worker mode says not to display “matching level” and to show search results in the order PDS returns them. Our UI honours both. We also reduced cognitive load by highlighting exact token matches in the pick-list (DOB, postcode fragments) and using calm, predictable states rather than animated “confidence metres”. For sensitive records we avoid dramatic danger colours and instead present clear, factual messaging about why certain fields are hidden and what the user can do next (e.g., “ask an RBAC-permitted colleague to view address”). The aim is to reduce error and escalation without training clinicians in API arcana.
When the NHS number is present, go straight to read. PDS FHIR treats GET /Patient/{id} as the definitive read operation. Where our workflows receive the NHS number from a barcode or upstream feed, we skip search entirely and go straight to a retrieve. This removes ambiguity and shortens time-to-answer. Downstream services (like booking or prescription flows) inherit that verified patient context to comply with the broader Spine rule: a verified NHS number should be obtained from PDS before calling national APIs about that patient.
Governance, onboarding and future-proofing. Onboarding for PDS requires you to document care settings, intended end users, which endpoints you will call, and how you will keep local copies in sync. You also need to evidence your RBAC model (if you support multi-match searches or updates) and demonstrate safe handling for sensitive, invalid and superseded records. We composed our evidence pack in the language of the conformance pages, which made reviews faster and reduced back-and-forth with the onboarding team. As the API evolves, the platform site makes changes visible (for example, page “last edited” timestamps), and we keep a standing task to revisit the conformance pages after notable updates.
Pragmatic search strategies. In healthcare worker mode, we encourage users to start with strong identifiers (NHS number when available, else surname + DOB + gender, optionally postcode). On the wire we use FHIR’s search parameters in a straightforward way: family, given, birthdate, gender, and so on. If you follow FHIR semantics for dates, the default equals behaviour aligns with eq. For safety we normalise input (trim spaces, case-fold names, standardise hyphens and apostrophes) before constructing the URL, but we never “over-clever” the request—PDS is the source of truth for matching, not our UI.
Putting it all together: a durable blueprint:
A patient lookup sounds trivial until you put it in a clinical context. The PDS FHIR API bakes in the safety and governance that clinical software demands: attribution of user actions, sensible restrictions for unattended modes, and clear handling of sensitive, invalid and superseded records. The case study above shows the pattern that worked for us: treat PDS as the truth, keep local copies disciplined and synchronised, attribute healthcare worker actions, and implement UI states that directly reflect the national rules. Do those things and your lookup module will feel fast to users, be predictable under load and, most importantly, remain safe when the edge cases arrive.
As the platform continues to evolve, staying close to the official conformance pages, API catalogue entries and test data packs ensures your implementation remains aligned with current expectations. That diligence pays off: your team spends less time firefighting and more time delivering patient-centred features—on the right record, every time.
Is your team looking for help with NHS PDS integration? Click the button below.
Get in touch