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.”
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:
t, ts, timestamp) in clear text and tag only the signature itself, so you can still reason about timing:
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:
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.
redactions array, so you can audit what was removed:
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. |
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.
Operating rules
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.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.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.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.Continue
Authentication & scopes
Why
secrets should almost always be absent.Diagnose webhooks
Where redacted payloads appear in an investigation.
Output & errors
The
forbidden_scope error you get without secrets.