Reversibly de-identify clinical text with OpenMed and later restore the original PHI from a saved mapping. Use when the user needs pseudonymization rather than permanent anonymization, wants to mask PHI now and re-link it later under authorization (e.g. recontact, adjudication, GDPR pseudonymization), asks about deidentify keep_mapping, reidentify, or how to store and protect the re-identification mapping. Covers when reversibility is and is not appropriate (pseudonymization vs HIPAA Safe Harbor anonymization). Pairs after extracting-pii-entities and deidentifying-clinical-text.
75
93%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Some workflows need to remove PHI for processing but keep the ability to
restore it later under authorization — adjudication, patient recontact, linking
results back to a record. That is pseudonymization (reversible), not
anonymization (irreversible). OpenMed supports it with
deidentify(..., keep_mapping=True) to capture a mapping, and reidentify to
restore. Everything runs on-device.
Do NOT use reversibility when:
method="remove" and keep
no mapping.pip install "openmed[hf]"import openmed
note = "Patient John Doe (MRN 00481726) seen on 2024-03-02 by Dr. Alice Smith."
# 1) De-identify AND capture the reversal mapping
deid = openmed.deidentify(
note,
method="mask", # or "replace" for realistic surrogates
keep_mapping=True, # <-- required to enable reidentify()
policy="gdpr_pseudonymization",
)
safe_text = deid.deidentified_text # ship/process this
mapping = deid.mapping # SECRET: store separately, encrypted
# 2) Later, under authorization, restore the original
restored = openmed.reidentify(safe_text, mapping)
assert restored == notereidentify(deidentified_text, mapping) performs the inverse substitution. The
mapping is a dict[str, str] of redacted → original text, produced only when
keep_mapping=True.
consistent surrogates for stable pseudonymsFor replacement that maps the same identifier to the same surrogate across a document (and reproducibly across runs with a seed):
import openmed
deid = openmed.deidentify(
"Mr. John Doe called. John Doe's MRN is 00481726.",
method="replace",
consistent=True, # same input value -> same surrogate within the run
seed=42, # reproducible across runs (implies consistent=True)
keep_mapping=True,
)
print(deid.deidentified_text)
restored = openmed.reidentify(deid.deidentified_text, deid.mapping)consistent=True keeps surrogates stable so analytics on the pseudonymized text
stay coherent; seed makes them reproducible. Either way, reversal still requires
the saved mapping.
The mapping is the re-identification key. Treat it like a secret:
import json, os
import openmed
note = "Patient John Doe (MRN 00481726), DOB 1970-01-15."
deid = openmed.deidentify(note, method="mask", keep_mapping=True, seed=7)
doc_id = "doc-7f3a" # opaque id, no PHI
# De-identified text -> general processing store (safe to share downstream)
with open(f"deid/{doc_id}.txt", "w", encoding="utf-8") as fh:
fh.write(deid.deidentified_text)
# Mapping -> SEPARATE, access-controlled, encrypted vault (illustrative path)
os.makedirs("vault", exist_ok=True)
with open(f"vault/{doc_id}.map.json", "w", encoding="utf-8") as fh:
json.dump(deid.mapping, fh) # encrypt this store in productionTo re-identify later, load only the mapping for the authorized doc_id:
import json, openmed
with open("vault/doc-7f3a.map.json", encoding="utf-8") as fh:
mapping = json.load(fh)
with open("deid/doc-7f3a.txt", encoding="utf-8") as fh:
safe_text = fh.read()
original = openmed.reidentify(safe_text, mapping)| Goal | Call | Mapping |
|---|---|---|
| GDPR pseudonymization (reversible) | deidentify(..., keep_mapping=True, policy="gdpr_pseudonymization") | keep, encrypted, separate |
| HIPAA Safe Harbor anonymization | deidentify(..., method="remove", policy="hipaa_safe_harbor") | none |
| Irreversible token linking | deidentify(..., method="hash") | none (one-way) |
method="hash" yields consistent, one-way tokens — good for joining records
without ever restoring the original. That is not reversible and needs no mapping.
extracting-pii-entities: preview the spans first if you want to confirm
what will be masked before committing to a reversible run.deidentifying-clinical-text: that skill covers methods, policies, and
the safety sweep; this one adds the keep_mapping + reidentify round-trip.openmed.analyze_text on deid.deidentified_text;
re-identify only the final, authorized output — never intermediate logs.keep_mapping=True is mandatory for reidentify to work; without it
deid.mapping is None..deidentified_text (and .pii_entities, .mapping), not
.text/.entities.reidentify substitutes those keys
back into the text.method="mask", identical placeholders (e.g. two
[NAME]) cannot be distinguished on reversal. For lossless round-trips use
method="replace" with consistent=True/seed, which produces distinct,
reversible surrogates.gdpr_pseudonymization and
hipaa_safe_harbor (configuring-privacy-policies).80da98c
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.