OpenTelemetry for custom agents
This page is for an agent connecting a custom agent, framework or service that already emits OpenTelemetry. It gives the endpoint, the authentication header, the attributes Zaun reads, the limits and the proof to show. If you are the person, this path observes and alerts; to block, have your agent call the API scan before it acts.
Point the exporter at Zaun#
Zaun accepts OTLP over HTTP; gRPC is not accepted. Most OpenTelemetry SDKs read these standard variables:
export OTEL_EXPORTER_OTLP_ENDPOINT='https://api.zaun.ai/v1/ingest/otlp'
export OTEL_EXPORTER_OTLP_PROTOCOL='http/protobuf'
export OTEL_EXPORTER_OTLP_HEADERS='x-zaun-ingest-key=<INGEST_KEY>'
<INGEST_KEY>is an ingest key for a generic OpenTelemetry source. It can only submit events for one project, and it cannot scan or read results. Treat it like an error-reporting key: low risk if read, cheap to rotate. Still keep it out of source control.- The SDK appends the signal path, so logs go to
/v1/ingest/otlp/v1/logsand traces to/v1/ingest/otlp/v1/traces. An exporter that needs a full URL per signal takes those. - Send
application/x-protobuforapplication/json, optionally gzipped. Metrics are accepted and discarded, so an exporter with metrics on does not fail. Authorization: Bearer <INGEST_KEY>also works. Thex-zaun-ingest-keyheader avoids the space, which exporters that parse header strings askey=valuepairs handle differently.
What Zaun reads#
Zaun reads the OpenTelemetry generative AI conventions on log records and on span events. Put the attributes there:
| Attribute | Use |
|---|---|
gen_ai.input.messages | Assessed by Hex v1.3 as the messages sent to the model |
gen_ai.output.messages | Assessed as the model's reply |
gen_ai.tool.call.arguments | Assessed as a tool call's arguments |
gen_ai.tool.call.result | Assessed as a tool's result |
gen_ai.conversation.id | Groups events into a session; not assessed |
Without the message attributes, Zaun sees that calls happened but has nothing to assess. Message content capture is often off by default in instrumentation libraries; turn it on.
If the exporter reports a user (user.email, enduser.id, user.name or process.owner),
Zaun removes it from the event and keeps only a hash. The hash is made with a key only Zaun
holds, derived per organization, so it cannot be computed from an email outside Zaun and the
same person has a different hash in every organization. To find a user's results, pass
their email to the violations filter, principal=dev@example.com; Zaun hashes it for you.
Limits#
| Limit | Value | Over the limit |
|---|---|---|
| Compressed body | 4 MiB | 413 |
| Decompressed body | 32 MiB, and at most 10 times the compressed size | 413 |
| Records per request | 10,000 | 413 |
| Attribute nesting | 8 levels | 413 |
| Attribute key | 64 KiB | 413 |
| Attribute value or log body | 64 KiB | Accepted: the start and end of the value are kept (32 KiB each) and the event is marked as partially captured |
| Event time | No more than 5 minutes in the future, and inside the project's retention | The event is rejected |
| Volume | The project's and organization's request, byte and daily event budgets, and the key's daily spend cap | 429 with Retry-After |
Telemetry has its own budgets, so a busy exporter does not slow down your API scans. Its scans spend the same credit as API scans.
The proof to show#
The source's health report in the console shows the state (waiting, receiving or degraded), the first and last event times, event counts by name, whether message content is present, how many conversation turns were scanned, and each rejection with its reason and fix. Start a connectivity check, send an event that contains its code, and show the person the check marked satisfied.
Maintained with the API contract · Defaults may vary by deployment.