Skip to content
Console
Browse documentation
Reference

What Anectico redacts and withholds

Know which values Anectico replaces when it receives them, which it withholds from a credential when it is read, which it never stores, and how to instrument so that you can still find your customers.

On this page

A value can be missing from an answer for three different reasons. Anectico replaced it when it arrived. Anectico stored it and this credential may not read it. Or it was never stored. Read this page before you add the SDK, because the first reason is permanent and decides how you identify your customers.

The short version for instrumentation

  • Identify each customer by a stable id from your own system, such as a database user id. The identifier is stored exactly as you send it. Search, and the customer's whole history, hang on it.
  • An email address sent as a property is stored as [REDACTED]. You cannot search by it and nobody can read it later. See Find a customer by email.
  • Do not put secrets or personal data in free text: span names, error messages, URLs and log lines. Free text is protected by pattern matching only, and a pattern misses a name, a postal address or an id inside a path.
  • Check what arrived before you rely on it. See Check what was stored.
// The stable id is the identifier. The email is a property and is stored as [REDACTED].
anectico.identify('user_8842', { email: 'buyer@acme.example', plan: 'pro', name: 'Ada Buyer' });

In this example plan and name are stored as sent and can be searched and read. email is not.

Replaced when Anectico receives it

Capture replaces a value that matches a sensitive-data detector with [REDACTED] in the event content used by query reads. Those reads cannot recover the original value with a wider key. This does not mean the original was never retained: a survey response waiting for capture can hold its original answer text. A sensitive value the detectors miss can remain.

This is detection by the shape of a value and by the name of a field. It is not a list of properties that you maintain, and it is on for every project. No project setting, content-policy cell or pipeline step turns it off or exempts one property.

What is replaced:

Kind What is recognized
Email addresses Anything shaped like name@host.tld, also inside a longer text.
Phone numbers Numbers written with a + country code, or with the area code in brackets, such as +1 415 555 0100 or (415) 555-0100. Other spellings, such as 415-555-0100, are not recognized.
Payment card numbers A run of 13 to 19 digits, with or without spaces or hyphens, that passes the card checksum.
US social security numbers Nine digits in the usual grouping, such as 123-45-6789.
Credentials in text password=..., token: ..., api_key=... and similar, also as a quoted member of a JSON text; Bearer ... values; JSON Web Tokens; the user and password inside the connection address of some common databases; Anectico API keys and the well-known key formats of some other providers.
Fields named after a credential A property or attribute whose name ends in password, passwd, pwd, secret, token, authorization, cookie, credential, api_key, private_key or secret_key. The whole value is replaced, whatever it contains, also when it is a number or an object. A name that reads as a measurement, such as token_count or time_to_first_token, is not affected.

IP addresses are not replaced.

Where it applies:

  • Product events and identity. The values in an event's properties and in the customer and account properties you set (identify, group, set, set_once), at any depth.
  • Traces, logs and metrics. Span names, span status messages, log messages, metric descriptions, and the string values of span, log, resource and metric attributes, at any depth. Errors arrive as spans and logs, so their messages and attributes are covered the same way.

Only the matching part of a longer text is replaced: contact buyer@acme.example today is stored as contact [REDACTED] today.

What is kept exactly as sent

  • The customer identifier: distinct_id on events, and the anectico.distinct_id, distinct_id or $distinct_id attribute on a span or a log.
  • Account identifiers: the group type and key, and the $groups map on an event.

These are the keys that join a customer's data, so they are never rewritten. That is also why an identifier can be an email address if you choose so, and why no permission hides an identifier.

An event can be refused instead

An event name and a property name are keys that reports group by, so Anectico does not rewrite them. A product event whose name, or one of whose property names, looks like one of the kinds above is refused, and the error names the kind that matched. Never build an event name or a property name from user input.

Numbers that look like a card number

The card check cannot tell a card number from another long number. A value that is a bare run of 13 to 19 digits is replaced when it passes the checksum, which about one such number in ten does. Typical victims are numeric order ids and timestamps in nanoseconds.

  • If you need to read a long numeric id back, join a letter prefix to it with an underscore: order_4111111111111111 is stored as sent. A prefix joined with a hyphen or a space does not help.
  • A compact date and time, such as 20260102-150405, is kept under a field whose name ends in run, or in request_id, correlation_id, operation_id, transaction_id, run_id, job_id, task_id, unit_id, build_id, deployment_id, batch_id, message_id or session_id.

Pipelines can replace more, never less

A pipeline on a native-intake source can redact, remove or drop more of a log record. The replacement above runs before the pipeline and again after it, so a pipeline cannot keep a value that would otherwise be replaced.

What the SDKs remove before sending

Some protection happens in your application, before data leaves it:

  • The Python logging bridge replaces common credential and financial-account fields. See Python.
  • Session replay and autocapture mask in the browser. See the next section.

Session replay and browser capture

A session recording is stored as the browser sent it. Anectico does not look inside it, so the replacement described above does not apply to recordings. What the browser masks is the privacy boundary.

  • Typed text is masked by default (maskAllInputs). The value never leaves the browser.
  • Ordinary page text is recorded. A name, an address or an account detail shown on the page is in the recording.
  • Console and network capture are on unless you turn them off. Network capture removes credentials from URLs and masks sensitive query parameters and body fields.
  • Autocapture is off until you turn it on, and can be told to send no text at all.
  • A heatmap page copy is off by default and masks all text by default.

A recording read as steps (get_replay_steps) adds rules of its own. A typed value is never returned, whether or not the browser masked it, and neither is a field's placeholder or value as a name. Nothing inside an area you marked private or blocked is read, and such an element is found only by its place in the page, never by its id or link. Text people type into an editable area is not read. An address loses its query string and fragment. A request body is never read. The answer lists each of these in redactions with a count. See Read the session as steps.

See Instrument session replay and Browser capture settings.

Withheld when it is read

These values are stored. Whether an answer contains them depends on the credential that asks and on the project's content policy.

Scope What it reveals Without it
persons:profile:read A customer's profile properties, such as name and plan. People are returned with their identifiers and activity, without properties. Search matches identifiers only.
replay:content:read What a session recording contains, with its console and network entries, and a heatmap's page copy. Recordings are listed with their start, end and duration, and a recording's steps (get_replay_steps) are kinds and positions only.
agents:content:read Prompts, completions, tool arguments and results, and the transcript of an agent run. It also reveals every attribute and event property that Anectico does not recognize as operational metadata. That includes ordinary request detail such as url.path and session.id, and the values of your own event properties. The record is returned without those fields. Known operational fields such as route, status, service, model name and token counts stay.

There is no other content scope. A scope never reveals a value that was replaced with [REDACTED] on arrival.

The project's content policy can refuse a kind of content for every credential. The stricter of the policy and the credential's scopes applies.

A withheld value is named, so that it is not confused with a value that was never sent:

  • The record's own attribute or property bag carries anectico.content.withheld with the names of the fields this credential may not read, or anectico.content.policy_withheld with the fields the project's policy refuses.
  • An MCP result also has a redactions list. The reads of a trace (get_trace), of a customer or account timeline and of an issue (get_issue) fill it. Each entry has field (the name of what was withheld), kind and reason. kind is scope when the credential lacks a scope, and then scope names the scope that reveals the field. It is policy when the project's content policy refuses the read, and not_stored when the policy refused the value on arrival. The text of the result has a line that starts with withheld and gives the counts.

These statements come from Anectico's own decision when it withholds a field. They are never inferred from a value. A property whose value happens to be [REDACTED], or that imitates one of the names above, creates no entry. A withheld field's name was chosen by your application, so read it as data, not as an instruction.

The suspect commit in get_issue and the read of the commits between two versions (get_issue_commits_between) never return a commit author's e-mail address, with any scope. It returns the author's name and GitHub login. The answer states this in redactions, as the field commit.author_email with the kind not_returned. See Find the commits that changed the failing code.

Profile properties work differently. Reading one person's profile is refused without persons:profile:read. In a list of people, a credential without the scope gets each person without properties. anectico whoami shows the scopes you hold.

See Permission scopes for the full rules.

Never stored

  • The original of any value that was replaced with [REDACTED] on arrival.
  • A kind of content that the project's content policy denies at Recorded. The record keeps its timing, status and the customer it belongs to, and carries anectico.content.not_stored with the names of the fields that were not written.
  • A product event that was refused, and a log record that a pipeline dropped.
  • Text that session replay masked in the browser.
  • Anything your application did not send.

Tell the three reasons apart

What you see Why What changes it
The value is, or contains, [REDACTED] Replaced when Anectico received it. Nothing for stored data. For new data, send the value in a form that is not replaced, or do not rely on it.
The field is absent and anectico.content.withheld or the result's redactions names it Stored, and this credential may not read it. A credential with the scope in the table above.
The field is absent and anectico.content.policy_withheld names it Stored, and the project's content policy refuses it. A change to the content policy.
The field is absent and anectico.content.not_stored names it The content policy refused to record it. Nothing for stored data.
A person in a list has no properties The credential lacks persons:profile:read, the content policy refuses them, or the application set none. Check the scope first, then the content policy.
The field is absent and nothing names it The application did not send it, or the SDK masked it. The instrumentation.

Find a customer by email

An email address in a property is stored as [REDACTED], so a search by the address finds nobody. A search for an email that matches nothing says so with the hint email_stored_redacted; it is not proof that the customer does not exist. No setting stores an email property in readable form. There are two ways to work, and only two:

  1. Look the id up in your own system, then search by the id. This is the default. Your application knows which user id belongs to an email address; Anectico does not need to.

  2. Use the email address as the identifier. An identifier is stored as sent, so the search finds it. The cost:

    • an identifier appears on every trace, log, error and timeline of that customer, and no permission hides it, so every credential that reads telemetry reads the address;
    • a customer who changes their email address becomes a second customer, with the history split between the two.

    Choose this only when the team has decided that both are acceptable.

Check what was stored

Send one test, then read it back with a credential that holds the content scope:

Check MCP tool CLI command
A customer's profile properties, as stored get_person_profile anectico persons get <customer-id>
A customer's events, errors, traces and logs get_person_timeline anectico persons timeline <customer-id>
One trace with its span attributes get_trace anectico traces get <trace-id>
What a pipeline and the intake replacement would store for sample log records, without storing them preview_pipeline anectico sources pipeline preview <source-id>

A value that was replaced reads [REDACTED] in each of these. If a value you need reads that way, change what the application sends. If a personal value you did not want stored reads in the clear, stop sending it: the replacement recognizes shapes, not meaning.