The advisor confirmed the plan and flagged one catch I had already spotted: the facility name pair in the substring example ("Center for Advanced Medicine" / "The Center for Advanced Medicine") are real-sounding clinic names that need to be replaced with an obviously illustrative pair — while preserving the substring relationship the technical point depends on.
Here is the cleaned post:
TL;DR: When one webhook fans out to multiple competing recipients, each one needs its own redacted view of the data. Paragraph-level stripping works — but the naive version has a subtle substring-collision leak. Here's exactly what I built and where the edge case hides.
The Setup
A patient-referral platform I'm building has a feature called "Contact All Centers."
A patient opens the chat, describes their situation, and clicks contact all. The AI gathers their info, creates a consultation record, then fires one webhook per enrolled facility — routed through Make.com (my automation layer) and out as a formatted Gmail from my business address.
229 out of 859 facilities enrolled so far.
Each facility gets the patient's name, phone, health issue, and the full chat transcript. The whole point is to hand off warm leads the moment they happen, no copy-paste required.
Clean. Automated. Fast.
Except I had a problem.
The Wall
When a patient contacts all centers, the AI sometimes names specific facilities in the conversation — recommending one, comparing options, explaining what each specializes in.
If I fire that raw transcript to every facility, Facility A can see the AI mentioned Facility B, C, and D.
Cancer centers are competitors. No center wants to know a patient is shopping around. And no patient expects their "Contact All Centers" click to broadcast their comparison shopping to every recipient.
So each facility needed its own redacted copy of the transcript.
What I Built
The function is redactOtherFacilities() in src/lib/consultation-webhook.ts.
The logic is paragraph-level:
- Split each assistant message on
\n\ninto discrete paragraphs - For each paragraph — if it mentions only sibling facilities (competitors) and the recipient's own name doesn't appear, drop it
- Keep everything else
Patient messages are untouched. Patients almost never refer to facilities by their full formal name, and aggressive stripping would destroy useful context.
Simple, right? Almost.
The Substring Trap
Here's where I nearly shipped a leak.
Say the recipient is "Lakeside Cancer Center."
A sibling facility is "The Lakeside Cancer Center."
Naive check: does this paragraph mention the recipient? String includes "Lakeside Cancer Center" — yes. Paragraph is kept.
Except the paragraph is about the sibling. The recipient name is just a substring hiding inside the sibling's longer name. The check returns a false positive, you keep the paragraph, and you've leaked exactly the competitor mention you were trying to block.
The fix is one extra step: strip sibling-name occurrences from the paragraph first, then check if the recipient name still survives.
After you strip "The Lakeside Cancer Center" from the text, "Lakeside Cancer Center" is gone too — because it was only there as part of the sibling string. Recipient name doesn't survive the strip → paragraph is correctly dropped.
No leak.
Why This Matters to Me
This wasn't a hypothetical edge case — it was a real collision waiting to happen in a list of cancer centers with overlapping names.
The general lesson: when you fan out one payload to competing recipients, each recipient's view is a first-class data product, not an afterthought. The redaction logic needs to be as careful as the payload itself.
And "does this mention the recipient?" is a deceptively weak check. Strip first. Then ask.
P.S. — Only assistant messages get redacted, not patient messages. Precision matters. Redaction has a cost in context; aim it only where the leak actually lives.