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

# Transcript safety

> What Repost redaction covers, what it provably does not, and the rules for keeping sensitive data out of agent transcripts.

Agent transcripts are durable and frequently leave your infrastructure — they are stored, replayed, and sent to third-party model providers. Repost redacts **credentials** from event payloads by default, but redaction is a credential scrubber, not a privacy control. The boundary between the two is the single most important thing to understand before copying any payload into a transcript.

<Danger>
  **Redaction does not remove PII.** Names, email addresses, phone numbers, postal addresses, card numbers, bank details, national IDs, IP addresses, and customer identifiers pass through **verbatim**. Detection is by credential key/header name only — it has no concept of personal data. Never treat a payload as safe to paste, log, or forward just because it came back "redacted."
</Danger>

## What redaction covers

Redaction is applied **server-side, by default**, on exactly two commands — `events get` and `events diff` — the only commands that return full headers and bodies. The CLI never receives the unredacted form unless you explicitly opt out (see below). Each response also lists the exact paths it touched in a `redactions` array.

**Headers** are redacted by name:

```text theme={null}
authorization   proxy-authorization   cookie   set-cookie   x-api-key   x-api-token
stripe-signature   x-hub-signature   x-hub-signature-256   x-slack-signature
svix-signature   webhook-signature   x-signature
```

Signature headers keep their plain metadata (`t`, `ts`, `timestamp`) in clear text and tag only the signature itself, so you can still reason about timing:

```json theme={null}
"stripe-signature": ["t=1718061000,v1=[redacted:hmac:9f2a1c4d8b07]"]
```

**JSON-object body keys** are redacted by name — `password`, `token`, `secret`, `api_key` / `apikey`, `access_token`, `refresh_token`, `client_secret`, and any key whose name contains `secret` or `token`. Redaction recurses into nested objects.

Each redacted value becomes a deterministic tag:

```text theme={null}
[redacted:hmac:9f2a1c4d8b07]
```

<Note>
  The tag is the first 12 hex characters of an org-scoped HMAC of the value. It is **stable for the same value within an organization** — identical secrets produce identical tags — so you can correlate "same credential" across events without ever seeing the secret. It is not reversible and differs across organizations.
</Note>

Every response names the exact paths it touched in a `redactions` array, so you can audit what was removed:

```json theme={null}
"redactions": ["request.headers.authorization", "request.headers.stripe-signature"]
```

## What redaction does not cover

The mechanism is a name-based credential filter. Everything outside that definition is returned in full. Assume each of these is present and act accordingly.

| Gap                              | Why it leaks                                             | Example                                                                                                   |
| -------------------------------- | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| **All PII**                      | No value-based or personal-data detection exists.        | `"email": "ada@example.com"` is returned as-is.                                                           |
| **Secrets under innocuous keys** | Only sensitive *key names* match.                        | `{"data": "sk_live_51H..."}` — key `data` is not sensitive, so the value stays.                           |
| **Anything inside arrays**       | Redaction descends into objects, **not** array elements. | `{"items": [{"token": "..."}]}` — `token` inside the array is **not** redacted.                           |
| **Non-object bodies**            | Only JSON objects are parsed and filtered.               | Form-encoded, plain-text, XML, or a top-level JSON array body is shown **raw**, with no redaction at all. |
| **URLs and paths**               | Not in scope.                                            | Query strings and path segments are never redacted.                                                       |

<Warning>
  The two highest-risk gaps are **arrays** and **non-object bodies**. A webhook that sends a JSON array, a form post, or secrets nested in a list receives **no body redaction**. Inspect the `redactions` array: if it is empty for a body you expected to be scrubbed, the payload was not an object — treat the whole body as sensitive.
</Warning>

## Opting out: the `secrets` scope

`events get --reveal-secrets` and `events get --as-fixture` disable redaction and return full cleartext. Both require a token with the `secrets` scope; without it the CLI fails with `forbidden_scope` (exit `3`) and emits nothing.

<Warning>
  Reserve `secrets` for a narrow, deliberate need — reproducing a signature locally, building a fixture — and direct that output only to a trusted destination. Adding `--reveal-secrets` to "make debugging easier" routes raw credentials straight into the transcript. See [Authentication & scopes](/agents/authentication#scope-model).
</Warning>

## Operating rules

<Steps>
  <Step title="Default to redacted, always">
    Never add `--reveal-secrets` or `--as-fixture` for convenience. If a token does not carry `secrets`, you cannot leak credentials through `events get` — keep it that way.
  </Step>

  <Step title="Assume every body contains PII">
    Redaction will not catch it. Before a payload crosses a trust boundary (a logged transcript, a model provider, a ticket), summarize it — "a `payment_intent.succeeded` for `in_123`" — instead of pasting it.
  </Step>

  <Step title="Prefer IDs and metadata over bodies">
    `event_id`, `forward_id`, status, latency, and response code are almost always enough to drive a decision and carry far less risk than a full body.
  </Step>

  <Step title="Read the redactions array, but don't trust its absence">
    `redactions` tells you what *was* removed. An empty or short list means little was matched — often because the data was PII or lived in an array, not because the payload was clean.
  </Step>

  <Step title="Scrub what you must keep">
    If a body has to enter the transcript, remove PII yourself before writing it. Redaction is the floor, not the ceiling.
  </Step>
</Steps>

**Redaction contract**

* Applies by default on `events get` / `events diff` only (the only full-payload commands). `redactions[]` lists the exact paths removed.
* Removes by **name**: credential + signature headers; JSON-object keys `password`/`token`/`secret`/`api_key`/`apikey`/`access_token`/`refresh_token`/`client_secret` and any key containing `secret`/`token`; recurses into nested objects. Signature headers keep `t`/`ts`/`timestamp`.
* Tag form `[redacted:hmac:<12hex>]`: deterministic per (org, value), not reversible.
* Does **NOT** remove: any PII; secrets under non-sensitive keys; anything inside arrays; non-object bodies (form/text/XML/top-level array → raw); URLs/paths.
* `--reveal-secrets` / `--as-fixture` → full cleartext, requires `secrets` scope.
* Rule: treat every body as PII-bearing; emit IDs/metadata, not payloads; scrub before persisting; never opt out for convenience.

## Continue

<Columns cols={3} className="gap-y-4">
  <Card title="Authentication & scopes" icon="key-round" href="/agents/authentication" cta="Scope a token" arrow="true">
    Why `secrets` should almost always be absent.
  </Card>

  <Card title="Diagnose webhooks" icon="search-check" href="/agents/diagnose-webhooks" cta="Inspect safely" arrow="true">
    Where redacted payloads appear in an investigation.
  </Card>

  <Card title="Output & errors" icon="braces" href="/agents/output-and-errors" cta="The contract" arrow="true">
    The `forbidden_scope` error you get without `secrets`.
  </Card>
</Columns>
