---
title: "PII Detection vs HIPAA Safe Harbor: Mapping the Gap"
slug: "pii-detection-vs-hipaa-safe-harbor-gap"
author: "ibl.ai Engineering"
date: "2026-10-15 12:00:00"
category: "Premium"
topics: "HIPAA, PII filtering, de-identification, healthcare AI, PHI, AI agents, data retention, compliance"
summary: "ibl.ai's agent PII filter detects thirteen entity types. HIPAA Safe Harbor names eighteen identifier classes. This post maps one list onto the other, names the eight classes no entity-type detector enumerates, and explains what the four-tier Incognito control decides that a BAA does not."
banner: ""
thumbnail: ""
linkedin: |
  Every AI agent on ibl.ai can now detect thirteen categories of personal information and redact, mask or block them before the model sees a message.

  Thirteen is a real number. So is eighteen — the identifier classes HIPAA's Safe Harbor method requires removed, at 45 CFR 164.514(b)(2).

  We mapped one list onto the other, because nobody publishes that mapping and a compliance team deserves to see it.

  Five Safe Harbor classes match a detector category cleanly: names, telephone numbers, email addresses, Social Security numbers, IP addresses.

  Five match only partially. The detector finds a date; Safe Harbor also requires every age over 89 collapsed into "90 or older". It finds a location; Safe Harbor requires ZIP codes removed, permitting the initial three digits only where that three-digit area holds more than 20,000 people.

  Eight classes have no detector category at all: medical record numbers, health plan beneficiary numbers, vehicle and device serial numbers, URLs, biometric identifiers, full-face photographs, and the catch-all — "any other unique identifying number, characteristic, or code".

  That catch-all is why no entity-type detector can deliver Safe Harbor by itself, and why we would rather publish the gap than imply there isn't one. A filter is a technical control. De-identification is a legal determination.

  The other half of the release answers a question a BAA does not: whether the conversation is written to chat history and memory at all. Incognito resolves across four tiers — the organization's gate, the agent's policy, the person's profile default, and the single conversation in front of them.

  All four major model providers will sign a BAA. The difference is not that they cannot; it is that on ibl.ai you own all the code and the data, so the rule is one you can read in your own deployment rather than one you are promised.

  #iblai #AgenticAI #EnterpriseAI #HIPAA #DataPrivacy #HealthcareAI #PII
---

## The Short Answer

**ibl.ai's agent PII filter detects thirteen entity types. Five match HIPAA Safe Harbor's eighteen identifiers cleanly, five partly, eight not at all. A filter is a control, not de-identification. On ibl.ai you own all the code and the data, so you read the rule, not a promise.**

Two controls shipped for every agent on the platform in October 2026: PII filtering, which handles personal data before the model sees a message, and Incognito, which decides whether the conversation is written to chat history and memory at all.

This post does the thing the release notes do not. It maps the detector's thirteen categories onto the eighteen identifier classes in HIPAA's Safe Harbor method, and says plainly which ones are not covered.

## Which HIPAA Safe Harbor identifiers does an entity-type PII detector actually cover?

HIPAA's Safe Harbor de-identification method, at [45 CFR 164.514(b)(2)](https://www.law.cornell.edu/cfr/text/45/164.514) and explained in [HHS's de-identification guidance](https://www.hhs.gov/hipaa/for-professionals/special-topics/de-identification/index.html), names eighteen classes of identifier that must be removed before health information stops being protected health information.

ibl.ai's agent Privacy tab detects thirteen entity types: Person, Email, Phone, SSN, Credit Card, Location, Date / Time, Passport, Driver's License, IP Address, IBAN, Medical License and Bank Number. The two lists are not the same shape.

Here is the mapping, class by class.

<table style="width:100%; border-collapse:collapse; margin:1.5rem 0; font-size:0.95rem;">
  <thead>
    <tr style="background:#f5f5f0; border-bottom:2px solid #2175C5;">
      <th style="text-align:left; padding:0.75rem; color:#5f6368;">Safe Harbor identifier class</th>
      <th style="text-align:left; padding:0.75rem; color:#5f6368;">ibl.ai entity type</th>
      <th style="text-align:left; padding:0.75rem; color:#5f6368;">Coverage</th>
    </tr>
  </thead>
  <tbody>
    <tr style="background:#f0f9ff; border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(A) Names</td>
      <td style="padding:0.75rem;">Person</td>
      <td style="padding:0.75rem;"><strong>Covered</strong></td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(B) Geographic subdivisions smaller than a State</td>
      <td style="padding:0.75rem;">Location</td>
      <td style="padding:0.75rem;">Partial — detects a place, does not apply the three-digit ZIP rule</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(C) All elements of dates except year; all ages over 89</td>
      <td style="padding:0.75rem;">Date / Time</td>
      <td style="padding:0.75rem;">Partial — detects a date, not an age expressed in prose</td>
    </tr>
    <tr style="background:#f0f9ff; border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(D) Telephone numbers</td>
      <td style="padding:0.75rem;">Phone</td>
      <td style="padding:0.75rem;"><strong>Covered</strong></td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(E) Fax numbers</td>
      <td style="padding:0.75rem;">Phone</td>
      <td style="padding:0.75rem;">Partial — caught by number format, no fax-specific type</td>
    </tr>
    <tr style="background:#f0f9ff; border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(F) Electronic mail addresses</td>
      <td style="padding:0.75rem;">Email</td>
      <td style="padding:0.75rem;"><strong>Covered</strong></td>
    </tr>
    <tr style="background:#f0f9ff; border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(G) Social security numbers</td>
      <td style="padding:0.75rem;">SSN</td>
      <td style="padding:0.75rem;"><strong>Covered</strong></td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(H) Medical record numbers</td>
      <td style="padding:0.75rem;">—</td>
      <td style="padding:0.75rem;">Not enumerated</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(I) Health plan beneficiary numbers</td>
      <td style="padding:0.75rem;">—</td>
      <td style="padding:0.75rem;">Not enumerated</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(J) Account numbers</td>
      <td style="padding:0.75rem;">Credit Card, IBAN, Bank Number</td>
      <td style="padding:0.75rem;">Partial — financial accounts only</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(K) Certificate / license numbers</td>
      <td style="padding:0.75rem;">Passport, Driver's License, Medical License</td>
      <td style="padding:0.75rem;">Partial — three document types, not the class</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(L) Vehicle identifiers and serial numbers</td>
      <td style="padding:0.75rem;">—</td>
      <td style="padding:0.75rem;">Not enumerated</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(M) Device identifiers and serial numbers</td>
      <td style="padding:0.75rem;">—</td>
      <td style="padding:0.75rem;">Not enumerated</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(N) Web URLs</td>
      <td style="padding:0.75rem;">—</td>
      <td style="padding:0.75rem;">Not enumerated</td>
    </tr>
    <tr style="background:#f0f9ff; border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(O) Internet Protocol (IP) address numbers</td>
      <td style="padding:0.75rem;">IP Address</td>
      <td style="padding:0.75rem;"><strong>Covered</strong></td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(P) Biometric identifiers</td>
      <td style="padding:0.75rem;">—</td>
      <td style="padding:0.75rem;">Not enumerated</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(Q) Full face photographic images</td>
      <td style="padding:0.75rem;">—</td>
      <td style="padding:0.75rem;">Not enumerated — not a text entity</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;">(R) Any other unique identifying number, characteristic, or code</td>
      <td style="padding:0.75rem;">—</td>
      <td style="padding:0.75rem;">Not enumerable by design</td>
    </tr>
  </tbody>
</table>

The tally: **five** classes match a detector category cleanly, **five** match only partially, and **eight** have no counterpart at all.

That is not a criticism of the detector. It is the honest shape of every entity-type PII filter on the market, and it is the number a compliance reviewer needs before deciding what a filtered agent may be used for.

## Why can't a PII detection filter deliver HIPAA Safe Harbor de-identification on its own?

Two sentences in the regulation decide it, and neither is about detection quality.

The first is the catch-all. Class (R) reads: *"Any other unique identifying number, characteristic, or code, except as permitted by paragraph (c) of this section"* — paragraph (c) being the narrow allowance for a re-identification code.

A detector works from a fixed list of entity types. A rule written as "any other" cannot be reduced to a list, so no finite set of categories satisfies it.

The second is the condition attached to the whole method. Safe Harbor also requires that the covered entity *"does not have actual knowledge that the information could be used alone or in combination with other information to identify an individual."*

That is a statement about the organization's knowledge, not about a filter's output. Software cannot hold or discharge it.

Two of the eighteen classes also need a *transformation*, not a deletion. Ages over 89 must be aggregated into "age 90 or older", and a ZIP code may be kept at three digits only where that three-digit area holds more than 20,000 people, per the Census.

Detecting a date and rewriting "94-year-old" to "90 or older" are different operations. The detector does the first.

So the accurate statement is narrow and still useful: ibl.ai's PII filter is a strong runtime control over what personal data reaches a model. It is not a de-identification certification, and we are not going to imply it is.

## What should a healthcare team do about the eight identifier classes no detector enumerates?

Four things, in the order they actually get decided.

**Decide per agent whether PHI is ever in scope.** For agents that should never receive it, the right action is **Block**, not Redact — the message is rejected and your own wording is shown instead of a generic error. That turns a detection into a policy.

**Narrow the entity list to the agent's job.** An intake agent needs Person, Email, Phone and Date / Time. A finance-adjacent agent needs SSN, Credit Card, IBAN and Bank Number.

A narrow list produces fewer false positives on clinical prose, which is what makes staff stop pasting around the filter.

**Turn on the outbound pass.** The **Also filter AI responses** toggle runs the same detection over the agent's own output. Retrieval-augmented agents can surface an identifier the user never typed, and an inbound-only filter never sees it.

**Keep de-identification in the pipeline, not the chat box.** The eight unenumerated classes are medical record numbers, beneficiary numbers, vehicle identifiers, device serials, URLs, biometrics, photographs and the catch-all.

They belong to a data-engineering step upstream of the agent, or to an Expert Determination.

That split is the same one described in [Why PII Redaction Breaks Enterprise AI — and What Fixes It](/blog/pii-redaction-breaks-enterprise-ai-transformation): transformation before the model ever trains, runtime filtering for everything that arrives afterwards.

The workload tiering in [Shadow AI in Healthcare](/blog/shadow-ai-in-healthcare-patient-safety) is the other half — which workloads may leave the perimeter at all.

## Who decides whether a healthcare AI conversation is written to chat history at all?

Four tiers do, and the resolution order is the interesting part. Incognito keeps a conversation out of chat history and memory; who may turn it on is answered across all four.

<table style="width:100%; border-collapse:collapse; margin:1.5rem 0; font-size:0.95rem;">
  <thead>
    <tr style="background:#f5f5f0; border-bottom:2px solid #2175C5;">
      <th style="text-align:left; padding:0.75rem; color:#5f6368;">Tier</th>
      <th style="text-align:left; padding:0.75rem; color:#5f6368;">Who sets it</th>
      <th style="text-align:left; padding:0.75rem; color:#5f6368;">What it answers</th>
    </tr>
  </thead>
  <tbody>
    <tr style="background:#f0f9ff; border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;"><strong>Agent policy</strong></td>
      <td style="padding:0.75rem;">Agent administrator</td>
      <td style="padding:0.75rem;">Always Incognito, Never Incognito, or Users Decide</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;"><strong>Organization gate</strong></td>
      <td style="padding:0.75rem;">Organization administrator</td>
      <td style="padding:0.75rem;">Whether people may control chat privacy at all</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;"><strong>This conversation</strong></td>
      <td style="padding:0.75rem;">The person chatting</td>
      <td style="padding:0.75rem;">One chat, from the chat header</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;"><strong>User default</strong></td>
      <td style="padding:0.75rem;">Each person</td>
      <td style="padding:0.75rem;">Normal, Anonymized or Incognito, in their profile</td>
    </tr>
  </tbody>
</table>

The resolved answer has three values — **normal**, **anonymized** and **disabled**, where disabled is Incognito. One platform endpoint resolves all four tiers and reports which tier decided and whether the result is locked.

That last field is what lets a chat header show an honest locked state instead of a toggle that silently does nothing. For a regulated deployment, a control that lies about being in effect is worse than no control.

One detail is worth a compliance reviewer's attention. A chat *started* in Incognito is created with no owner record at all, so the guarantee rests on a missing row rather than a flag someone can flip back. Each turn is still metered for usage.

The teaser wording for this release said conversations "leave no trace". The accurate claim is narrower: Incognito decides whether the message text reaches chat history and memory.

It is not a statement about your own infrastructure logs or backups — those are yours, which is the point.

## How is a four-tier retention control different from a privacy setting in a model vendor's console?

Not by being possible where the alternative is impossible. As of October 2026 all four major model providers — Anthropic, OpenAI, Google and Microsoft — will sign a HIPAA business associate agreement for specific tiers, and several offer zero-retention endpoints.

Anyone telling you otherwise is selling something.

The difference is where the control lives and what kind of thing it is.

A retention setting in a vendor console is a contractual guarantee: you accept a term, and an auditor reads the agreement. A retention rule inside a deployment you run is an operational one: your own team reads the configuration, the resolver response and the absent history row.

Both can be true at once. Only the second is inspectable by the organization that carries the liability, and that is the whole of the claim we make. The limits of the contractual version are the subject of [HIPAA-Compliant AI: Why a BAA Is Not Enough](/blog/hipaa-compliant-ai-baa-not-enough).

ibl.ai is family-owned and operated from New York, NY — a U.S.-headquartered, domestically-owned long-term partner, not a vendor that sells licenses and moves on.

## What does a compliance review of agent-level privacy controls look like on ibl.ai?

It looks like reading configuration, which is the only reason this post can quote exact numbers.

![The PII Filtering sub-tab of an agent's Privacy tab in ibl.ai, showing the capability toggle switched on, a When PII is detected dropdown set to Redact, thirteen entity-type chips including Person, Email, Phone, SSN, Credit Card, Passport and Medical License, and an Also filter AI responses toggle switched off.](/images/updates/incognito-pii-filtering-agent-privacy-pii-filtering.webp)

With role-based access control enabled, **seven** fields on the Privacy tab carry independent permissions: the PII capability switch, the handling action, the block message, the entity list, the response filter, and the two Incognito flags.

A field someone may read but not write renders disabled rather than hidden, so a reviewer can see a policy they are not allowed to change. Members typically see Incognito read-only and no PII Filtering at all.

Both sub-tabs are documented in [Agent Settings: Privacy](/docs/os/agent-settings/privacy) and shipped in the [`iblai/vibe`](https://github.com/iblai/vibe) toolkit — PII filtering first in **v1.2.0 on May 25, 2026**, the current four-action build and the Incognito tiers in **v2.12.0 on October 12, 2026**.

The release detail is in [Incognito and PII Filtering for Every Agent](/updates/incognito-pii-filtering-agent-privacy).

[Agentic OS](/product/agentic-os) runs both controls today. 1.6M+ users across 400+ organizations run the platform this way, including NVIDIA, MIT, and Syracuse University.

## Want to map these controls against your own HIPAA posture?

We will walk your privacy and security teams through the thirteen entity types, the eight Safe Harbor classes they do not cover, and the four tiers that decide retention — on infrastructure you own.

[Book a 30-minute demo](https://cal.com/iblai/30min) or [talk to the ibl.ai team](/contact).

## Why does owning the AI stack matter?

**ibl.ai is the agentic AI platform where you own all the code and the data. You self-host the entire stack inside your own perimeter, run it model-agnostic across any LLM and switch anytime, and pay by usage with no per-seat pricing — so you can deploy anywhere: your cloud, on-premise, GovCloud, or fully air-gapped.**

- **You own all the code and the data.** Full source code under a perpetual license, running on your infrastructure. Not API access to someone else's platform — the stack itself is yours.
- **Model-agnostic.** Run any LLM — Claude, GPT, Gemini, Llama, Command, or your own fine-tune — and switch providers without rewriting the platform.
- **No per-seat pricing.** Usage-based billing against a budget cap you set. Cost tracks what your organization actually uses, not how many people you employ.
- **Deploy anywhere.** Your cloud, your VPC, on-premise, GovCloud, or a fully air-gapped network with no outbound connectivity.

1.6M+ users across 400+ organizations run the platform this way, including NVIDIA, MIT, and Syracuse University.

ibl.ai is family-owned and operated from New York, NY — a U.S.-headquartered, domestically-owned long-term partner, not a vendor that sells licenses and moves on.
