Chat is where customers volunteer their most sensitive data — card numbers, addresses, health details — one message at a time. Learn how to mask PII live in bots, live chat, and agent-assist tools, keep transcripts compliant, and do it inside a websocket round-trip.
Explore the GuideChat interfaces train users to over-share. The conversational register feels private and ephemeral — like talking, not like filling in a form — so customers type things into a chat widget they would never put in an email: full card numbers "so you can just fix it," a photo of a driver's license, a child's date of birth, medication names, one-time passcodes. Unlike a form, chat has no field validation to refuse the input. Whatever is typed becomes part of the record.
And chat records multiply aggressively. A single customer message typically lands in the chat vendor's database, your own conversation store, the bot platform's logs, the LLM provider's request logs if a model powers the bot, the analytics warehouse that computes deflection rates, the QA tool that samples conversations for coaching, and the CRM timeline synced for the account team. One pasted card number becomes seven stored copies in seven systems, most of them outside any diagram your compliance team has seen.
The regulatory exposure follows the copies. GDPR and CCPA treat every one of those stores as processing that needs a basis, a retention period, and coverage in access and deletion requests. PCI DSS drags any system storing a card number into audit scope. HIPAA turns a symptom description in a patient-support chat into PHI. Meanwhile the conversational-AI wave means bot completions themselves can emit PII pulled from context or connected tools.
The fix is architectural, not behavioral: intercept every message at the moment it crosses a boundary, detect sensitive entities with our PII Detection API, and decide — mask, block, or flag — before the message is displayed, stored, or forwarded. Done at the right points, filtering is invisible to users, protective for agents, and it turns seven risky copies into seven masked ones.
Four distinct actors put PII into chat, and each needs slightly different handling. Filtering only customer messages — the common first implementation — leaves three doors open.
The highest-volume source: identifiers volunteered for convenience ("my email is..."), payment data pasted in frustration, and sensitive context shared to explain a problem. Detection here should be broad — all entity types, moderate threshold — with masking applied before the message reaches storage, the bot's LLM, or an agent's screen, depending on your policy per entity type.
A bot wired to order systems, CRMs, or a retrieval index can surface another customer's data through a lookup bug or a model's confusion — the classic wrong-recipient disclosure. Completions need a post-generation scan before display, with a hard block on anything that looks like credentials or payment data. Our LLM guardrails guide covers the model side in depth.
Agents copy account details into replies to be helpful, and internal notes ("cust SSN ends 2233, verified") accumulate identifiers that outlive the conversation. Agent-assist filtering works best as a nudge: detect on send, show the agent what will be masked, and let overrides require a click plus a reason. Notes should be scanned exactly like customer-visible text — auditors read them too.
Screenshots of statements, photos of ID documents, forwarded emails, and — in voice-enabled channels — speech-to-text transcripts. Run OCR or transcription first, then push the extracted text through the same detection pipeline as typed messages. A conversation store is only as clean as its least-filtered input path.
A chat message's lifecycle offers five places to put a detection call, and mature deployments use most of them with different policies. (1) On receive — the message arrives at your chat backend over websocket or webhook, before any processing. This is the master checkpoint: scan once, attach the entity metadata to the message object, and let every downstream consumer reuse the result instead of re-scanning. (2) Before the bot/LLM — the assembled prompt (message plus history plus retrieved context) gets a policy check so third-party model providers never see raw identifiers. (3) Before display — bot completions and cross-agent views are scanned so the UI never renders unvetted text.
(4) Before storage — the persistence layer writes the masked text plus entity metadata; the raw value, if operationally needed (say, a phone number the agent must actually dial), goes to a separate vault with its own access control and TTL rather than living in the transcript. (5) Before export — the syncs to analytics, CRM, QA tools, and data lakes carry masked text only. Points four and five are where compliance is won: they determine what exists a year later when the access request or the breach happens.
Policy should vary by entity type, not by channel. A workable default: hard-mask payment data (CREDIT_CARD_NUMBER, CVV_NUMBER), government IDs (SSN, PASSPORT_NUMBER), and credentials (PASSWORD, API_KEY) everywhere including the agent view; soft-mask contact identifiers (EMAIL_ADDRESS, PHONE_NUMBER, ADDRESS) in storage and analytics while showing agents the live value during the session; and flag-only for PERSON_NAME in channels where names are operationally necessary.
Chat is latency-sensitive but message-oriented, which makes it friendlier to filtering than it first appears. A detection call runs 100–300 ms — imperceptible against typing cadence. Streaming bot output is the only genuinely hard case, and three patterns cover it.
For human messages, scan the complete message in the websocket handler before fan-out: receive, detect, mask, then broadcast and store. Users perceive nothing — the round-trip fits inside normal network jitter. Use connection pooling / keep-alive to the detection endpoint and a hard client timeout (2–5 s) with an explicit fail-open or fail-closed decision per deployment.
detected_entities metadata to the message objectFor token-streamed bot replies, buffer tokens until a sentence boundary closes, scan the sentence, release it masked, and continue. Perceived latency shifts by one sentence — usually 200–400 ms — while guaranteeing no unscanned text ever renders. This is the recommended pattern for regulated deployments because it is both streaming and strictly safe.
Where every millisecond of streaming matters and risk tolerance allows, render tokens immediately while a trailing scan runs over the accumulating text; on a hit, send a websocket edit event that replaces the rendered message with the masked version. Exposure is real but brief (under a second) and client-side only. Never use this pattern for payment or health channels — brief display is still display.
The conversations your product team mines for intents, your QA team samples for coaching, and your data team joins against orders are the same conversations your regulators treat as personal-data stores. Storing masked text changes their status fundamentally: a transcript reading "my card is [CREDIT_CARD_NUMBER] and I live at [ADDRESS]" trains intent models, computes CSAT drivers, and answers "what did the customer ask" exactly as well as the raw version — while dropping out of PCI scope and shrinking every privacy request that touches it.
The mask_mode choice per destination matters. Use replace for QA and CRM copies, where readable placeholders keep context. Use hash for the analytics warehouse: each unique value maps to a stable token, so "how many conversations did this customer have" and funnel joins keep working without any store holding the actual email address. Use redact for exports leaving your organization — training corpora for vendors, benchmarking datasets — where even placeholder structure is unnecessary.
A single call does the work at write time:
curl -X POST https://piidetectionapi.com/api/moderate.php \
-H "Content-Type: application/json" \
-d '{
"api_key": "YOUR_API_KEY",
"api_type": "pii_detection",
"text": "sure my number is 415-555-0182 and card 4111 1111 1111 1111, im at 88 Pine St Apt 4B, Seattle",
"mask_mode": "hash",
"threshold": 0.5,
"custom_instruction": "Do not mask order numbers of the form ORD-XXXXXX"
}'
Note the custom_instruction: chat text is full of business tokens (order IDs, RMA numbers, tracking codes) that look identifier-like. A one-line natural-language exclusion keeps them intact so downstream automation that parses them never breaks. The response's entities_detected count per conversation also becomes a useful analytics dimension in its own right — which flows, bots, and prompts cause customers to over-share.
Chat transcripts sit at the intersection of most data-protection regimes. This table summarizes what each expects of stored conversations and where real-time detection carries the load.
| Regulation | What it means for transcripts | Detection's role | Key entity types |
|---|---|---|---|
| GDPR | Transcripts are personal data: lawful basis, minimization, retention limits, and erasure/access rights apply to every stored copy | Mask identifiers at write time so stored transcripts are minimized by default; locate a data subject's messages for requests | PERSON_NAME, EMAIL_ADDRESS, PHONE_NUMBER, ADDRESS, IP_ADDRESS |
| CCPA/CPRA | Transcripts count in access, deletion, and correction requests; SPI shared in chat triggers limitation rights | Classify entities per conversation so requests can be fulfilled; strip SPI before analytics use | SSN, GPS_COORDINATES, RELIGION, MEDICAL_DATA |
| PCI DSS | A stored PAN puts the chat platform in scope; a stored CVV is a violation outright | Inline masking keeps card data out of transcripts entirely — the descoping control | CREDIT_CARD_NUMBER, CVV_NUMBER, CREDIT_CARD_EXPIRATION_DATE |
| HIPAA | Health details in patient-facing chat are PHI; storage and vendor access require BAAs and safeguards | Detect and mask the 18 identifiers plus medical context before storage or vendor sync | MEDICAL_RECORD_NUMBER, DIAGNOSIS, PRESCRIPTION, DATE_OF_BIRTH |
| Call-recording / wiretap laws (e.g. CIPA) | Chat session recording and monitoring tools face consent requirements; minimizing captured PII reduces exposure | Filter session-replay and monitoring feeds the same way as transcripts | PERSON_NAME, EMAIL_ADDRESS, payment entities |
The pattern across every row: the regulation is about stored conversations, and the control is applied at the moment of storage. Retention policies then get simpler too — a masked transcript can often be kept for the full business-useful period, because what made retention risky has already been removed.
Two production-shaped examples: a Node.js websocket handler implementing the per-message gate with per-entity policy, and a Python sentence-chunk filter for streaming bot replies. Both call the canonical endpoint documented in the API documentation.
JavaScript (Node) — websocket message gate
const HARD_MASK = new Set(["CREDIT_CARD_NUMBER", "CVV_NUMBER", "SSN",
"PASSWORD", "PASSPORT_NUMBER", "API_KEY"]);
async function detect(text) {
const res = await fetch("https://piidetectionapi.com/api/moderate.php", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
api_key: process.env.PII_API_KEY,
api_type: "pii_detection",
text,
mask_mode: "replace",
threshold: 0.5
}),
signal: AbortSignal.timeout(4000) // bounded wait
});
return res.json();
}
wss.on("connection", (socket) => {
socket.on("message", async (raw) => {
const msg = JSON.parse(raw);
let scan;
try {
scan = await detect(msg.text);
} catch (err) {
// Regulated channel: fail closed rather than leak
socket.send(JSON.stringify({ type: "retry", id: msg.id }));
return;
}
const hardHits = scan.detected_entities
.filter(e => HARD_MASK.has(e.type));
// Store + broadcast the masked text; agents see soft entities live
await store.save({
id: msg.id,
text: scan.anonymized_text, // masked at rest
entities: scan.detected_entities.map(({type, start, end, confidence}) =>
({type, start, end, confidence})), // metadata, no raw values
});
broadcast(room(msg), {
...msg,
text: hardHits.length ? scan.anonymized_text : msg.text,
piiTypes: scan.detected_entities.map(e => e.type)
});
});
});
Python — sentence-chunk filter for a streaming bot reply
import re, requests
API = "https://piidetectionapi.com/api/moderate.php"
SENTENCE_END = re.compile(r"(?<=[.!?])\s+")
def mask(text):
r = requests.post(API, json={
"api_key": "YOUR_API_KEY",
"api_type": "pii_detection",
"text": text,
"mask_mode": "replace",
"threshold": 0.5,
}, timeout=5)
r.raise_for_status()
return r.json()["anonymized_text"]
async def stream_filtered(token_stream, send):
"""Buffer model tokens to sentence boundaries; release masked."""
buf = ""
async for token in token_stream:
buf += token
parts = SENTENCE_END.split(buf)
# Everything except the trailing fragment is complete
for sentence in parts[:-1]:
await send(mask(sentence) + " ")
buf = parts[-1]
if buf.strip():
await send(mask(buf)) # flush the last fragment
In production, run the mask() call for sentence N concurrently with generation of sentence N+1 (an asyncio task queue) and the added latency effectively disappears. For an end-to-end view of guarding the model call itself — prompt side as well as completion side — see the LLM guardrails guide; for volumes and rate limits, the pricing page lists chat-scale tiers.
Operational lessons from chat platforms filtering millions of messages a day.
Silent masking confuses customers ("why does my message say [CREDIT_CARD_NUMBER]?"). Pair the filter with a gentle system message: "For your security, we've hidden the card number you shared — our agents never need the full number." This turns the control into visible trust-building, reduces re-pasting, and trains users toward safer behavior. Bots should follow up by offering the safe path, such as a PCI-compliant payment link.
Chat is dense with order numbers, ticket IDs, SKUs, and confirmation codes that pattern-based systems mangle. Use the custom_instruction parameter to describe your token formats once ("do not mask order numbers like ORD-123456 or tracking numbers starting 1Z"), and keep a per-channel exclusion config under version control. Every false positive an agent sees erodes trust in the filter; a tight exclusion list keeps override rates near zero.
Track detections per thousand messages by entity type, channel, and bot flow; track scan latency percentiles against your websocket budget; track agent overrides and retractions as quality signals. Spikes are information: a jump in card-number detections after a checkout change means the product is pushing users to paste PANs into chat. Review a weekly sample of masked messages for false positives and feed fixes into thresholds and exclusions.
Mask card numbers, identifiers, and health details in real time — in bots, live chat, and agent tools — with one API call per message. Paste a sample chat into the demo and watch it work.
Try the Live Demo View Pricing