> ## Documentation Index
> Fetch the complete documentation index at: https://pipedream.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Telemetry Export

> Send Conduit's traces, events, and metrics to OpenTelemetry collectors — for the whole instance, and per workspace.

Conduit emits OpenTelemetry signals for everything it does: a **trace** for
every request and tool call, an **event** (an OTel log record) for every
sign-in, configuration change, provisioning action and tool call, the
application's own **logs**, and aggregate **metrics**. Exporters deliver them
over OTLP to any backend that ingests it — an OpenTelemetry Collector, Datadog,
Honeycomb, Grafana, HyperDX, and the rest.

Exporters exist at two levels, and they answer different questions.

| | Instance exporters | Workspace exporters |
| - | - | - |
| Who configures them | Instance administrators, in **Settings → Instance → Telemetry** | Workspace administrators, in **Settings → Telemetry** |
| What they receive | Everything: every workspace's activity, instance administration, application logs, and metrics | The traces and events of activity **within that workspace** only |
| Protocols | gRPC, HTTP/Protobuf, HTTP/JSON | HTTP/Protobuf, HTTP/JSON |
| Endpoints | An `http` or `https` URL; the scheme picks plaintext or TLS for every protocol, gRPC included, so plaintext in-cluster collectors work | `https` endpoints only, reached through the hardened outbound client |

Exporters hot-reload: saving takes effect on every replica within moments, no
restart needed.

## What telemetry contains

Telemetry describes what Conduit did, not what your members' tools said.

* **People are identified by id.** Spans and events carry `user.id`, never an
  email address or a display name. The audit log is where the actor's email
  lives, and each audit entry carries
  `actor_user_id`, `trace_id` and `span_id` to join it to telemetry. Client IP
  (`client.address`) and user agent (`user_agent.original`, capped at 256
  bytes) remain, as the source signal for security events.
* **Names a workspace gives its own settings** — its policies, groups,
  identity providers, SCIM tokens and API clients — are sent only to that
  workspace's exporters. Instance exporters get their ids, since they are
  the operator's backend. Names of instance-level settings (instance
  identity providers, workspace assignment rules) go to instance exporters.
* **Connector names and verified domains go to every exporter**, instance
  exporters included. Traces, events and the `connector` label on metrics
  use connector names, and so do tool and prompt names, which begin with
  their connector's name. The event recording a workspace domain change
  carries the domain. Don't put anything in a connector name that the
  operator's backend shouldn't hold.
* **Never included:** tool call arguments and results, prompt arguments,
  completion input, resource URIs (a resource read carries only the URI's
  scheme), the bodies of upstream requests and responses, and credentials.
  Connector URLs appear as `scheme://host` only, since a URL can carry a key
  in its path or query.
* **Errors from member calls** — a tool call, prompt fetch, resource read or
  completion that failed — carry a stable `error.type` (listed below) plus
  the HTTP status and JSON-RPC code, never the upstream's message, which can
  repeat what the member sent.
  The member who made the call always sees the full message in their client.
  For a failed tool call, the message is also kept in Conduit's own
  database, and administrators see it among the recent tool calls on the
  **Usage** page. For a failed prompt fetch or resource read, the first 512
  bytes of the message are also kept in Conduit's database (a resource
  read's URI too), though no page shows them yet. A failed completion's
  message isn't kept.
* **Errors from connection-level requests** — listing a connector's tools,
  the MCP handshake, token exchanges — keep the first 256 bytes of the
  upstream's message in application logs and span status, because that text
  describes the integration (a revoked credential, a wrong URL) and is what
  an operator needs to fix it. Workspace exporters never receive it.
* **Bounded text:** every string attribute is valid UTF-8 and capped — 1 KiB
  on events and application logs, 16 KiB for stack traces, and 16,384
  characters for any span attribute — so a single malformed or oversized
  value can never cause a collector to reject a whole batch.

These rules are the same for every deployment, whether you run Conduit
yourself or use Conduit Cloud; there is no setting that loosens them. To
strip more before data reaches your backend (for example, hashing IP
addresses), use the OpenTelemetry Collector's redaction processor.

### Where each kind of data lives

| Data | Telemetry export | Audit log | Usage |
| - | - | - | - |
| Who acted | `user.id` | Email, plus `actor_user_id` | User id (email shown at read time) |
| Client IP and user agent | Yes (user agent capped at 256 bytes) | IP on every entry; user agent on sign-ins | No |
| Names of a workspace's policies, groups, identity providers | Ids on instance exporters; names too on the workspace's own | Yes, in each entry's details | No |
| Tool arguments and results | No | No | No |
| Error message of a failed tool call | No — an error class and codes | No | Up to 512 bytes, visible to admins |
| Error message of a failed prompt fetch or resource read | No — an error class and codes | No | Up to 512 bytes, not shown yet |
| Error message of a failed completion | No — an error class and codes | No | No |
| Credentials | No | No | No |
| Kept for | Your backend's retention | Indefinitely | Raw rows 30 days, aggregates 365 days (see [reference](/docs/conduit/configure/reference)) |

### Error types on MCP events

The `conduit.mcp.tool.called`, `conduit.mcp.prompt.fetched` and
`conduit.mcp.resource.read` events carry `error.type` when the call failed:

| `error.type` | Meaning |
| - | - |
| `auth_required` | The member hasn't connected their account to the connector, or the connector rejected the saved one; they were sent a link to connect |
| `connect_placeholder` | The member called a connector's `authenticate` tool, which answers with that link |
| `upstream_unauthorized` | The connector refused the credential and the member can't replace it themselves |
| `credential_unavailable` | Conduit couldn't obtain a credential it's responsible for (for example, a token endpoint was unreachable) |
| `upstream_http_4xx`, `upstream_http_5xx` | The connector answered with an HTTP error |
| `upstream_rpc_error` | The connector answered with a JSON-RPC error |
| `upstream_session_not_found` | The connector no longer knew the MCP session |
| `tool_error` | The tool ran and reported a failure in its result |
| `denied_policy` | Access policy doesn't allow the call |
| `denied_oauth_scope` | The OAuth grant of the client the member called through doesn't cover the call |
| `unknown_connector`, `unknown_tool`, `ambiguous_tool` | The name called doesn't route to exactly one tool |
| `builtin_unavailable` | Conduit's own tools weren't available to answer the call |
| `not_found`, `ambiguous_resource`, `resource_resolution_incomplete` | Conduit couldn't resolve the resource to exactly one connector |
| `blocked` | Conduit's outbound guard refused the address (for example, a private network address) |
| `response_too_large` | The connector's answer exceeded Conduit's size limit |
| `timeout`, `canceled` | The call ran out of time, or the client abandoned it |
| `transport` | The connection to the connector failed (DNS, TCP, TLS) |
| `gateway_error` | Any other failure inside Conduit |

Every audit entry carries `actor_user_id`, `trace_id` and `span_id`, so a
span or event in your backend leads to the audit entry behind it without the
telemetry itself holding an email address.

## Instance exporters

An instance exporter is the operator's view of the deployment. It receives
every signal the process produces, so it is the right place for the backend
your platform team watches.

Add one in **Settings → Instance → Telemetry** with a name, a protocol, an
endpoint, and optional headers (typically an API key for a hosted backend).
Header values are write-only: once saved they are never shown again, and
editing an exporter without re-entering a value keeps the stored one — unless
the edit changes the exporter's endpoint or protocol, which drops the stored
headers, so re-enter them for the new collector. The
**service name** field sets the `service.name` resource attribute on
everything sent to instance exporters (default `conduit`); the rest of the
deployment's resource comes from the standard `OTEL_RESOURCE_ATTRIBUTES`
variable.

Exporters can also come from the environment. When the standard
`OTEL_EXPORTER_OTLP_*` variables are set, Conduit configures an exporter from
them at startup and shows it, read-only, alongside the ones configured in the
UI. This is the recommended way to reach a node-local agent or sidecar, whose
address belongs to the deployment rather than to the database:

| Deployment | `OTEL_EXPORTER_OTLP_ENDPOINT` | Notes |
| - | - | - |
| Kubernetes node-local agent | `http://$(HOST_IP):4317` | `HOST_IP` from the downward API (`status.hostIP`); set `OTEL_EXPORTER_OTLP_PROTOCOL=grpc` |
| Sidecar or host agent | `http://localhost:4317` | loopback, plaintext |
| In-cluster gateway | `http://otel-collector:4318` | stable DNS |
| Hosted OTLP (SaaS) | `https://otlp.vendor.example` | TLS via `https`; auth via `OTEL_EXPORTER_OTLP_HEADERS` from a secret |

Conduit honors the standard variables directly: the generic and per-signal
endpoint variables, `OTEL_EXPORTER_OTLP_PROTOCOL` (default `http/protobuf`),
`OTEL_EXPORTER_OTLP_HEADERS`, and the TLS, timeout, and compression variables.
See the [OTLP exporter specification](https://opentelemetry.io/docs/specs/otel/protocol/exporter/).
The exporter's display name comes from `CONDUIT_OTEL_EXPORTER_NAME`.

## Workspace exporters

A workspace exporter is a tenant's view of their own activity. In a
multi-workspace deployment it lets each workspace's team send their telemetry
to their own backend without the operator forwarding and filtering it for them.

A workspace's exporters receive:

* **Traces** of every request made within the workspace: its members' MCP
  traffic and tool calls (including the upstream calls those make), its
  administrators' settings changes, and SCIM provisioning pushed by its
  identity provider.
* **Events** produced by those requests — the same records that appear in the
  workspace's audit log, plus the tool-call and policy-denial events that
  don't.

They never receive activity from another workspace, or instance
administration — a change an instance administrator makes from the instance
console (a rename, a member removed there) is the operator's activity, not the
workspace's, and an instance administration tool invoked over the workspace's
MCP endpoint sends only the tool call itself, not what the tool did. Metrics
are aggregates over the whole instance with no per-workspace dimension, and
Conduit's own application logs are the operator's diagnostics; both go to
instance exporters only.

A span's failure detail is redacted for a workspace: the error status and the
stable error type stay, while the error message and stack trace — which can
name internal hosts and paths — are dropped. In the other direction, events
about the workspace's own settings carry their names (`policy.name`,
`group.name`, `idp.name`, `scim_token.label`, `api_client.name`,
`scim.idp_name`) on the workspace's copy only.

**Resource attributes.** Everything sent to a workspace's exporters carries the
OpenTelemetry resource attributes the workspace sets in **Settings →
Telemetry** — for example `service.name` or `deployment.environment` — and
`service.name` defaults to `conduit` when the workspace sets none. The
deployment's own resource, including anything in `OTEL_RESOURCE_ATTRIBUTES`,
describes the operator's installation and goes to instance exporters only.

Every span and log record a workspace exporter receives carries the attribute
`conduit.workspace.id` naming the workspace. Instance exporters see the same
attribute on workspace-scoped signals, which makes it the key to filter an
instance backend by tenant.

Workspace administrators add exporters in **Settings → Telemetry** (workspace
settings), with the same name, protocol, endpoint, and write-only headers as
instance exporters. Renaming keeps stored headers, while changing the protocol
or endpoint clears them so credentials are never carried to a different
collector. Workspace exporters have two restrictions that follow from the endpoint
being configured by a tenant rather than the operator:

* The endpoint must be an `https` URL (no plaintext, no private or internal
  address), using HTTP/Protobuf or HTTP/JSON. Traces are sent to `/v1/traces`
  and events to `/v1/logs` under the endpoint you give, so either the base URL
  or a signal path works. A workspace may have one exporter.
* Export requests go through the same hardened outbound client as connectors:
  a hostname that resolves to a private or internal address is refused unless
  the operator has listed it in `CONDUIT_SAFEHTTP_ALLOWED_PRIVATE_HOSTS`. An
  operator who wants workspaces to share an in-cluster collector lists its
  hostname there; see [Network Access](/docs/conduit/deploy/network).

A workspace exporter that cannot be reached backs up only its own queue: it
never delays a tool call, and never affects the instance's exporters or
another workspace's. Conduit creates a workspace's export pipeline when that
workspace first produces telemetry. Each replica keeps the 64 most recently
used workspace pipelines active; activating another flushes and recycles the
least recently used pipeline without removing its configuration. Sustained
cache churn produces a warning in the instance log, while occasional eviction
of a cold pipeline is silent.

In a single-workspace deployment the one workspace is the instance, so this
page is not shown; configure telemetry on the instance page, where the
deployment's resource attributes apply.

## Where each signal lands

| Signal | Instance exporters | Workspace exporters |
| - | - | - |
| Traces | All | Requests within the workspace |
| Events | All | Requests within the workspace |
| Application logs | All | — |
| Metrics | All (also on the [Prometheus endpoint](/docs/conduit/deploy/monitoring)) | — |

Traces go to `<endpoint>/v1/traces`, logs and events to `<endpoint>/v1/logs`,
and metrics to `<endpoint>/v1/metrics`. Configure the endpoint as the base URL
or with any of those paths; all work.

## When an export fails

A failed export is reported in Conduit's console log on a line starting with
`otel:`, carrying what the collector said (a timeout, a refused connection, an
unknown service). A failure that recurs on every batch — a collector that is
down — is reported once, then once a minute with the number of repeats since
the previous line, for as long as it lasts.

A collector that does not serve one of the signals is handled separately. Some
backends accept traces but have no receiver for logs or metrics; over gRPC
such a collector answers `Unimplemented`, and the batch cannot be delivered by
retrying. Conduit reports that once, naming the exporter and the signal, then
stops sending that signal to that exporter and probes it with one batch every
five minutes. When the collector starts serving the signal — after you add the
receiver and restart it — Conduit reports that it is resuming and sends
normally again. The exporter's other signals are unaffected throughout. Over
HTTP a collector reports a missing receiver as a plain error, which is logged
like any other failure.
