Title
Create new category
Edit page index title
Edit category
Edit link
AIO Span and Trace Masking
Read-time masking lets you hide or transform parts of traced data (spans and traces) as they are returned by the AIO read APIs and UI, without ever touching the stored data. You configure it per workspace, and optionally narrow it to a single project. The problem it solves: sensitive content (system prompts, tool calls, PII in message payloads) is often captured in traces but should not be visible to everyone who reads them — and you need to control that visibility without re-ingesting or deleting the underlying records.
A rule does one of two things, depending on the action you choose:
Action | What it does | Applies to |
|---|---|---|
| Removes matching spans from list/tree responses and reparents their children to the nearest surviving ancestor, so the tree stays connected. | Spans only |
| Blanks JSON fields in place on matching entities — it sets the value a JsonPath selects to | Spans, Traces (and Threads) |
Use DROP_AND_REWIRE when you want a whole span to disappear from listings (for example, noisy tool-call spans), and REDACT when you want the entity to remain visible but with specific values blanked.
Important
REDACTblanks in place — it sets the matched values to a constant. It cannot delete keys or remove array elements, so array lengths and positions are always preserved. A matched array element becomesnull/"[REDACTED]"; it does not disappear from the array.
1. Authentication & scoping
All endpoints live under /aio/api/v1/private/aio/masking-rules. You authenticate with your workspace access credentials, sent as request headers:
The workspace a rule belongs to is taken from the tenant subdomain in the URL you call — never from the request body. In https://aio-test.acceldata.app/…, the subdomain aio-test is the workspace, and it becomes the rule's workspaceId. Because scoping is tied to the URL rather than anything you pass in the body, every operation is automatically isolated to the workspace you're calling — you cannot accidentally create or touch a rule in another workspace.
If your credentials are missing or invalid, the request is rejected with 403 Forbidden.
For brevity, the examples below assume you've set:
2. Endpoints
There are four endpoints — list, create, toggle, and delete. Paths are relative to $BASE (…/aio/api/v1/private).
Method | Path | Description | Success | Errors |
|---|---|---|---|---|
|
| List all rules in your workspace (enabled and disabled). |
|
|
|
| Create a rule. |
|
|
|
| Enable/disable a rule. Body: |
|
|
|
| Delete a rule. |
|
|
Note There is no full-update (PUT) endpoint. To change a rule's filters or patches, delete it and create a new one.
Your changes take effect immediately: creating, deleting, or toggling a rule invalidates the rule cache. (When nothing changes, the effective-rules cache simply expires on a short TTL of about 60 seconds.)
3. The MaskingRule object
This is the shape of a rule as the API returns it. When you create one, you only supply the fields that describe what to match and what to do — the server fills in the rest.
Here is what each field you can set means:
Field | Description | Default | Required |
|---|---|---|---|
| A human-readable label so you can recognize the rule later. | — | Yes |
|
| — | Yes |
|
| — | Yes |
| The predicates that decide which entities the rule matches. Must be non-empty. | — | Yes |
| What to blank, for |
| For REDACT |
| Whether the rule is active. Set it to |
| No |
| A project UUID to scope the rule to a single project, or |
| No |
| An integer that orders rules when several apply; lower values are applied first. |
| No |
| Server-assigned — omit them (or send | — | No |
Note:
entityKindandactionare uppercase enum names.createdAt/lastUpdatedAtare stored but not surfaced in responses in v1.
4. Filters (filters[]) — choosing which entities a rule matches
A filter is a single predicate. A rule matches an entity only when every filter matches (logical AND), so adding more filters narrows what the rule applies to. Because a rule must never match everything, an empty filters array is rejected with 400.
5. Available filter fields
These are the fields you can filter on today. (The data model defines a broader canonical set mirroring Opik's filters — duration, usage tokens, feedback scores, and so on — but if you filter on a field that isn't in the tables below, it currently evaluates to no match rather than returning an error. So double-check your field names against these tables.)
Spans (entityKind: "SPAN"):
Field | What you match against |
|---|---|
| The span/trace UUID |
| The span name |
| The span type — |
| The LLM model / provider |
| The span source (e.g. |
| The tag set |
| The stringified input/output JSON — handy with |
| The JSON value; add a |
Traces (entityKind: "TRACE") — the same as spans minus type/model/provider, plus thread_id:
Field | What you match against |
|---|---|
| As above |
| The thread id |
| The stringified input/output |
| The JSON value (+ optional |
Operators you can use: =, !=, contains, not_contains, starts_with, ends_with, >, >=, <, <=, is_empty, is_not_empty.
A few things to keep in mind when you pick an operator:
=/!=are case-sensitive, butcontains/starts_with/ends_withare case-insensitive.The numeric operators (
>,>=,<,<=) compare numerically; if the value isn't numeric, the filter is treated as no-match.key(a JsonPath like$.env) is only meaningful forinput_json/output_json/metadata. Use it to reach inside the JSON, e.g.{"field":"metadata","key":"$.env","operator":"=","value":"prod"}matches only entities whose metadata hasenvset toprod.
6. Patches (patches[]) — for REDACT only: what to blank
A patch tells a REDACT rule exactly which value to blank and what to replace it with. You need at least one patch on a REDACT rule (a REDACT rule with no patches is rejected 400), and patches are ignored entirely on DROP_AND_REWIRE rules.
How to fill each part:
fieldselects which root part of the entity you're editing — one ofINPUT,OUTPUT,METADATA,ERROR_INFO(uppercase).jsonPath(jayway syntax) selects the value or values to set toconstant. You can target:a single property —
$.system_promptevery element that matches a predicate —
$.messages[?(@.type=='tool')]the whole field —
$, which replaces the entire input/output/metadata
constantmust benullor a string. Numbers, booleans, and objects are rejected. Usenullwhen you want a clean, removal-like blank, or a marker such as"[REDACTED]"when you want readers to see that something was withheld.
Two behaviors to be aware of so you aren't surprised:
A
jsonPaththat matches nothing is a no-op — the field is left untouched. So if a redact "isn't working," check the path first.ERROR_INFOpatches always blank the wholeerrorInfofield; thejsonPath/constantare not applied to sub-paths.
Warning: Patches are fail-closed. If a patch's JsonPath is malformed, the entire target field is blanked rather than returned unredacted — so a broken path errs on the side of hiding more, not less.
7. Where masking is (and isn't) applied
It helps to know exactly which read paths honor your rules, because masking is not applied everywhere.
Masked (read path):
Spans: list (find), getById (
REDACTonly — see below), getByTraceIds, search.Traces: get, list (find), search.
Threads: find, getById, search. A
TRACEREDACTrule also blanks the thread'sfirstMessage(treated as input) /lastMessage(treated as output).
Behaviors worth knowing:
DROPonly affects lists and trees, never a direct single-spanGET /spans/{id}(which isREDACT-only). So a deep link to a dropped span still resolves — it just won't show up in listings. When dropping happens in a paged list, the page total/size is adjusted to the post-mask count.Traces are never dropped (
REDACTonly);DROP_AND_REWIREis a span-only concept.For spans,
REDACTruns beforeDROP, so a span may be redacted and then dropped.
Not masked (out of scope in v1): stats/aggregations, exports, getByIds (internal flows such as experiment-item creation), and trace→dataset materialization. The data also stays unmasked in the database itself — masking is purely a read-time concern.
8. Examples
8.1 Drop all tool spans (workspace-wide)
Hide every tool-call span from your span listings:
Effect: every span with type = tool is removed from span listings, and its children are reparented to the nearest surviving ancestor so the tree stays connected.
8.2 Drop spans named RunnableSequence in a specific project
This uses an exact match. If you also want to catch variants like RunnableSequence<...>, switch the operator to "starts_with".
8.3 Redact system/tool messages in a span's input (nested array)
ChatBedrockConverse spans carry input.messages as a nested array (messages[x][y]). This rule blanks the message objects whose type is system or tool, in place:
Result: the matching message objects become null, the array keeps its length, and human/ai messages are untouched. If you'd rather show a visible marker than a blank, set "constant": "[REDACTED]".
8.4 Redact tool messages in a trace's output (flat array)
The LangGraph trace (the root in the traces view — not a span) has output.messages as a flat array (messages[y]), so this is a TRACE rule:
Watch the path shape, because it's the most common mistake: a nested array needs $.messages[*][?(...)], while a flat array needs $.messages[?(...)]. The wrong shape simply matches nothing and silently leaves the data unmasked.
8.5 List, toggle, delete
list all rules in your workspace
disable a rule (brings the data back) — re-enable later with "enabled": true
delete a rule
Disabling is the safe, reversible way to stop masking; deleting removes the rule for good.
9. Errors
If a request fails, the status code tells you why:
Status | When |
|---|---|
| Empty |
| Missing or invalid auth credentials. |
| A |
For additional help, contact www.acceldata.force.com OR call our service desk +1 844 9433282
Copyright © 2025