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

# Redaction

> Three composable layers to scrub data before it leaves the process

Redaction runs **after** the capture policy and **before** events hit the network. It walks every event and strips anything matching your rules, replacing the value with `"[REDACTED]"`.

Three layers, applied in order:

```typescript theme={null}
createBoundaryLogger({
  redact: {
    fields:   ["ssn", "email"],                     // 1. exact key names
    patterns: [/\b\d{3}-\d{2}-\d{4}\b/],            // 2. regex over string leaves
    custom:   (value, path) => scrub(value, path),  // 3. last-chance callback
  },
});
```

Each layer is optional. They compose — you can use any combination.

## Layer 1 — `fields`

Matches by object key (case-sensitive, deep). Whenever the walker encounters a key in this list, the entire value at that key is replaced with `"[REDACTED]"` — even if it's an object, array, or deeply nested.

```typescript theme={null}
redact: {
  fields: ["ssn", "apiKey", "password", "phone"],
}
```

Matches `{ user: { ssn: "123-45-6789" } }` and `{ customers: [{ phone: "..." }] }` alike.

## Layer 2 — `patterns`

Regex patterns applied to every string leaf in the event. Each match is replaced with `"[REDACTED]"` — the rest of the string is preserved.

```typescript theme={null}
redact: {
  patterns: [
    /\b\d{3}-\d{2}-\d{4}\b/,                    // SSN
    /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/,  // credit card
    /[\w.+-]+@[\w-]+\.[\w.-]+/,                 // email
    /\+?\d{1,3}[\s-]?\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{4}/,  // phone
    /sk-[A-Za-z0-9]{20,}/,                      // OpenAI-style secret
  ],
}
```

Patterns run across all nested string values — including inside `repairs[].content` and any raw `input` / `output` you've opted into capturing.

## Layer 3 — `custom`

Runs last, after `fields` and `patterns`. Called once for every **leaf** in the event tree, with the current value and the full JSON path to that leaf.

```typescript theme={null}
redact: {
  custom: (value, path) => {
    // Hash customer IDs but keep them correlatable
    if (path.includes("customerId") && typeof value === "string") {
      return hash(value);
    }
    // Drop a field entirely by returning undefined
    if (path.includes("internalToken")) return undefined;
    // Default — keep the value as-is
    return value;
  },
}
```

Return:

* the value unchanged to keep it,
* a transformed value to replace it,
* `undefined` to drop the leaf.

### Path semantics

`path` is an array of string segments. For `{ repairs: [{ role: "user", content: "..." }] }`, the walker calls `custom` with:

* `path = ["repairs", "0", "role"]`
* `path = ["repairs", "0", "content"]`

Array indices are stringified. Use `path.includes(...)` for coarse matching or `path.join(".")` for precise matching like `repairs.0.content`.

## Cycle safety

The redactor tolerates cyclic object references. Each object is entered at most once; on revisit, the walker returns `"[CYCLE]"` in place. You never crash, even if `beforeSend` enrichment accidentally creates a cycle.

## Composability example

Scrub emails/SSNs by pattern, wipe specific fields by name, hash the customer ID via custom:

```typescript theme={null}
createBoundaryLogger({
  redact: {
    fields: ["ssn", "passport", "dob"],
    patterns: [
      /\b\d{3}-\d{2}-\d{4}\b/,
      /[\w.+-]+@[\w-]+\.[\w.-]+/,
    ],
    custom: (value, path) => {
      if (path.at(-1) === "customerId" && typeof value === "string") {
        return `cust_${hash(value).slice(0, 10)}`;
      }
      return value;
    },
  },
});
```

## When redaction isn't enough

If you need to make a decision about the **whole event** (e.g. drop all events for a given contract, stamp a trace ID across every event), use [`beforeSend`](/sdk/before-send). Redaction works per leaf; `beforeSend` works per event.

## See also

<CardGroup cols={2}>
  <Card title="Capture policy" icon="filter" href="/sdk/capture-policy">
    Strip buckets of data before redaction runs
  </Card>

  <Card title="beforeSend" icon="code" href="/sdk/before-send">
    Last-chance transform or drop
  </Card>
</CardGroup>
