Skip to main content
Broadcast sends a trace of each gateway request to destinations you choose, without changing your code. The trace follows the OpenTelemetry GenAI conventions: one span for the request, and one span for each provider attempt, with the model, provider, token usage, cost, timing, and (unless you turn it off) the prompt and output. Broadcast is free. Owners and admins manage destinations in Settings → Broadcast, or with the observability destinations API and a management key with settings:write. A workspace can have up to five destinations of each type.

Add a destination

  1. Open Settings → Broadcast and choose a destination.
  2. Enter its credentials. They are encrypted before storage and never shown again.
  3. Choose what to send (see Choose what is sent).
  4. Select Test Connection. Phaseo sends a sample trace the way real deliveries are sent, so a failure here shows the destination’s own error.
  5. Save. Requests that finish after this are sent within about a minute.
Each form asks only for what that tool needs, such as an API key and a project or region. OpenTelemetry tools receive the same spans; Phaseo fills in each vendor’s endpoint, headers, and encoding.

Choose what is sent

Each destination has its own settings:
  • Privacy Mode leaves prompts, outputs, system instructions, and tool definitions out of every trace. Requests made with an API key that never stores content are never forwarded with content, whatever this setting says.
  • Generation Metadata, Cost, Identity, and Request Context each turn a group of span attributes on or off. Turning off Request Context also drops request metadata.
  • Rate sends a share of eligible requests, from 0 to 1. The choice is stable for each request and destination, so a retried delivery is never sampled differently.
  • Filter by API key includes or excludes requests made with specific keys.
  • Filter Rules send only requests that match. Rules compare fields such as model, provider, user ID, finish reason, cost, or token counts, and can be grouped with and and or. Matches regex uses a linear-time engine: back-references and lookaround are not supported, and a pattern that is too large is rejected when you save it.

Add your own trace context

Put a metadata object in the request body to name traces and attach your own fields. Phaseo reads it for Broadcast:
Values can be strings, numbers, or booleans; nested objects are skipped. Phaseo keeps up to 64 keys of at most 64 characters, and cuts values at 512 characters. A W3C traceparent header, when present, decides the trace instead of trace_id and parent_span_id. Do not put secrets or personal data in metadata: it is sent to every destination that has Request Context on.

Webhook deliveries

A webhook receives one POST (or PUT, if you choose it) per request. The body is an OTLP/JSON export, {"resourceSpans": [...]}, holding that request’s spans. Use Headers JSON to add your own headers, such as an Authorization header your endpoint expects. Add a Signing Secret (16 to 256 characters, no spaces) to have every delivery, test, and sample signed. Phaseo then adds two headers:
x-phaseo-signature is the hexadecimal HMAC-SHA256 of the timestamp, a period (.), and the unmodified request body, keyed with your secret. This is the same scheme as notification webhooks, so you can reuse their verification code. Broadcast deliveries have no event ID header: deduplicate on the span IDs in the body. Test Connection sends {"resourceSpans": []} with an X-Test-Connection: true header. A 400 response counts as reachable, so a receiver that rejects empty exports still passes.

Object storage and warehouse rows

Object storage, ClickHouse, BigQuery, and Snowflake receive one row per span instead of traces. Each batch is written in one request: one object, or one insert. A row is at most 512 KB. When a span carries more, such as an inline image, its largest attribute values are cut to share the space, each keeping its start and ending in …[truncated], and their names are listed in the phaseo.truncated_attributes attribute. A batch that fails is retried. If the destination saved it but the response was lost, the retry can write some rows twice; use event_id and span_id to deduplicate. BigQuery and Snowflake use request IDs that prevent most of these duplicates.

Object storage

Each batch is a gzipped JSON lines file. Path Template sets its key, by default phaseo/{year}/{month}/{day}/{hour}/{uuid}, and Phaseo appends .jsonl.gz. The template can use {year}, {month}, {day}, {hour}, {date} (YYYY-MM-DD), and {uuid}, and must include {uuid} so files never overwrite each other. Times are UTC.
  • Amazon S3: an access key that can s3:PutObject on the bucket, and the bucket’s region. For an S3-compatible store, set S3-Compatible Endpoint.
  • Cloudflare R2: an R2 API token’s access key ID and secret, your account ID, and the jurisdiction if the bucket is in one (eu or fedramp).
  • Google Cloud Storage: an HMAC key for a service account that can create objects in the bucket.
Objects are stored compressed, without a Content-Encoding header, so Athena, BigQuery, and Snowflake external tables read them by their .gz extension.

ClickHouse

Rows are inserted as JSONEachRow through the HTTPS interface, with asynchronous inserts. Columns the table does not have are skipped, so you can keep only the ones you need:
Use the endpoint on port 8443 for ClickHouse Cloud. attributes and resource_attributes can also be JSON columns.

BigQuery

Rows are streamed with insertAll, using the event and span IDs as insert IDs. Create a service account with BigQuery Data Editor on the table, then paste its JSON key file into the form. attributes and resource_attributes must be JSON columns:

Snowflake

Each batch is one INSERT through the SQL API, signed in with key-pair authentication, so it runs on the warehouse you name. Create a user for Phaseo and give it an RSA key:
Paste the private key (BEGIN PRIVATE KEY, unencrypted) into the form, and use the org-account identifier, such as myorg-myaccount. The user’s role needs INSERT on the table and USAGE on the warehouse, database, and schema.

Delivery, health, and alerts

Deliveries that fail with a timeout, a rate limit, or a server error are retried with backoff, up to eight attempts. Other errors, such as a rejected key or a missing table, are not retried. Settings → Broadcast shows each destination’s health: Healthy, Failing, No deliveries yet, or Disabled, with the last error and the last 24 hours of delivered and failed requests. To be told when a destination stops working, route Broadcast Alerts to a notification destination. An alert is sent when a destination has failed at least five requests in a row and has delivered nothing for 15 minutes. It is sent once per failing streak, and again only after the destination has recovered and at least six hours have passed.
Last modified on October 23, 2026