# Pseudonymize PII with NER IDs

> Replace matched PII with stable NER ID pseudonyms instead of a static placeholder, so the model still reasons over which values belong together, then restore the real values before the response reaches the caller.

This guide changes a Prompt Guard rule’s match action from static redaction to NER ID pseudonymization, and turns on response-side restoration, so a masked prompt stays useful to the model without the provider ever seeing the raw value. For the concept behind pseudonymization, see [PII protection and NER IDs →](/products/affinidi-trust-fabric/agent-stream/concepts/pii-protection.md).

Static redaction replaces every match of a type with the same placeholder, such as <EMAIL>. When a prompt names more than one person, this collapses every one of them into the same indistinguishable shape, and the model can no longer tell which address or email belongs to which person.

Use this guide when:

- Your surface handles prompts that mention more than one person, account, or record whose values need to stay distinguishable from each other.

- You need the model to produce a personalized answer, such as a separate reply to each of two customers, without ever seeing either customer’s real details.

- The caller should receive the real value back in the response, not a placeholder.

You do not need NER IDs if a single generic placeholder per category is enough for your use case; static redaction is simpler and has no restoration step to configure.

## Prerequisites

- An active LLM Surface with a Prompt Guard element on the request seam. See [Add guardrails and a budget](/products/affinidi-trust-fabric/agent-stream/get-started/add-guardrails-and-budget.md) if the surface has none yet.

- Dashboard access with permission to edit surfaces.

## Steps

Open the Prompt Guard editor on the request seam

Under SURFACES in the dashboard sidebar, select LLM, then open your surface. Select the Prompt Guard element on the request seam: the top ("→") card in the editor, headed Prompt Guard with the subtitle “Screen requests before they reach the model.”

Add a pattern rule for each value type

Under Detect these patterns, add a Built-in Pattern (for example email, ssn, or credit_card) or a Custom Regex with a label for each value type that should get its own pseudonym.

Set “When detected” to preserve context

In the When detected dropdown, select Use a NER ID to preserve context. This replaces the redact-and-reject options with pseudonymization: every distinct matched value gets its own stable NER ID, such as [EMAIL_NER_001], reused for that exact value everywhere it appears in the request.

Enable restore in the response

Turn on Restore Original Values in the Response. This swaps every pseudonym back to its real value after every model-facing stage, the provider call, the Decider, the Judge, and the Jury, so only the final response the caller receives carries real values.

Optional: link pronouns to a named person

If a prompt refers back to a masked person with “he” or “she”, turn on Link Pronouns to a Person’s NER ID. This is a free, regex-based feature with no effect on caching: it links a singular gendered pronoun to the one PERSON NER ID seen so far in the text.

Save the surface

Select the save icon in the toolbar. The change applies immediately; no restart is required.

## Confirm

Replace <YOUR_APPLIANCE_HOST> and <YOUR_SURFACE_ROUTE> with your surface’s values.

### Test 1: the response pairs Sarah Chen with her own email, not James Cole’s

```bash
curl -k -X POST "https:///v1/chat/completions" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      { "role": "user", "content": "Sarah Chen'\''s email is sarah.chen@example.com. James Cole'\''s email is james.cole@example.com. Draft a one-line welcome note to each person, addressed by name, confirming their own email address." }
    ]
  }'
```

The -k flag disables TLS certificate verification. Use this for local testing only. Remove it in production.

Expected: 200 OK, with the response correctly pairing Sarah Chen with sarah.chen@example.com and James Cole with james.cole@example.com. If both people were collapsed to the same placeholder instead, the model could not have produced this pairing.

### Test 2: the logged request shows pseudonyms, not raw values

If request logging is enabled on the surface, open the surface’s Monitoring tab and scroll to Surface Logs, find the request from Test 1, and inspect the logged outbound request body. It should show [EMAIL_NER_001] and [EMAIL_NER_002] in place of the two email addresses, confirming the provider itself never received the raw values.

## Troubleshooting

| Symptom | Likely cause | Fix |
| Both people’s details come back on the wrong person, or collapsed to one identity. | The rule’s When detected option is still set to redact, or only a single generic pattern was added instead of a NER-ID rule. | Re-open the Prompt Guard editor and confirm When detected reads Use a NER ID to preserve context for the relevant pattern. |
| The response still contains a NER placeholder instead of the real value. | Restore Original Values in the Response is off, or the rule is a response-side guard rather than a request-side one. | Restoration only applies to a request-side pseudonymize guard. Turn on Restore Original Values in the Response on the request-seam rule. |
| A pronoun (“he”/“she”) resolves to the wrong person. | Link Pronouns to a Person’s NER ID links a singular gendered pronoun to the one PERSON NER ID seen so far; with more than one person already named, the link is ambiguous by design. | Have the prompt refer to each person by name instead of a pronoun where more than one person is in play, or accept this as a known limitation of deterministic pronoun linking. |
| A NER ID appears unrestored inside a streamed tool call. | Streamed restoration covers message text only; a NER ID inside a streamed tool-call argument is not restored, since tool-call arguments are only fully known once the stream completes. | This is expected behaviour for streaming tool calls, not a misconfiguration. |

## Next steps

- [Detect free-form PII with AI matching](/products/affinidi-trust-fabric/agent-stream/how-to-guides/guardrails/detect-free-form-pii-with-ai-matching.md): Catch names, addresses, and other free-form PII that a regex pattern cannot express.

- [Block and re-review calls with Judge and Jury](/products/affinidi-trust-fabric/agent-stream/how-to-guides/guardrails/block-and-re-review-calls-with-judge-and-jury.md): Layer a pre-call and post-call safety review on top of Prompt Guard.

- [PII protection and NER IDs](/products/affinidi-trust-fabric/agent-stream/concepts/pii-protection.md): The full round trip, coreference linking, and governance visibility.

## Related

- [Guardrails](/products/affinidi-trust-fabric/agent-stream/concepts/guardrails.md): Where Prompt Guard sits alongside Expert Witnesses, Judge, and Jury.

- [Guardrails reference](/products/affinidi-trust-fabric/agent-stream/reference/surfaces/guardrails.md): The full Prompt Guard field reference.

- [Governance records](/products/affinidi-trust-fabric/agent-stream/concepts/governance-records.md): How a PII detection stage is captured without exposing the value map itself.
