Content capture
Both SDKs capture content by default:captureContent / capture_content is true unless you set it to false (since TypeScript 0.14.0 and Python 0.8.0; earlier releases need the option set explicitly). With capture on, spans record prompts and messages, responses, and tool arguments and results, alongside model, usage, timing and errors. Hue can show, search and evaluate only what a trace records, so a trace without content cannot become a case.
Set it to false for metadata-only export. The SDKs then drop helper content and strip recognized GenAI, OpenInference, OpenLLMetry, Langfuse and Vercel AI SDK content attributes from every record on Hue’s export path, including spans from other instrumentation on the same provider. Custom attribute names pass through, so configure third-party instrumentors’ own content controls too. Metadata-only mode does not turn off redaction or credential scrubbing. If your application reads HUE_CAPTURE_CONTENT, treat only an explicit false as the opt-out.
With capture on, images, audio, documents and very large values upload apart from the span after redaction; see Media. Nothing uploads with metadata-only capture.
Hue names each trace from its own recorded request text, or from a trace name the producer set, inside Hue. Naming sends nothing to a model provider and needs no setting.
HTTP content
Default HTTP capture adds app request inputs and response bodies to captured content from TypeScript 0.26.0 and Python 0.14.0. These HTTP values pass through the SDK’s credential filtering and your configuredredact / redactor callback. Credential filtering is not a general personal-data policy. Review app payloads and query values with synthetic examples before sending real data.
Set captureHttpBodies: false / capture_http_bodies=False to keep HTTP identity without content, or captureHttp: false / capture_http=False to disable HTTP capture. captureContent: false / capture_content=False also disables HTTP content. The SDK does not read HUE_CAPTURE_HTTP or HUE_CAPTURE_HTTP_BODIES; applications can read those variables and pass the options themselves.
HTTP body capture is bounded, with a default 1 MiB limit per request or response. Over-limit HTTP bodies use structured truncation markers rather than oversized-value uploads. Binary, unread or incomplete streams and over-limit bodies may not supply a complete exchange. Hue records missing or truncated evidence as such; it cannot reconstruct the missing app response. A trace with HTTP identity alone does not prove that the response needed to build a World was recorded.
With HTTP body capture enabled, supported compressed bodies are decoded in a bounded copy before credential filtering and your redactor run. An upgrade can therefore capture app payloads that were previously unavailable. Review your redactor with representative compressed payloads; the existing content controls still apply. Decoding for capture does not change the bytes your application receives.
TypeScript decodes gzip, x-gzip, zlib-wrapped or raw deflate, and Brotli. Python decodes gzip, x-gzip and both forms of deflate with the standard library. Raw Brotli capture in Python requires the optional hue-run[compression] extra, which installs Brotli >=1.2.1,<2; older or unsupported decoder versions are refused. Already-decoded client responses need no extra. Installing the extra can also enable an HTTP client’s own Brotli negotiation; the base SDK does not change that negotiation.
Encoded input and each decoded layer must fit the capture cap. Python additionally permits at most eight encoding layers and sixteen concatenated gzip members per layer, and limits each Brotli decoder output block to 32 KiB. Missing decoders and malformed, incomplete or over-limit compressed content leave a truncation marker. These limits affect capture only.
Retention
Telemetry retention is configured per project. A policy removes eligible older traces by periodic sweep; without one, telemetry does not expire automatically. Saved snapshots retain evidence after the source trace expires and need separate deletion; archiving does not delete data. Quotas protect admission, not retention.AI processing
Hue assesses stored traces, and some assessments send conversation text to a model. Your content capture and redaction decide what recorded content each pass can send. Trial organizations skip rule and AI trace findings. Their AI World generation still draws on the monthly AI budget.
Classifiers describes the findings, intents, request answers and trace checks these produce.
Monthly AI budget
Hue pays for the model work above and for the model judges that score runs. Each organization’s share draws on one budget per UTC calendar month: $1,000 unless Hue sets another amount for the organization. Your agent’s own model calls are not part of it, and Settings has no setting for it. When the month’s budget is used up, background AI work pauses instead of failing and resumes on its own when the next month starts or Hue raises the budget. Traces keep arriving, and you can still browse them, read rule findings and open completed results. An action you start that needs a model is refused at once withThis organization's monthly AI budget is used up.: queueing hosted judge jobs answers HTTP 402, and a case suggestion or an added fact answers 409.
The hosted judge budget reports the month as organizationBudget (organization_budget over MCP): monthlyAllowanceUsd, actualSpendUsd, reservedUsd, unresolvedUsd (held for calls whose cost is not known yet, counted in unresolvedCalls), remainingUsd, periodStart, resetsAt and paused, with amounts as decimal USD strings. Read it with get_judge_budget over MCP, GET /api/v1/judge-budget, or the SDKs’ getJudgeBudget() and get_judge_budget(). Its earlier fields keep their names, but the project’s judge allowance no longer limits spend: enabled is always true, blocked means a judge charge above its quote stopped the month until Hue reviews it, and allowanceMicroUsd minus spent and reserved is what the month has left.
Where to redact
Callbacks do not cover every OpenTelemetry field. Keep sensitive information out of span and event names, attribute keys and instrumentation names. Python user/session metadata and custom attributes also need your own producer-side policy. See the complete TypeScript and Python contracts.
TypeScript example
Install the SDK and setHUE_API_KEY to a project key: Read and write, with separate keys for development and production. Save this reusable callback as redaction.ts:
trace.ts in a package whose package.json contains "type": "module", then run node trace.ts on Node 24. It makes no model or email-provider calls.
[EMAIL]; user_123, session_456, counts, timing and trace/span IDs remain available. Replacing every address with one marker loses recipient distinctions. If your evaluations need to compare recipients, use stable aliases generated under your application’s policy instead.
Existing providers
Put the callback on the transport when supplying a transport tocreateHue. Add Hue’s processors to the provider your application already constructs; keep its other processors, resources, sampling and registration. For example, extend the existing bootstrap as follows:
provider, resource and existingProcessor represent your existing setup. If you also export logs, add hueTransport.logRecordProcessor to your existing logger provider.
Pass redact to the transport even when your own processor already redacts spans. A processor that wraps Hue’s and rewrites spans as they end misses the live-span placeholders Hue sends while a span is still running, and the resource attributes, such as process.command_args, that the provider’s resource detectors add. Hue’s callback covers both; alternatively, turn placeholders off with liveSpans: false. Follow the complete OpenTelemetry setup for lifecycle handling and production setup for fail-open initialization.
Each exporter needs its own policy. A Langfuse mask callback does not automatically scrub Hue’s copy, and Hue’s callback does not scrub Langfuse’s copy. Keep the current instrumentation and provider; see Use with Langfuse.
Python example
Installhue-run and set HUE_API_KEY to a project key: Read and write, with separate keys for development and production. Save this as trace.py and run python trace.py in that environment:
Hue(redactor=...) to its provider does not apply this callback to those external spans.
Customize the policy
Use the callback to combine text patterns with the fields your application knows are sensitive. Keep it synchronous, fast and free of network calls.- TypeScript:
pathidentifies the exported value, such asattributes.input.valueorattributes.customer.email. Return[REDACTED]for a sensitive string attribute. Helper inputs/outputs are serialized JSON strings:pathdoes not identify their nestedemailfield. For rules on nested fields, explicitly parse known JSON content fields, transform the structure, then serialize it, or sanitize a separate telemetry copy before recording it. If required parsing fails, omit or replace the content rather than returning the original sensitive value. - Python:
fieldidentifies the helper’s content field, not each nested dictionary key. Add key-based rules inside the dictionary branch when you need to replace a field such asemailregardless of its format. The example processes values only; add separate handling if your data uses sensitive dictionary keys.
Linear-time patterns
The callback runs synchronously in your serving process, on the Node.js event loop or in the Python request thread, and receives untrusted text such as user messages, model output and fetched pages. A pattern that backtracks on crafted input can block the whole process.- Bound repetition. The examples cap the part before
@at 64 characters, the longest RFC 5321 allows, so each position is checked against at most 64 characters. With an unbounded[A-Z0-9._%+-]+, a run with no@is rescanned to its end from every position: a few hundred kilobytes ofacharacters can block the process for tens of seconds. In a longer local part, which is not a valid address, the characters before the last 64 stay visible. Avoid nested or overlapping quantifiers such as(a+)+. For a guaranteed linear-time engine, usere2for Node.js orgoogle-re2for Python; both accept the example patterns. - Limit the input size. Hue’s stored-field limits do not cap what the callback receives. When uploads are available, both SDKs pass the whole helper value to the redactor, up to 1 GB and 1,048,576 values. Before running any pattern, return a fixed marker such as
[REDACTED]for a string longer than your application allows. Replace the whole value rather than redacting only a prefix. - Test adversarial input. Time the callback on a string of your maximum size made of address characters without
@, such as"a".repeat(1_000_000)or"a" * 1_000_000, and on one of half that size. Doubling the input should roughly double the time, not quadruple it, and the time must fit your request budget.
Verify redaction
Run a synthetic request through the application’s actual instrumentation, including nested messages and tool results. Inspect the Hue-bound export or the stored trace: the raw test address should be absent and[EMAIL] present, while opaque user/session IDs, numeric values, trace/span IDs and parent relationships remain intact. Check logs and any running-span placeholders too. With multiple exporters, inspect each destination separately.
Test a callback that throws. TypeScript rejects the affected export record and reports an export issue; Python omits the affected helper content field and counts an instrumentation failure. The original sensitive value must not be sent as a fallback, and application results and errors must remain unchanged. Inspect hue.transport.getReport() / getIssues() or Python’s hue.export_status; log only sanitized counters and status, never original content or callback exceptions.
A successful trace receipt confirms requested spans and field presence, not correct redaction. The examples use strict flush checks for diagnostics; use the production-safe lifecycle in serving applications. Existing stored traces are unaffected; agree retention and deletion separately.