Home / Guides / mask-pii-before-openai

How to mask PII before calling the OpenAI API (Node.js)

Run deterministic PII masking with @noeticguard/core in your Node backend before openai.chat.completions.create — local tokens, no cloud scrubbing endpoint.

Published 2026-08-10 · NoeticGuard engineering notes

Live masking playground

Paste text — PII and API keys become vault tokens before any LLM sees them.

Try a scenario:
01

Raw Prompt

Untrusted user input

Type sensitive data or pick a scenario. NoeticGuard Core tokenizes PII and high-precision secrets into a vault ([EMAIL_n], [SECRET_n]), routes safe placeholders to the LLM, then re-identifies on the way back to the user.

Inbound Shield · Tokenize
02

Masked Prompt (To LLM)

Vault tokens ([EMAIL_n], [SECRET_n]) + Brand Guard — reversible PII/secrets only

Masked prompt will appear here.

LLM Inference
03

Raw LLM Output

Model echoes vault tokens (never raw PII)

Run the pipeline to simulate the LLM response.

Outbound Shield · Re-identify
04

Final Unmasked Output (To User)

unmaskPii restores vault PII & secrets · competitors stay blocked

Run the pipeline to see PII restored for the end user.

If your Node service calls the OpenAI API with user or customer text, PII can land in provider logs, fine-tunes, and support exports. The fix is not a remote “scrubbing API” that receives the raw prompt — it is an in-process mask before chat.completions.create.

Pattern

  1. Load @noeticguard/core in your backend (see SDK quickstart).
  2. Call maskPii with a per-session vault.
  3. Send only the masked string to OpenAI.
  4. Optionally unmaskPii the model reply for your UI.
  5. Report masked_count via POST /v1/telemetry with an ng_pub_ key.
typescript
import OpenAI from 'openai';
import { maskPii, unmaskPii } from '@noeticguard/core';

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const vault = new Map();

export async function chatWithMaskedPii(userText: string) {
  const { output: masked, matches } = maskPii(userText, {
    vault,
    kinds: ['email', 'creditCard', 'phone', 'nationalId'],
  });

  const completion = await openai.chat.completions.create({
    model: 'gpt-4o-mini',
    messages: [{ role: 'user', content: masked }],
  });

  const raw = completion.choices[0]?.message?.content ?? '';
  return {
    reply: unmaskPii(raw, vault),
    entitiesMasked: matches.length,
  };
}

Also shipping Claude? See the Claude masking guide.