<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Trevor</title>
    <description>The latest articles on DEV Community by Trevor (@trevorthecreativeguy).</description>
    <link>https://dev.to/trevorthecreativeguy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3989643%2F304acbd3-d17c-4106-9b8e-8e82352fbe3a.png</url>
      <title>DEV Community: Trevor</title>
      <link>https://dev.to/trevorthecreativeguy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/trevorthecreativeguy"/>
    <language>en</language>
    <item>
      <title>Redaction is Not Enough: When an LLM can still Infer the PII You Stripped Out</title>
      <dc:creator>Trevor</dc:creator>
      <pubDate>Tue, 30 Jun 2026 16:52:20 +0000</pubDate>
      <link>https://dev.to/trevorthecreativeguy/redaction-is-not-enough-when-an-llm-can-still-infer-the-pii-you-stripped-out-1b9d</link>
      <guid>https://dev.to/trevorthecreativeguy/redaction-is-not-enough-when-an-llm-can-still-infer-the-pii-you-stripped-out-1b9d</guid>
      <description>&lt;p&gt;A reader left a sharp question on my last post about redacting PII before sending prompts to an LLM. Paraphrased: &lt;strong&gt;If you redact "John from ACME" to "[NAME] from [COMPANY]", can the model still infer who it is?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. And it is worth pulling that apart, because it points to a real limit that a lot of teams miss. Redaction handles the identifiers it can see. It does not stop a model from reasoning about everything you left in.&lt;/p&gt;

&lt;p&gt;Two problems people treat as one&lt;br&gt;
When people say "keep PII out of my prompts," they are usually worried about two different things:&lt;/p&gt;

&lt;p&gt;Direct personal data sitting verbatim in a third party's logs. The literal email, phone number, SSN, or card number ends up in OpenAI's or Anthropic's systems, outside your control. This is the concrete GDPR and HIPAA exposure for most teams.&lt;br&gt;
The model inferring or re-identifying someone from what is left, even after the obvious identifiers are gone.&lt;br&gt;
Redaction solves the first problem completely. Swap the direct identifiers for placeholders, and they never appear verbatim in logs you do not own. For a large share of real compliance work, that is the whole job, and it is cheap to do.&lt;/p&gt;

&lt;p&gt;The second problem is different and harder. It is not really a redaction problem at all.&lt;/p&gt;

&lt;p&gt;How inference leakage actually happens&lt;br&gt;
Three common ways the data you stripped comes back:&lt;/p&gt;

&lt;p&gt;Quasi-identifiers. No single field identifies a person, but a combination does. Latanya Sweeney's well-known finding is that ZIP code, date of birth, and sex uniquely identify roughly 87 percent of Americans. Strip the name and email, leave "a 41-year-old cardiologist in Boise who joined in March," and you may have described exactly one person.&lt;/p&gt;

&lt;p&gt;Context that gives away the blank. Placeholders do not help if the surrounding text answers the question for the model. "[NAME] runs the electric vehicle company in Austin" leaves very little to guess. The model fills in the blank because you told it how.&lt;/p&gt;

&lt;p&gt;Rare details. An unusual job title, a specific dollar amount, a distinctive phrasing lifted from a support ticket. The more unique the detail, the more it points at one person.&lt;/p&gt;

&lt;p&gt;What to actually do about it&lt;br&gt;
This is an architecture decision, not a setting you flip. A few practical layers:&lt;/p&gt;

&lt;p&gt;Minimize the context, not just the identifiers. Send the model only what the task needs. If you are summarizing a complaint, it probably does not need the customer's age, employer, and city. Data minimization is the highest-leverage move and the one most people skip.&lt;/p&gt;

&lt;p&gt;Generalize the quasi-identifiers you do need. When the task allows, bucket the specifics. Exact age becomes an age range. A city becomes a region. "Cardiologist in Boise" becomes "a physician." You keep what the model needs to do the work and drop what re-identifies.&lt;/p&gt;

&lt;p&gt;Match the protection to the threat. If your only goal is keeping direct PII out of logs you do not control, redaction is enough and you can stop there. If the content itself is sensitive enough that inference matters, that is a deliberate design choice, and you handle it above the redaction layer.&lt;/p&gt;

&lt;p&gt;For the strictest cases, keep it off shared models. When inference is genuinely unacceptable, the clean answer is a self-hosted or local model. If the text never reaches a third party, there is nothing to infer in someone else's system.&lt;/p&gt;

&lt;p&gt;Restore on your side and check the output. The model can reintroduce details or guess at them. Doing the restore step yourself, and reviewing what comes back before it reaches a user keeps that contained.&lt;/p&gt;

&lt;p&gt;Where redaction fits&lt;br&gt;
Redaction is the necessary first layer. It guarantees the explicit email, phone, SSN, card, and name never sit verbatim in logs you do not control, which is the concrete compliance win and the easiest to ship. Inference protection is a second layer you reach for based on how sensitive the content is. Most teams need the first. Regulated, high-sensitivity workloads need both.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;To be clear about my own tool: the API I built does the first layer. It detects and tokenizes direct identifiers fast and in-process, and restores them in the reply. It does not claim to stop inference, because that lives in how you design the prompt and what context you choose to send. Anyone who tells you a redaction step alone makes an LLM "private" is selling you something.&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rapidapi.com/like-father-like-son-investments-like-father-like-son-investments-default/api/llm-privacy-shield" rel="noopener noreferrer"&gt;LLM Privacy Shield on RapidAPI&lt;/a&gt; if you want the first layer handled. The second layer is on you, and now you know what it is.&lt;/p&gt;

&lt;p&gt;Wrapping up&lt;br&gt;
Redaction is necessary, not sufficient. Strip the direct identifiers so they stay out of logs you do not own. Minimize the context so there is less to infer from. Generalize the quasi-identifiers you have to keep. And for the most sensitive work, keep it off shared models entirely. Redaction is the floor, not the ceiling.&lt;/p&gt;

&lt;p&gt;If you missed the first post on the redact then restore pattern, it is here: &lt;a href="https://dev.to/trevorthecreativeguy/how-to-redact-pii-before-sending-prompts-to-openai-claude-or-gemini-25gg"&gt;How to redact PII before sending prompts to OpenAI, Claude, or Gemini&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>privacy</category>
      <category>security</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Redaction is Not Enough: When an LLM can still Infer the PII You Stripped Out</title>
      <dc:creator>Trevor</dc:creator>
      <pubDate>Tue, 30 Jun 2026 16:52:20 +0000</pubDate>
      <link>https://dev.to/trevorthecreativeguy/redaction-is-not-enough-when-an-llm-can-still-infer-the-pii-you-stripped-out-k95</link>
      <guid>https://dev.to/trevorthecreativeguy/redaction-is-not-enough-when-an-llm-can-still-infer-the-pii-you-stripped-out-k95</guid>
      <description>&lt;p&gt;A reader left a sharp question on my last post about redacting PII before sending prompts to an LLM. Paraphrased: &lt;strong&gt;If you redact "John from ACME" to "[NAME] from [COMPANY]", can the model still infer who it is?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. And it is worth pulling that apart, because it points to a real limit that a lot of teams miss. Redaction handles the identifiers it can see. It does not stop a model from reasoning about everything you left in.&lt;/p&gt;

&lt;p&gt;Two problems people treat as one&lt;br&gt;
When people say "keep PII out of my prompts," they are usually worried about two different things:&lt;/p&gt;

&lt;p&gt;Direct personal data sitting verbatim in a third party's logs. The literal email, phone number, SSN, or card number ends up in OpenAI's or Anthropic's systems, outside your control. This is the concrete GDPR and HIPAA exposure for most teams.&lt;br&gt;
The model inferring or re-identifying someone from what is left, even after the obvious identifiers are gone.&lt;br&gt;
Redaction solves the first problem completely. Swap the direct identifiers for placeholders, and they never appear verbatim in logs you do not own. For a large share of real compliance work, that is the whole job, and it is cheap to do.&lt;/p&gt;

&lt;p&gt;The second problem is different and harder. It is not really a redaction problem at all.&lt;/p&gt;

&lt;p&gt;How inference leakage actually happens&lt;br&gt;
Three common ways the data you stripped comes back:&lt;/p&gt;

&lt;p&gt;Quasi-identifiers. No single field identifies a person, but a combination does. Latanya Sweeney's well-known finding is that ZIP code, date of birth, and sex uniquely identify roughly 87 percent of Americans. Strip the name and email, leave "a 41-year-old cardiologist in Boise who joined in March," and you may have described exactly one person.&lt;/p&gt;

&lt;p&gt;Context that gives away the blank. Placeholders do not help if the surrounding text answers the question for the model. "[NAME] runs the electric vehicle company in Austin" leaves very little to guess. The model fills in the blank because you told it how.&lt;/p&gt;

&lt;p&gt;Rare details. An unusual job title, a specific dollar amount, a distinctive phrasing lifted from a support ticket. The more unique the detail, the more it points at one person.&lt;/p&gt;

&lt;p&gt;What to actually do about it&lt;br&gt;
This is an architecture decision, not a setting you flip. A few practical layers:&lt;/p&gt;

&lt;p&gt;Minimize the context, not just the identifiers. Send the model only what the task needs. If you are summarizing a complaint, it probably does not need the customer's age, employer, and city. Data minimization is the highest-leverage move and the one most people skip.&lt;/p&gt;

&lt;p&gt;Generalize the quasi-identifiers you do need. When the task allows, bucket the specifics. Exact age becomes an age range. A city becomes a region. "Cardiologist in Boise" becomes "a physician." You keep what the model needs to do the work and drop what re-identifies.&lt;/p&gt;

&lt;p&gt;Match the protection to the threat. If your only goal is keeping direct PII out of logs you do not control, redaction is enough and you can stop there. If the content itself is sensitive enough that inference matters, that is a deliberate design choice, and you handle it above the redaction layer.&lt;/p&gt;

&lt;p&gt;For the strictest cases, keep it off shared models. When inference is genuinely unacceptable, the clean answer is a self-hosted or local model. If the text never reaches a third party, there is nothing to infer in someone else's system.&lt;/p&gt;

&lt;p&gt;Restore on your side and check the output. The model can reintroduce details or guess at them. Doing the restore step yourself, and reviewing what comes back before it reaches a user keeps that contained.&lt;/p&gt;

&lt;p&gt;Where redaction fits&lt;br&gt;
Redaction is the necessary first layer. It guarantees the explicit email, phone, SSN, card, and name never sit verbatim in logs you do not control, which is the concrete compliance win and the easiest to ship. Inference protection is a second layer you reach for based on how sensitive the content is. Most teams need the first. Regulated, high-sensitivity workloads need both.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;To be clear about my own tool: the API I built does the first layer. It detects and tokenizes direct identifiers fast and in-process, and restores them in the reply. It does not claim to stop inference, because that lives in how you design the prompt and what context you choose to send. Anyone who tells you a redaction step alone makes an LLM "private" is selling you something.&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rapidapi.com/like-father-like-son-investments-like-father-like-son-investments-default/api/llm-privacy-shield" rel="noopener noreferrer"&gt;LLM Privacy Shield on RapidAPI&lt;/a&gt; if you want the first layer handled. The second layer is on you, and now you know what it is.&lt;/p&gt;

&lt;p&gt;Wrapping up&lt;br&gt;
Redaction is necessary, not sufficient. Strip the direct identifiers so they stay out of logs you do not own. Minimize the context so there is less to infer from. Generalize the quasi-identifiers you have to keep. And for the most sensitive work, keep it off shared models entirely. Redaction is the floor, not the ceiling.&lt;/p&gt;

&lt;p&gt;If you missed the first post on the redact then restore pattern, it is here: &lt;a href="https://dev.to/trevorthecreativeguy/how-to-redact-pii-before-sending-prompts-to-openai-claude-or-gemini-25gg"&gt;How to redact PII before sending prompts to OpenAI, Claude, or Gemini&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>privacy</category>
      <category>security</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>How to Redact PII before sending prompts to OpenAI, Claude, or Gemini</title>
      <dc:creator>Trevor</dc:creator>
      <pubDate>Wed, 17 Jun 2026 19:21:56 +0000</pubDate>
      <link>https://dev.to/trevorthecreativeguy/how-to-redact-pii-before-sending-prompts-to-openai-claude-or-gemini-25gg</link>
      <guid>https://dev.to/trevorthecreativeguy/how-to-redact-pii-before-sending-prompts-to-openai-claude-or-gemini-25gg</guid>
      <description>&lt;p&gt;If you send user text to an LLM, you are probably sending personal data with it without meaning to. A support message, a chat transcript, a pasted form. They carry names, emails, phone numbers, and sometimes card numbers, and all of it ends up in your prompt. Once that prompt leaves your server, the personal data is sitting in someone else's logs, which is a real problem under GDPR and HIPAA.&lt;/p&gt;

&lt;p&gt;The fix is simpler than most people expect, and it does not mean giving up the model. You redact the personal data before the prompt goes out, send the safe version to the model, then put the real values back into the answer. This post walks through that pattern with working code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern in three steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Redact. Take your raw text and swap each piece of personal data for a placeholder like &lt;code&gt;[EMAIL_1]&lt;/code&gt;. Keep a small map on your side that records which placeholder stands for which real value.&lt;/li&gt;
&lt;li&gt;Call the model with the redacted text. The model only ever sees placeholders, so the real data never lands in logs you do not control.&lt;/li&gt;
&lt;li&gt;Restore. Take the model's reply and your map, and put the real values back so the final output is useful to your user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is the whole idea. The personal data takes a round trip through placeholders and never leaves your stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real example
&lt;/h2&gt;

&lt;p&gt;Say you run a support tool that uses GPT to summarize customer emails. A customer writes in with their email and phone number. You want the summary, but you do not want to ship their contact details to OpenAI.&lt;/p&gt;

&lt;p&gt;Here is the flow end to end. I am using a small API I built for the redact and restore steps, but the shape is the same no matter how you handle those two calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Real example: summarize a customer support email with GPT,&lt;/span&gt;
&lt;span class="c1"&gt;// without sending any personal data to OpenAI.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SHIELD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://llm-privacy-shield.p.rapidapi.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shieldHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-rapidapi-host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;llm-privacy-shield.p.rapidapi.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-rapidapi-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR-RAPIDAPI-KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;summarizeSafely&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerEmail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Redact PII before the text leaves your server.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;redacted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token_map&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SHIELD&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/redact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;shieldHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerEmail&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="c1"&gt;// customerEmail:&lt;/span&gt;
  &lt;span class="c1"&gt;//   "Hi, my confirmation went to john@acme.com but nothing arrived.&lt;/span&gt;
  &lt;span class="c1"&gt;//    Please call me back on 415-555-0132."&lt;/span&gt;
  &lt;span class="c1"&gt;// redacted:&lt;/span&gt;
  &lt;span class="c1"&gt;//   "Hi, my confirmation went to [EMAIL_1] but nothing arrived.&lt;/span&gt;
  &lt;span class="c1"&gt;//    Please call me back on [PHONE_1]."&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Send only the safe version to your model. OpenAI is shown here.&lt;/span&gt;
  &lt;span class="c1"&gt;//    Anthropic and Gemini work the same way, just swap this call.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Summarize this support email in one sentence.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;redacted&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Put the real contact details back for your support agent.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;restored&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SHIELD&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/restore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;shieldHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token_map&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;restored&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The customer's email and phone number never reach OpenAI. The model summarizes placeholder text, and you swap the real values back in at the end for your support agent to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two calls, side by side
&lt;/h2&gt;

&lt;p&gt;If you just want to see the redact and restore calls on their own:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The two calls, at a glance&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HOST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://llm-privacy-shield.p.rapidapi.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-rapidapi-host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;llm-privacy-shield.p.rapidapi.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-rapidapi-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR-RAPIDAPI-KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;// from your RapidAPI dashboard&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HOST&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/redact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email john@acme.com about invoice 4521&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// r.redacted   =&amp;gt; "Email [EMAIL_1] about invoice 4521"&lt;/span&gt;
&lt;span class="c1"&gt;// r.token_map  =&amp;gt; { "[EMAIL_1]": "john@acme.com" }&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;back&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HOST&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/restore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;modelReply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;token_map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token_map&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="c1"&gt;// back.restored =&amp;gt; the model reply with the real values put back&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The redact call hands you back the safe text and the &lt;code&gt;token_map&lt;/code&gt;. You hold the map. The restore call takes that map and the model's reply and rebuilds the real answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three modes
&lt;/h2&gt;

&lt;p&gt;Redaction is not one size fits all. There are three modes worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tokenize&lt;/strong&gt; (reversible). Replaces each value with a placeholder you can reverse later. Use this when you need the real values back in the answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mask&lt;/strong&gt;. Replaces a value with a generic label like &lt;code&gt;&amp;lt;EMAIL&amp;gt;&lt;/code&gt;. Good when you never need it back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove&lt;/strong&gt;. Deletes the value entirely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tokenize is the default in the examples above, because the restore step depends on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you have to use a hosted API?
&lt;/h2&gt;

&lt;p&gt;No. If you would rather self-host, Microsoft Presidio is a solid open source option for detecting and anonymizing PII. The redact then restore pattern is the part that matters, and it works the same whether you run it yourself or call a service.&lt;/p&gt;

&lt;p&gt;I built a hosted version because I wanted the redact and the restore in one place, running in-process so the protected data never goes to a third party, with a response time under a millisecond. It detects emails, phone numbers, SSNs, credit cards, IP addresses, and API keys, and there is a free tier if you want to try it without committing to anything paid:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rapidapi.com/like-father-like-son-investments-like-father-like-son-investments-default/api/llm-privacy-shield" rel="noopener noreferrer"&gt;LLM Privacy Shield on RapidAPI&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;If your app sends user text to an LLM, run it through a redact step first. Keep the map, send placeholders to the model, restore at the end. Your users get the same useful output, and the personal data stays where it belongs.&lt;/p&gt;

&lt;p&gt;What new app or existing pipeline can you heighten privacy using this API?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>privacy</category>
      <category>security</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
