Patient Matching When the API Contract Is Ambiguous
A de-identified field pattern for containing patient-match risk when filter and mode parameters interact in undocumented ways.
- Published
- Materially updated
- Reading time
- 5 min read
- Context
- De-identified field experience
Evidence at a glance
Context markers for the implementation. Measurement windows, caveats, and remaining gaps are documented in the study.
- Observed Conflict
- 2 parameters
- a score filter and best-match mode with unclear precedence
- Auto-link Policy
- Fail closed
- reject or review candidates that fail local invariants
- Regression Proof
- Pinned fixtures
- record request shape, response shape, and the expected local decision
Implementation stack
Backend
- REST API
- Go
Database
- PostgreSQL
Monitoring
- Prometheus
Patient matching is a bad place for an implicit contract. A false positive can associate activity with the wrong chart. A false negative creates duplicates and manual reconciliation. An ambiguous response can do either while looking technically successful.
This case study describes a de-identified failure pattern I encountered from both sides of an API boundary. A patient-search endpoint accepted a minimum-score filter and a “return best matches” mode. When both were present, the mode could return candidates below the requested threshold.
The durable fix was not a smarter guess about upstream behavior. It was a local decision boundary with contract fixtures, constrained auto-linking, review for uncertain cases, and instrumentation that could show which rule ran.
TL;DR
- If a filter and a mode can conflict, the API needs an explicit precedence rule.
- Treat a vendor score as a signal, not authorization to link records.
- Apply local invariants before any irreversible action. If required evidence is missing or contradictory, fail closed or send the case to review.
- Pin the ambiguous behavior in contract fixtures so a documentation gap becomes an executable regression check.
- I removed the old 99% accuracy, two-million-calls-per-month, 75% debug-time reduction, 15 ms latency, and storage-cost claims. There is no shareable measurement set here that can reproduce them.
Context and evidence
This is a de-identified field retrospective, not a public benchmark or a claim about a named client or vendor. The parameter names are retained because they make the contract problem concrete:
minscoreappeared to define a lower acceptance threshold; andreturnbestmatchesselected a broader best-match mode.
The observed behavior was that enabling the mode could return candidates below minscore. I do not have a public vendor contract, source repository, labeled patient corpus, or production telemetry that readers can inspect. For that reason, the outcomes below describe the containment pattern and its evidence, not an accuracy or performance result.
The challenge
The endpoint returned a valid response. The ambiguity was semantic: which parameter owned the final candidate set?
This is dangerous because status codes and schemas cannot detect it. The request is well formed, the response validates, and the wrong assumption survives until someone inspects candidate-level behavior.
Constraints
| Constraint | Design consequence |
|---|---|
| False positives carry higher harm | Auto-linking needs stricter evidence than candidate retrieval. |
| Upstream scoring is opaque | Local code cannot infer what a score means beyond the documented contract. |
| Demographic data can be incomplete or stale | Missing evidence must not be treated as agreement. |
| Protected health information (PHI) is sensitive | Logs and fixtures need minimization, redaction, and controlled access. |
| The upstream contract can drift | Request and response behavior need repeatable contract checks. |
| Some cases remain ambiguous | Human review is a required state, not an implementation failure. |
Decisions and implementation
Separate retrieval from the link decision
The API can retrieve candidates. Local policy decides whether any candidate is safe to auto-link. Those are different responsibilities.
The local decision checks only reviewable invariants: whether the upstream threshold was honored, whether required identifiers agree, whether date of birth matches when policy requires it, and whether contradictory evidence is present. A candidate that fails the boundary is rejected or queued for review.
The following is illustrative pseudocode, not a drop-in clinical matching algorithm:
for candidate in response.candidates:
if candidate.score < policy.minimum_score:
reject(candidate, "upstream threshold not honored")
continue
if required_identifier_conflicts(candidate, request):
reject(candidate, "identifier conflict")
continue
if required_demographic_evidence_missing(candidate, request):
review(candidate, "insufficient evidence")
continue
if local_invariants_pass(candidate, request):
allow_next_workflow_step(candidate)
else:
review(candidate, "ambiguous match")
The “next workflow step” may still require approval. Passing local validation is not a universal guarantee that two records identify the same person.
Turn the ambiguity into contract fixtures
The useful fixture is not only a response body. It records the complete behavior under test:
| Fixture field | Why it matters |
|---|---|
| Endpoint and API version | Prevents a test against the wrong contract. |
| Request parameters | Captures the exact filter and mode combination. |
| Sanitized candidate response | Preserves the observed precedence behavior without copying live PHI. |
| Expected local decision | Tests reject, review, or continue separately from retrieval. |
| Observation date | Makes drift and vendor changes visible. |
Contract tests should include each parameter independently, both together, boundary scores, empty results, and malformed or partial candidates.
Instrument decisions, not patient details
Useful telemetry answers:
- Which request mode was used?
- Did any returned candidate violate the requested threshold?
- Which local rule produced reject, review, or continue?
- Which API and policy version made the decision?
- How long did the dependency and local validation take?
The metric labels should not contain names, identifiers, dates of birth, or raw response fragments. Detailed audit evidence belongs in an access-controlled store with an explicit retention policy.
Make review a first-class outcome
Binary “match/no match” interfaces encourage unsafe certainty. The local workflow uses at least three states:
- Reject when a hard constraint fails.
- Review when evidence is incomplete or contradictory.
- Continue when the configured local invariants pass.
Reviewer decisions can improve policy, but they should not silently train or change production behavior. Any rule change needs versioning and validation against an approved, representative set.
Outcomes and measurement context
The unsafe assumption became an explicit invariant
The integration no longer depended on minscore being honored implicitly. It verified the threshold on every candidate before the local workflow could continue.
The documentation gap became reproducible
A pinned fixture could demonstrate the parameter interaction without repeatedly querying production or relying on memory. That gave API owners and consumers a concrete artifact to discuss.
Ambiguity gained a safe destination
Candidates with missing or conflicting evidence moved to review instead of being forced through an auto-link path. This is a workflow outcome; without a labeled corpus and adjudication protocol, it is not an accuracy percentage.
Operational signals became actionable
Threshold-violation counts, decision reasons, API versions, and latency distributions can reveal drift. The study does not publish values because the underlying telemetry is private and the original figures were not reproducible.
Limits and open work
- Local validation cannot repair a poor upstream candidate generator. It can only constrain downstream action.
- Exact demographic matches are not sufficient proof of identity, and missing data is common.
- Thresholds require validation against a representative, labeled population with clinical and governance oversight.
- A manual queue needs ownership, audit rules, service expectations, and a path for urgent cases.
- De-identification limits how much evidence this public write-up can provide. That is preferable to implying false precision.
Takeaways
- Document precedence, not just parameters. A truth table is often clearer than two independent descriptions.
- Separate candidate retrieval from record linking. The upstream score should not directly authorize an irreversible action.
- Fail closed on missing evidence. “The API returned it” is not a matching invariant.
- Make ambiguity executable. Fixtures turn a support anecdote into a regression test.
- Measure with a labeled set or do not claim accuracy. Directional percentages without a cohort, denominator, and adjudication method are not evidence.
The related patient-matching field note covers the narrative side of the same contract failure.
Working through a similar constraint?
I am happy to compare implementation notes, tradeoffs, and the evidence you would want before shipping.