Audit Entity Resolution
Read the resolution report to understand how entities from different sources were merged, what conflicts were found, and which resolver made each decision.
Audit Entity Resolution
The resolution report records every merge decision made by the 4-layer pipeline for a patient. Use it to understand why two records were (or were not) merged, what conflicts were found between sources, and how each conflict was resolved.
Fetch the report
curl "https://<workspace-id>.w.clinia.cloud/v1/patients/{patientId}/resolution?page=0&perPage=100" \
-H "Authorization: Bearer <access-token>"The facts list is paginated: page is zero-indexed (default 0) and perPage defaults to 100 (maximum 1000). Summary counts always cover the patient's full graph, not just the returned page.
Report structure
{
"summary": {
"totalFacts": 18,
"totalEvents": 11,
"mergedFacts": 3,
"warningCount": 1,
"loadMs": 840
},
"meta": { "total": 18, "page": 0, "perPage": 100 },
"facts": [ ... ],
"warnings": [ ... ]
}summary.totalFacts: total number of facts persisted for the patient after resolution.summary.totalEvents: total number of clinical events in the patient graph.summary.mergedFacts: number of facts carrying two or more contributing sources. If this is 0 after ingesting overlapping sources, every record from every source was treated as unique.summary.warningCount: number of warnings generated during the resolution process (see thewarningssection for details).summary.loadMs: time taken (in milliseconds) to generate the resolutionmeta: pagination metadata for thefactsarray (totalfacts across all pages, the returnedpage, and theperPagesize).facts: one entry per persisted fact on the requested page — every fact type, single- or multi-source — with its full provenance trace. Ordered by fact type, then display.warnings: facts or events that could not be processed cleanly.
Reading a fact
{
"id": "condition:snomed:44054006",
"type": "condition",
"display": "Type 2 Diabetes Mellitus",
"provenance": {
"resolvedBy": "deterministic-code",
"confidence": 0.9,
"reasoning": "Both records share SNOMED code 44054006.",
"sources": [
{ "origin": "Condition/abc", "type": "fhir", "reliability": 0.85 },
{ "origin": "observation/456", "type": "cda", "reliability": 0.8 }
],
"conflicts": []
}
}A fact merged from several sources lists every contributing record under provenance.sources. A fact seen in only one source has a single entry there.
resolvedBy values
| Value | Layer | What happened |
|---|---|---|
source-ref-equality | 0 | Same source record re-ingested — identical origin reference |
deterministic-code | 1 | Records share a standard code (SNOMED, RxNorm, ICD-10, LOINC) |
nlp-normalization | 2 | Same concept with different display text or minor variation |
embedding-similarity | 3 | Semantically similar with no shared code |
llm-adjudication | 4 | Ambiguous case escalated to the LLM for clinical judgment |
novel-entity | none | Record was not merged with any other. Treated as a new fact |
Layer 4 fires for at most ~15 pairs per patient. Higher layers are more expensive; lower layer numbers are cheaper and faster.
Reading conflicts
When sources disagree on an attribute value, both values are recorded along with the resolution strategy:
{
"conflicts": [
{
"field": "status",
"values": [
{ "value": "active", "source": "Condition/abc" },
{ "value": "resolved", "source": "observation/456" }
],
"winner": "active",
"strategy": "temporal-precedence",
"explanation": "Condition/abc is more recent (2023-09-07 vs 2021-04-12)."
}
]
}Conflict resolution strategies
| Strategy | When used |
|---|---|
temporal-precedence | Prefer the most recently dated source |
corroboration | Majority of sources agree on one value |
source-hierarchy | Prefer the more reliable source type |
specificity | Prefer the more specific or detailed value |
llm-adjudicated | Complex conflict requiring clinical evaluation |
Reading warnings
{
"warnings": [
{
"source": "cda",
"id": "observation/789",
"message": "No recognised code system. Could not match by code.",
"severity": "low"
}
]
}Warnings are non-fatal. The fact is still ingested, but it could not be matched by the deterministic layer and may have been handled by a more expensive layer or left unmerged.
Debugging a non-merge
If you expected two records to merge but they didn't, you will see two separate facts in facts, each carrying a single source (typically resolvedBy: "novel-entity"), instead of one fact carrying both. Check:
- Do the records share a standard code? If not, deterministic matching (layer 1) cannot fire.
- Are the display names similar? Layer 2 uses NLP normalization, very different display text may not match.
- What was the embedding similarity score? Layer 3 fires only above the escalation threshold.
- Was it escalated to LLM? If
max_llm_calls_per_patientwas reached, remaining ambiguous pairs are left unmerged rather than burning more LLM budget.
See Entity Resolution for a full explanation of the 4-layer cascade.