What Payer-to-Payer actually requires.
Coverage transition, consent, member match, export, validation, provenance, durable ingestion, and a read path for the clinical data at the end of it. Nine stages, each with a decision that determines whether the implementation is safe or merely present.
CMS-0057-F is the federal rule that requires Medicare Advantage, Medicaid, CHIP, and some exchange plans to offer FHIR APIs for patient access, provider access, payer-to-payer exchange, and prior authorization by January 1, 2027.
"Supports Payer-to-Payer" is not a description of anything
A member changes plans. The new payer needs the history the prior payer holds. Between those two sentences sits a workflow with a member-matching problem, a consent problem, an SSRF problem, a validation problem, an idempotency problem and a durability problem — each of which has to be decided one way or another before any data moves. Here is how Cloud Health Office decided them.
-
Coverage transition, inside one tenant
The member and their prior-payer coverage come from Cloud Health Office's own data. A member belonging to another tenant is never initiated on. Where coverages with the target payer overlap, the exchange refuses rather than guesses which one it is about.
-
Endpoint resolution — from configuration, never from the caller
A caller names a payer id. A resolver maps that id to endpoints held in trusted configuration. No request type in this surface accepts a URL, non-HTTPS entries are rejected, and the HTTP client does not follow redirects. That is the SSRF boundary, and it is a boundary because there is no code path through which a caller-supplied address can become a request target.
-
Consent, evaluated server-side before anything leaves
Cloud Health Office asks its own consent registry whether the member authorized the Payer-to-Payer purpose specifically. This runs before any remote call, so an unauthorized member's identity is never disclosed to the other payer in the first place. A denial is terminal
NotAuthorized, deliberately distinct fromFailed: a member who has not authorized an exchange is not an error to retry. -
Remote
Patient/$member-matchCloud Health Office builds the request and interprets the answer; it does not re-run the peer's matching rules. No match and an ambiguous match are both terminal. Resolving an ambiguity by picking one would mean importing a stranger's record into a member's history.
-
Consent again, immediately before the export
Consent is evaluated at the attempt, not carried forward from step three. A revocation that lands while the match is in flight stops the data request. A retry clears the recorded decision and asks again rather than reusing the answer it liked.
-
Member data export
Issued only once exactly one member has resolved and consent has been re-confirmed as of that moment.
-
Validation of what came back
The Bundle must parse, must carry exactly one
Patient, and that Patient — along with everyPatient/…reference in the package, relative or absolute — must be the member who matched. A package that disagrees with itself about who it is describing is refused. -
Provenance, and an archive of what was actually sent
The package is stamped with a
Provenancenaming the source payer, the exchange and the receipt time, and is archived as received — before Cloud Health Office rewrites any reference — so the archive answers "what did the payer actually send?" rather than "what did we make of it?". -
Durable ingestion, and only then
CompletedThe exchange reaches
Completedonly once the import commits.DataReceivedexists as its own state precisely so that "retrieved but not stored" is a condition the system can be in and report. Retrieval alone never reads as success.
The state machine is explicit: Pending → Matching → Matched → RequestingData →
DataReceived → Ingesting → Completed, with terminal NoMatch,
Ambiguous, NotAuthorized and Failed.
Five choices that decide whether an implementation is safe
Consent is purpose-scoped, and no caller can supply it
Only PayerToPayerExchange authorizes an exchange. An Active consent is not sufficient by
itself: a member holding a Provider Access consent, or a consent carrying no purpose, is denied with
NoConsentForPurpose. The required purpose is a constant in code, not configuration, so a
deployment cannot widen which purpose satisfies Payer-to-Payer.
No request type in this surface has a consent field. Not the inbound
$member-data-export request, not the outbound initiation request. A receiving payer cannot
self-attest that a member consented, and neither can an internal caller. Inbound and outbound ask the same
registry through the same policy, so responding cannot become more permissive than initiating.
The completed exchange records what authorized it — the consent id, the decision reason and the instant it was evaluated — so a finished exchange names the consent record that permitted it and a refused one names why.
Imported data cannot overwrite what Cloud Health Office owns
Imported resources live in their own store, separate from the authoritative member, enrollment, claim and provider stores. Source ownership is structural, not a convention: an imported row physically cannot be read as a Cloud Health Office record, so "did we originate this?" is answered by which store the data lives in rather than by a flag somebody has to remember to set.
- the remote
Patientestablishes source-side identity only — it never replaces the authoritative member record; - a prior payer's
Coveragenever touches the member's current enrollment; - administrative resources keep the peer's resource id as a source id while being filed under Cloud Health Office's own member id.
Replays resolve to the same rows; two payers never merge
The import key is a SHA-256 over
tenant + local member + source payer + resource type + source resource id, joined with a unit
separator so no two tuples can collide by concatenation. Replaying a package resolves to the same keys, so a
member's history does not double. The same source id from a different payer is a different key,
so two payers' records are never silently merged into one. A content hash distinguishes "same again" from
"changed".
A failed ingestion adds nothing and takes nothing away
Rows are versioned by exchange. Staging writes only that exchange's own rows; committing is a single ledger write. Both halves follow without needing a multi-document transaction: a failed ingestion adds nothing visible, because its rows belong to an uncommitted exchange; and it takes nothing away, because it cannot overwrite or hide the version an earlier exchange committed. The member keeps the history they had.
Unsupported types are named, not dropped
A resource type Cloud Health Office cannot serve is counted and named on the exchange, and preserved in the archived package. A clinical resource the payload validator refuses — no source id, oversized, too deeply nested — is counted and named by reason, per resource, so one bad Observation does not cost the member the rest of their history. Cloud Health Office never claims to have ingested a type it cannot serve.
What happens to the clinical resources after they arrive
Receiving a prior payer's Condition, Observation and Procedure is
only half of a Payer-to-Payer implementation. Until there is somewhere to put them and a read path to serve
them, they are validated, counted and archived — and honestly classified as unsupported.
Twelve FHIR R4 clinical types now have durable member-scoped storage and a standards-correct read path
through both Patient Access and Provider Access: AllergyIntolerance, CarePlan,
CareTeam, Condition, Device, DiagnosticReport,
Goal, Immunization, MedicationDispense,
MedicationRequest, Observation and Procedure.
One table, five consumers
The resource inventory is a single source of truth read by the SMART scope layer, the Provider Access authorization filter, the Payer-to-Payer import classification, the CapabilityStatement and the controller's route constraint — with structural tests pinning each of them to it.
That is not tidiness. A clinical type reachable through SMART but missing from the governed set would be readable by any provider holding a scope, attributed or not, consented or not. Taking both from one table removes the possibility rather than relying on a reviewer to notice. The same table decides what is ingested, so a type is ingested exactly when it is served.
The import store was promoted, not copied
The clinical serving store is the Payer-to-Payer import store, resolved through a second interface. There is no projection to fall behind the source, no dual write to reconcile, and no second place a resource could be stale in — and "which store did this come from?" still answers "the imported one". Clinical data therefore stays out of the authoritative member, enrollment, coverage and claim stores by construction.
Native and imported coexist rather than merge
Every served clinical resource today is imported from a payer; no Cloud Health Office component authors native clinical data. The origin axis exists so that when a native writer appears it coexists with imported data instead of overwriting it: two sources for the same clinical fact would be two resources rather than one silently merged one. Until then it is an unexercised seam, and it is described here as one.
What proves the above
Implementation evidence
- Scenarios
- P2P-01 inbound respond · P2P-02 outbound initiate and ingest · P2P-03 consent enforcement · P2P-04
$member-matchand concurrent coverage · PAT-02 USCDI clinical · CONSENT-01 one registry - Role
- Cloud Health Office as the new payer (outbound) and as the prior payer (inbound) — the same two operations in both directions
- Test data
- Synthetic. No exchange described here has moved production member data.
- Architecture docs
- payer-to-payer.md · consent.md · clinical-fhir.md
- Pull requests
- #1148 member data export · #1149
$member-match· #1150 outbound initiation · #1151 durable ingestion · #1152 purpose-scoped consent · #1157 USCDI clinical read path - Result
- The latest published acceptance snapshot below — generated from the suite, not typed into this page
Payer-to-Payer, Patient Access and consent scenarios from the latest published acceptance evidence. The Replace column is Cloud Health Office product capability; each Augment column is integration capability against one external core.
Loading the latest published CMS-0057-F acceptance evidence. It is generated in CI from the acceptance suite at a named source revision and committed to this repository, so the status shown here is the tested status rather than a claim typed into a page.
What is not proven here
- Synthetic data only, and no production exchange with a real payer is claimed or evidenced.
- Consent must be recorded with the Payer-to-Payer purpose. A pre-existing generic consent does not carry it and is not reinterpreted, so a deployment upgrading to this code authorizes nothing until purposes are recorded. That is deliberate, and it is a real migration step.
- Imported member history — EOB, Claim, ClaimResponse, Encounter, DocumentReference — is ingested and archived but is not yet projected into the Patient and Provider Access read paths. Imported clinical resources are.
- FHIR R4 resource support is not US Core certification. Resources are parsed and served as R4; this page does not claim independently validated US Core profile conformance.
- Audit is emitted to the log, not to a durable audit store. The FHIR service has no audit sink.
- Target payers come from configuration. There is no directory of live payer endpoints in this repository, and connecting one is deployment work.
- Transport credentials for a specific payer are not supplied. SMART Backend Services or UDAP client registration and mTLS sit behind a credential provider seam that supplies none by default, so authenticating to a real peer payer is deployment integration.
- No external-core Payer-to-Payer integration exists. Nothing here runs against QNXT, Facets or HealthEdge.
Walk the exchange against your own coverage data.
The operations, the consent model and the ingestion path are in the repository. The interesting conversation is what your prior-payer directory looks like.