> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeroeval.com/llms.txt
> Use this file to discover all available pages before exploring further.

# PII redaction

> Opt-in SDK-side redaction for tracing data in the Python and TypeScript SDKs

ZeroEval supports opt-in source-side PII redaction in the Python and TypeScript
SDKs. When enabled, sensitive values in ingested payload fields (`input_data`
and `output_data`) are redacted before spans are sent by the SDK.

<Note>
  This is an SDK-side feature. ZeroEval does not rely on backend-side redaction
  for this behavior.
</Note>

## Enable redaction

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import zeroeval as ze

    ze.init(
        api_key="YOUR_API_KEY",
        redaction={"enabled": True},
    )
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    import * as ze from "zeroeval";

    ze.init({
      apiKey: "YOUR_API_KEY",
      redaction: { enabled: true },
    });
    ```
  </Tab>
</Tabs>

You can also enable the default redaction toggle with:

```bash theme={null}
export ZEROEVAL_REDACT_PII=true
```

Python uses snake\_case nested keys such as `redact_inputs`, `sensitive_keys`,
and `custom_patterns`. TypeScript uses the camelCase equivalents `redactInputs`,
`sensitiveKeys`, and `customPatterns`.

## What gets redacted

Redaction applies to ingested payload fields only:

* `input_data` (the value passed to `setIO` / `set_io`)
* `output_data` (the value passed to `setIO` / `set_io`)

When wrapper integrations (OpenAI, Vercel AI, LangChain) serialize prompts and
tool arguments into these IO fields, those values are also redacted.

Built-in detectors cover:

* email addresses
* phone numbers
* SSN-style identifiers
* PAN / credit card numbers
* bearer tokens, JWTs, and common API key formats
* cookie and authorization header values
* IP addresses

Key-based detection force-redacts common sensitive fields such as
`email`, `phone`, `password`, `token`, `authorization`, `cookie`, and
`api_key` / `apiKey` when they appear inside ingested IO payloads.

## What stays intact

All span metadata outside of `input_data` and `output_data` is preserved
unchanged:

* span names
* trace IDs and span IDs
* timing and status fields
* attributes
* tags, trace tags, and session tags
* session identifiers and session names
* error messages and stacks
* token counts, model/provider metadata, and cost metadata

## Placeholder behavior

Sensitive values are replaced with stable placeholders inside a single trace:

```text theme={null}
alice@example.com -> [REDACTED_EMAIL_A]
bob@example.com   -> [REDACTED_EMAIL_B]
alice@example.com -> [REDACTED_EMAIL_A]
```

* the same normalized sensitive value in one trace gets the same placeholder
* different values in the same trace get different placeholders
* placeholder assignment resets per trace
* matching is exact after normalization only
* this is not fuzzy identity resolution

That means repeated references across parent and child spans stay joinable
without preserving the original value.

## Normalization and limitations

| Type      | Normalization used for placeholder reuse                 |
| --------- | -------------------------------------------------------- |
| Email     | Trim + lowercase                                         |
| Phone     | Digits only                                              |
| SSN / PAN | Digits only                                              |
| IP        | Canonical or lowercase string as implemented by each SDK |
| Secrets   | Exact trimmed string                                     |

Important limitations:

* there is no reversible backend token vault
* there is no de-anonymization support
* there is no fuzzy entity resolution across semantically related strings
* bypassing SDK capture paths bypasses this protection

## Examples

The SDK repositories include runnable examples. In both examples, only the
values passed to `set_io` / `setIO` are redacted. Session metadata, tags,
attributes, and error messages are sent unchanged.

<Tabs>
  <Tab title="Python">
    From `zeroeval-sdk/examples/pii_redaction.py`:

    ```python theme={null}
    import zeroeval as ze

    ze.init(
        api_key="sk_ze_demo_local",
        redaction={"enabled": True},
    )

    with ze.span(
        name="pii-redaction-demo",
        session={"id": "alice@example.com", "name": "Alice alice@example.com"},
        tags={"customer_email": "alice@example.com"},
    ) as span:
        span.set_io(
            input_data={"email": "alice@example.com", "phone": "+1 (415) 555-1234"},
            output_data={"result": "Reach alice@example.com"},
        )
    ```

    In the resulting span:

    * `input_data.email` becomes `[REDACTED_EMAIL_A]`
    * `input_data.phone` becomes `[REDACTED_PHONE_A]`
    * `output_data.result` contains `[REDACTED_EMAIL_A]`
    * `session_id`, `session_name`, and `tags.customer_email` stay as-is
  </Tab>

  <Tab title="TypeScript">
    From `zeroeval-ts-sdk/examples/10-pii-redaction.ts`:

    ```typescript theme={null}
    import * as ze from "zeroeval";

    ze.init({
      apiKey: "demo-api-key",
      redaction: { enabled: true },
    });

    const span = ze.tracer.startSpan("demo.pii_redaction", {
      sessionId: "alice@example.com",
      sessionName: "Alice Example <alice@example.com>",
      tags: { customer_email: "alice@example.com" },
    });

    span.setIO(
      {
        email: "alice@example.com",
        phone: "+1 (415) 555-1212",
        apiKey: "sk-live-abcdef1234567890",
      },
      "Send follow-up to alice@example.com and bob@example.com"
    );
    ```

    In the resulting span:

    * `input_data` contains `[REDACTED_EMAIL_A]`, `[REDACTED_PHONE_A]`,
      `[REDACTED_SECRET_A]`
    * `output_data` contains `[REDACTED_EMAIL_A]` and `[REDACTED_EMAIL_B]`
    * `sessionId`, `sessionName`, and `tags.customer_email` stay as-is
  </Tab>
</Tabs>

Repeated exact values such as the same email address reuse the same
placeholder within a trace.
