Getting certified as an NPHIES vendor was the thing that opened the Saudi market for our platform. It was also the project where I learned that healthcare interoperability standards are about ninety percent standard and ten percent local reality, and that the ten percent is where your schedule goes.
If you're approaching NPHIES — or any national FHIR-based exchange — here's what I wish someone had told me before we started.
The Spec Is Not the Hard Part
NPHIES is built on FHIR R4. FHIR R4 is well documented, widely implemented, and genuinely good. If you've worked with it before, the resource model will feel familiar: Claim, ClaimResponse, Coverage, CoverageEligibilityRequest, Patient, Organization, bundled into messages and exchanged over HTTPS.
Reading the spec, you will conclude this is a few weeks of work. Reading the spec is not the hard part.
The hard part is that a national exchange isn't just FHIR — it's FHIR plus a national profile plus local terminology plus business rules that live in validation responses rather than documentation. Your Claim resource isn't valid because it's valid FHIR. It's valid because it satisfies the local profile, uses the expected code systems, carries the identifiers the exchange expects in the format it expects them, and passes rules you will mostly learn about by failing them.
Build for the Profile, Not the Base Spec
The single most useful decision we made was to validate against the national profiles in our own pipeline, early, rather than discovering conformance problems by submitting.
Generic FHIR validation tells you your resource is structurally sound. Profile validation tells you whether this exchange will accept it — required extensions present, bound value sets respected, cardinalities narrowed as the profile demands. These are very different answers, and only the second one matters.
Wire profile validation into your test suite. Make an invalid bundle a failing build, not a failing submission. The feedback loop between "we submitted" and "we found out" is measured in hours or days; the loop between "we compiled" and "we found out" is seconds.
Identifiers and Terminology Will Eat a Sprint
Every healthcare integration has an identifier problem, and it's never the interesting part of the work.
Your system has its own patient identifiers, provider identifiers, payer identifiers, and claim numbers. The exchange has its own. Some map cleanly. Some have formatting rules. Some are namespaced by an authority whose URI you must get exactly right. Identity mapping is unglamorous plumbing that no roadmap ever allocates enough time for.
Terminology is the same story. ICD-10 diagnoses, procedure codes, drug codes, local code systems for things like service categories. You need mapping tables, you need to version them, and you need to decide what happens when a code arrives that you don't recognise — because one will, and silently dropping it is a data-integrity incident waiting to be discovered by an auditor.
Treat both as first-class subsystems with their own tests. They're the most common source of "why was this rejected" and the easiest to get quietly wrong.
Design for Asynchrony From the Start
Message-based exchange is asynchronous, and that has architectural consequences you should accept early rather than bolt on later.
You submit. You get an acknowledgement. The real response arrives later — maybe seconds, maybe not. Meanwhile things time out, get retried, arrive out of order, or arrive twice.
That means, non-negotiably:
- Idempotency. A retried submission must not create a second claim. Ever. Build this in from the first line of code.
- Correlation. Every message needs an identifier you can trace end to end, through your logs and theirs.
- Explicit state. A claim in flight is a distinct state from submitted and from adjudicated. Model it, persist it, and make it visible in the UI — otherwise operations staff resubmit, and now you have duplicates.
- Durable retries with backoff. In-memory retry loses messages on restart. Use a real queue.
Every one of these is harder to retrofit than to build, because each one changes your data model.
Certification Is a Process, Not a Test
Certification means working through defined scenarios against a test environment and demonstrating correct behaviour for each — eligibility checks, pre-authorization, claim submission, adjudication responses, and the error paths.
Two things this taught me:
The error paths carry the weight. Anyone can make the happy path work. Certification is substantially about what your system does with a rejection, a partial approval, a resubmission, a cancellation. Build and test those with the same seriousness as the success case, because that's where the scrutiny is.
The test environment is not production. Data volumes differ, latency differs, and some behaviours only show up under real traffic. Passing certification means you're allowed to go live. It doesn't mean you're finished.
What I'd Tell My Past Self
Assign an owner. Not a committee. This work spans integration, data modelling, terminology, compliance, and testing, and it needs one person holding the whole picture who can say no to scope.
Don't treat it as an integration project. We initially scoped it like connecting to an API. It's closer to implementing a protocol, with a conformance suite and an external party who decides whether you passed.
Budget for the ten percent. The FHIR part went roughly as estimated. The local profile, the identifier mapping, the terminology, and the error-path behaviour took considerably longer — and that's the part that actually gets you certified.
Certification unlocked a market for us. But the genuinely valuable outcome was that it forced a rigour into our integration layer that we then applied everywhere else.



