Skip to main content

Input Controls

PII Detector

pii-detector finds personal identifiers such as email addresses, Social Security numbers, and card numbers, and replaces them before the model sees them. My SSN is 123-45-6789 reaches the model as My SSN is [REDACTED:SSN].

It can also block the request instead. On Chat Completions, Responses, and Messages, it redacts the model response too. For a streamed response, the gateway holds the whole response until it has redacted it, so the client waits longer for the first token.

Configuration​

Save this as policy-config.yaml in a new test directory. To add it to a running gateway, use the config-first workflow.

policy-config.yaml
pack:
  name: pii-protection
  version: 1.0.0
  enabled: true
policies:
  chain:
    - pii-detector
policy:
  pii-detector:
    action: redact
    healthcare_mode: false
    pci_mode: true
    detect_patterns: []
    redaction:
      marker_format: label
      include_metadata: true
      preserve_length: false
      custom_markers: {}

To block requests with card data instead of redacting them, set action: block and keep pci_mode: true.

Try it without a provider​

Add these tests to the end of the file:

Add to policy-config.yaml
testing:
  suites:
    - name: pii-detector
      cases:
        - name: redacts-identifier
          input:
            messages:
              - role: user
                content: "My SSN is 123-45-6789"
          expected:
            verdict: redact
            reason_code: redact.applied
        - name: allows-greeting
          input:
            messages:
              - role: user
                content: "Hello there"
          expected:
            verdict: allow
            reason_code: ok
verdictan policy lint --file policy-config.yaml
verdictan policy test --json

Both cases report passed: true. The overall reason for the redaction is redact.applied, and the nested policy_results entry names the match: pii.ssn_detected.

A verdict does not show the forwarded text. To see the replacement itself, send the same synthetic identifier through a test gateway and examine the redaction result.

Fields​

FieldTypeDefaultDescription
actionstringredactWhat happens to a match in the request: redact replaces it, block rejects the request. Response redaction runs with either value.
healthcare_modebooleanfalseAdds HIPAA identifiers: names, addresses, cities, fax numbers, MRNs, health plan IDs, license numbers, device IDs, biometric terms, and photo terms
pci_modebooleantrueAdds payment card numbers, CVV or CVC values, and expiration dates. Cardholder names are not detected.
detect_patternsstring[][]Extra regular expressions for request and response text. Each match has the type generic_id.
redaction.marker_formatstringlabellabel writes [REDACTED:SSN]. asterisk writes ***. partial masks SSNs, card numbers, and email addresses, and writes a label for other types.
redaction.include_metadatabooleantrueAdds the type, replacement, offsets, and a SHA-256 hash of each original span to the decision details. false removes only the redaction metadata: the span list, the x-verdictan-redaction-count and x-verdictan-redacted-entities headers, and the true value of x-verdictan-response-redacted. With gateway 0.1.26 or later, the client still gets the redacted text for each redacting policy on Chat Completions, Responses, and Messages.
redaction.preserve_lengthbooleanfalseWith marker_format: asterisk, writes one asterisk per original character
redaction.custom_markersobject{}Replacement text by lowercase type, such as ssn, email, mrn, pan, or generic_id. Overrides marker_format for that type.

How it works​

  1. The gateway joins the content of all request messages into one string.
  2. The base detector runs on it.
  3. healthcare_mode adds the HIPAA checks, and pci_mode adds the card checks.
  4. Each detect_patterns regular expression runs, and each match is recorded as generic_id with confidence low.
  5. Matches are sorted by position and by the priority of their type.
  6. No match: the verdict is allow with pii.clean.
  7. A match with action: block: the verdict is block with the reason code of the first match, for example pii.ssn_detected.
  8. A match with action: redact: the verdict is redact with the same reason code. The gateway replaces each match in the request and sets the response header x-verdictan-prompt-redacted: true.
  9. When the provider responds, the same detectors run on the response text. A match is replaced and adds an output result with verdict redact and reason pii.detected.
  10. A redacted response carries the headers x-verdictan-response-redacted, x-verdictan-redaction-count, and x-verdictan-redacted-entities.

The input decision details contain first_kind, first_reason_code, first_confidence, detection_count, and confidence_counts. The output details contain detection_count and entity_types.

Detection coverage​

ModeIdentifiersReason code prefix
Base detector, always onEmail addresses, SSNs, phone numbers, public IPv4 and IPv6 addresses, URLs, MAC addresses, IMEIs, VINs, ZIP codes, dates, account and routing numbers, IBANs, SWIFT codes, license platespii.
healthcare_mode: trueNames, addresses, cities, fax numbers, MRNs, health plan IDs, certificate and license numbers, device IDs, biometric terms, photo termsphi.
pci_mode: truePayment card numbers, CVV or CVC values, expiration datespii.pan_detected, pci.
detect_patternsEach regular expression matchpii.id_detected

Phase and verdicts​

ItemValue
Phaseinput for the request, output for response redaction
Request familiesChat Completions, Completions, Responses, Messages, Embeddings, Audio transcriptions, Moderation
Verdictsallow, redact, block
Reason codespii.clean, pii.detected, and a pii.*, phi.*, or pci.* code for the first match, such as pii.ssn_detected or phi.mrn_detected

A blocked request gets HTTP 400 with error code content_policy_violation. The body has a verdictan.decision object with verdict BLOCK and the reason code.

Output redaction reason codes​

pii-detector, dlp-filter, hipaa-phi-detector, student-privacy, case-privacy, and dual-use-filter with action: redact use the same redaction pass on the model response. When this pass changes the response, the decision event has one output result with the verdict redact. With gateway 0.1.26 or later, the result names one policy and uses the reason code of that policy:

PolicyOutput redaction reason code
pii-detectorpii.detected
dlp-filterdlp.redact
hipaa-phi-detectorhipaa_phi.detected
student-privacystudent_privacy.redact
case-privacycase_privacy.detected
dual-use-filterdual_use.triggered

When the chain has pii-detector, the result names pii-detector. Otherwise, the result names the first policy of the chain in this sequence: hipaa-phi-detector, dlp-filter, student-privacy, case-privacy, dual-use-filter. The decision reason code is redact.applied when no other policy blocks or escalates the request.

Request results keep the reason code of each policy, for example pii.ssn_detected, phi.mrn_detected, or case_privacy.detected.

Limits and failure behavior​

  • The detectors are regular expressions and validators, not a model. Numbers such as order IDs can match by mistake, and unusual formats can be missed.
  • The reason code names only the first match. Use detection_count and the redaction metadata for the rest.
  • A detect_patterns entry with invalid syntax is silently ignored. Lint the pack and test each pattern with verdictan policy test.
  • A detect_patterns match is always generic_id with the label [REDACTED:ID]. Named capture groups do not create new types.
  • marker_format: partial keeps the last four digits of an SSN or card number and the first character of an email address. Other types get the label marker.
  • With action: block, output redaction still runs on the model response.
  • When hipaa-phi-detector is also in the chain, request and response redaction use healthcare_mode: true and pci_mode: false. This applies to streamed and buffered responses.
  • With stream: true, the client receives no tokens until the provider has finished, because the gateway redacts the whole text first.
  • The request check runs for every listed request family. Response redaction runs for Chat Completions, Responses, and Messages.

Next steps​