Skip to main content

De-Identification1.0.0

De-identification of Protected Health Information (PHI)

In this tutorial, we will use the IMO Health De-Identification APIs to identify and obfuscate Protected Health Information (PHI) in clinical text. The flow is two steps: first identify PHI entities in the text, then obfuscate those entities according to a configurable policy.

Setup

Start with a record of unstructured free text.

NLP Example Text:

Chief Complaint: "chest pain" HPI: John Doe is a 76 yo man with h/o HTN, DM, and sleep apnea who presented to the ED complaining of chest pain. He states that the pain began the day before and consisted of a sharp pain that lasted around 30 seconds, followed by a dull pain that would last around 2 minutes. The pain was reported as left chest pain. The onset of pain came while the patient was walking in his home. He did not sit and rest during the pain but continued to do household chores. Later on in the afternoon he went to the gym where he walked 1 mile on the treadmill, rode the bike for 5 minutes, and swam in the pool. He did not have any reoccurrences of chest pain while at the gym or later in the evening. The following morning (of his presentation to the ED) he noticed the pain as he was getting out of bed. Once again it was a dull pain, preceded by a short interval of a sharp pain. The patient did experience some tingling in his right arm after the pain ceased. He continued to have several episodes of the pain throughout the morning, so his daughter-in-law decided to take him to the ED around 12:30pm. The painful episodes did not increase in intensity or severity during this time. At the ED the patient was given nitroglycerin, which he claims helped alleviate the pain somewhat. -- has not experienced any shortness of breath, no nausea, or no diaphoresis during these episodes of pain. He has never had chest pain in the past. He was told "years ago" that he has a right bundle branch block and premature heart beats. Procedure History: he had an colonoscopy in 2002.

API Usage

The De-Identification flow uses two APIs:

  1. PHI Identification — extracts PHI entities from unstructured clinical text. Endpoint: POST https://api.imohealth.com/entityextraction/pipelines/imo-phi-identification
  2. PHI Obfuscation — redacts or replaces the identified PHI in the original text. Endpoint: POST https://api.imohealth.com/transformation/v1/pipelines/imo-phi-obfuscation

An IMO Precision Normalize agreement is required to receive authentication credentials. Once you have an agreement, API keys are provided and can be used to generate access tokens. For details, contact IMO Customer Support.

Authentication

Authentication uses OAuth 2.0 Bearer tokens. You exchange an IMO-provided client ID and secret for an access token, then send that token on every API request.

curl

curl -u "<API_KEY>:<API_SECRET>" \
  --data "grant_type=client_credentials&audience=https://api.imohealth.com" \
  "https://api.imohealth.com/oauth/token"

python

import requests

data = {
    "grant_type":  "client_credentials",
    "client_id":   "<API_KEY>",
    "client_secret": "<API_SECRET>",
    "audience":    "https://api.imohealth.com",
}

response = requests.post(
    url="https://api.imohealth.com/oauth/token",
    json=data,
)
auth_token = response.json()["access_token"]
print(auth_token)
How to call the APIs
Step 1. Identify PHI Entities

The PHI Identification pipeline uses Named Entity Recognition (NER) to find PHI within unstructured text. It returns the original content with a list of identified entities, each tagged with its position and semantic type (one of 27 supported types — see the reference at the end of this page).

curl

curl -X POST \
  -H "Authorization: Bearer <AUTH_TOKEN>" \
  -H "Content-Type: application/json" \
  --data '{"text": "John Doe is a 76 yo man with h/o HTN seen on 7/7/25."}' \
  "https://api.imohealth.com/entityextraction/pipelines/imo-phi-identification"

python

import requests

auth_token = "<AUTH_TOKEN>"
headers = {"Authorization": f"Bearer {auth_token}"}
data = {"text": "John Doe is a 76 yo man with h/o HTN seen on 7/7/25."}

phi_identification_response = requests.post(
    url="https://api.imohealth.com/entityextraction/pipelines/imo-phi-identification",
    json=data,
    headers=headers,
)
print(phi_identification_response.json())

Response

{
    "filename": "",
    "content": "John Doe is a 76 yo man with h/o HTN seen on 7/7/25.",
    "entities": [
        {
            "id": "0_8_Entity_patient_name",
            "text": "John Doe",
            "begin": 0,
            "end": 8,
            "type": "clinical_ai",
            "semantic": "patient_name",
            "section": null,
            "explanation": "Entity found at position 0 to 8 of input text."
        },
        {
            "id": "14_16_Entity_patient_age",
            "text": "76",
            "begin": 14,
            "end": 16,
            "type": "clinical_ai",
            "semantic": "patient_age",
            "section": null,
            "explanation": "Entity found at position 14 to 16 of input text."
        },
        {
            "id": "45_51_Entity_any_other_date",
            "text": "7/7/25",
            "begin": 45,
            "end": 51,
            "type": "clinical_ai",
            "semantic": "any_other_date",
            "section": null,
            "explanation": "Entity found at position 45 to 51 of input text."
        }
    ],
    "text_id": "c9ce93ec-4f6d-4ab1-bc23-cb4602d2ec4a",
    "pipeline": {
        "name": "imo-phi-identification",
        "version": "1.0"
    },
    "preferences": {},
    "metadata": {}
}
Step 2. Obfuscate PHI Entities

The PHI Obfuscation pipeline takes the original text plus the entities returned in Step 1 and applies a configurable transformation policy. The policy is supplied via preferences.transform_config, which maps each entity type to one of six obfuscation modes (see the reference at the end of this page).

If preferences.transform_config is omitted, defaults are applied per entity type. If a mode is supplied that isn't valid for that entity's category (for example truncate_to_year for patient_name), the request returns a 422.

curl

curl -X POST \
  -H "Authorization: Bearer <AUTH_TOKEN>" \
  -H "Content-Type: application/json" \
  --data '{"text": "John Doe is a 76 yo man with h/o HTN seen on 7/7/25.", "entities": [...], "preferences": {"transform_config": {"patient_name": "redact_type", "patient_age": "redact_type", "any_other_date": "redact_type"}}}' \
  "https://api.imohealth.com/transformation/v1/pipelines/imo-phi-obfuscation"

python

import requests

auth_token = "<AUTH_TOKEN>"
headers = {"Authorization": f"Bearer {auth_token}"}

data = {
    "text":     phi_identification_response.json()["content"],
    "entities": phi_identification_response.json()["entities"],
    "preferences": {
        "transform_config": {
            "patient_name":   "redact_type",
            "patient_age":    "redact_type",
            "any_other_date": "redact_type",
        }
    },
}

phi_obfuscation_response = requests.post(
    url="https://api.imohealth.com/transformation/v1/pipelines/imo-phi-obfuscation",
    json=data,
    headers=headers,
)
print(phi_obfuscation_response.json())

Response

{
    "result": "[REDACTED_patient_name] is a [REDACTED_patient_age] yo man with h/o HTN seen on [REDACTED_any_other_date].",
    "preferences": {
        "transform_config": {
            "patient_name":   "redact_type",
            "patient_age":    "redact_type",
            "any_other_date": "redact_type"
        }
    }
}

The preferences block in the response echoes the configuration that was applied (including any defaults), so callers can confirm the transform policy that produced the result.

Reference: Supported semantic types

PHI Identification can emit any of the following 27 semantic values. These are the same values you pass as keys to transform_config when calling the obfuscation endpoint.

Category semantic values
Names patient_name, provider_name, other_name
Dates date_of_birth, date_of_death, any_other_date
Age patient_age
Patient location patient_street, patient_city_state, patient_zip, patient_country, patient_location
Facility facility_name, facility_street, facility_city_state, facility_zip, facility_country
Contact phone_number, email_address, web_url
Identifiers account_number, medical_record_number, provider_license_number, id_number, location_id
Devices/Network device_identifier, IP_address

Note: IP_address is the only value emitted with mixed casing — the rest are lowercase with underscores. Consumers chaining identification into obfuscation should match on the exact string.

Reference: transform_config modes

Each entity type belongs to a category, and only certain modes are valid for each category. Passing a mode outside the valid set for that entity returns a 422.

Category Entity types Allowed modes
Date date_of_birth, date_of_death, any_other_date ignore, redact_general, redact_type, truncate_to_year
Age patient_age ignore, redact_general, redact_type, cap_age_at_89
General All other entity types (names, locations, contact, identifiers, devices/network) ignore, redact_general, redact_type, replace

What each mode does:

Mode Behavior
ignore Leave the entity unchanged in the output text.
redact_general Replace the entity with the placeholder [REDACTED].
redact_type Replace the entity with [REDACTED_<semantic>], e.g. [REDACTED_patient_name].
replace Replace with a synthetic value of the same kind (e.g. another plausible patient name).
truncate_to_year For dates: drop month/day, keep only the year (e.g. 03/15/19851985).
cap_age_at_89 For ages: leave under 90 as-is, replace 90+ with 90+ per HIPAA Safe Harbor.

Summary

You called the PHI Identification API to extract PHI entities from clinical text, then passed those entities (along with the original text and a transform policy) to the PHI Obfuscation API to produce de-identified output. The transform policy is configurable per entity type, with six modes available across three category-specific rule sets.

Notices

©️ 2026 Intelligent Medical Objects, Inc. All Rights Reserved.

CPT®️ copyright 2024 American Medical Association. All rights reserved.

SNOMED®️ and SNOMED CT®️ are registered trademarks of IHTSDO.

LOINC®️ is a registered United States trademark of Regenstrief Institute, Inc.

RxNorm is publicly available data courtesy of the U.S. National Library of Medicine (NLM), National Institutes of Health, Department of Health and Human Services.