Connecting to Bifrost

⚠️

Bifrost support is in beta. Validate end to end that your usage and cost data appear correctly in CloudZero before relying on this integration. Some attribution may not yet flow for every field.

Connect your Bifrost gateway to CloudZero to bring AI inference telemetry into a unified view of all your cloud and AI spend. Once configured, your gateway sends model usage, token counts, and cost per request to CloudZero, where it appears in AI Signals within a few minutes of gateway activity. From there, CloudZero organizes your AI costs, along with your other costs, into the categories (called Dimensions) that matter most to your business, such as team, product, feature, or customer.

Bifrost sends telemetry to CloudZero's v2 telemetry endpoint using the OpenTelemetry (OTel) plugin built into the gateway.

ℹ️

This integration does not use the Create Connection flow in Settings > Cloud Connections. You connect it by creating a CloudZero API key and pointing telemetry at CloudZero, as described below. Confirm it is working in AI Signals once data flows.

What you need

  • A CloudZero user with permission to create API keys
  • A Bifrost gateway you control, version v1.4.5 Enterprise / v1.5.6 OSS or later. Earlier versions do not emit the current OTel GenAI semantic conventions and are not supported. Upgrade before you begin.
  • Outbound HTTPS connectivity from your gateway host to telemetry.cloudzero.com
ℹ️

Install this integration on a Bifrost gateway with consistent, production-level traffic. Steady traffic makes it much easier to confirm data is flowing and to interpret totals. If you run multiple production gateways, start with the highest-volume one.

Step 1: Create a CloudZero API key

  1. Sign in to CloudZero and go to Settings > API Keys.
  2. Create a new API key.
  3. Under Scopes, open the Ai-Telemetry-Ingest category and select ingest-bifrost-v1. You can also type bifrost in the scope search box to find it.
  4. Copy the key value and store it securely. You reference it in Step 2. Treat it like any other secret.

The ai-telemetry-ingest:ingest-bifrost-v1 scope authorizes the Bifrost ingest endpoint only. If a request later returns 403, the key is missing this scope.

Step 2: Configure your Bifrost gateway

There are two things to configure:

  1. Add the otel plugin block to your Bifrost config file (bifrost.json or equivalent).
  2. Set the CloudZero API key as an environment variable on the gateway process.

Keep the API key out of the config file by referencing it through Bifrost's env.VAR_NAME substitution syntax. The required settings are the same regardless of how your gateway is deployed. What changes is where you put them.

Required settings

These are the settings for direct export, where the Bifrost OTel plugin posts straight to CloudZero. If you already run an OpenTelemetry Collector, see Export through an OpenTelemetry Collector for the alternative.

#SettingWhere it goesValue
1OTel plugin enabledbifrost.json, inside the plugins array"enabled": true, "name": "otel"
2OTLP traces endpointcollector_url in the OTel plugin confighttps://telemetry.cloudzero.com/v2/telemetry/bifrost. Use the full route verbatim. Do not strip the path or expect Bifrost to append /v1/traces.
3OTLP protocolprotocol in the OTel plugin config"http" (not "grpc"). CloudZero's ingest does not accept gRPC.
4Authorization headerheaders in the OTel plugin config"Authorization": "env.CZ_API_KEY", where CZ_API_KEY is an environment variable holding your raw CloudZero API key (no Bearer prefix).
5Service nameservice_name in the OTel plugin configA meaningful identifier for this gateway, for example bifrost-prod. This is how CloudZero distinguishes your gateways in AI Signals. Two gateways with the same value are reported as one service.
6Trace typetrace_type in the OTel plugin config"genai_extension"
7Cloud account and regionEnvironment variable OTEL_RESOURCE_ATTRIBUTES on the gateway processcloud.account.id=<your-account-id>,cloud.region=<region>. CloudZero uses these to join your Bifrost telemetry against the matching cloud billing data. Without them, model costs cannot be reconciled against the account and region they landed in.
ℹ️

Set service_name in the plugin config block (row 5) as the primary source of truth. In some Bifrost versions it takes precedence over the OTEL_SERVICE_NAME environment variable. If you also set OTEL_SERVICE_NAME, confirm the two values match.

Config file and environment variable (most common)

Set the environment variables on your gateway process:

CZ_API_KEY=<your-cloudzero-api-key>
OTEL_RESOURCE_ATTRIBUTES=cloud.account.id=<your-account-id>,cloud.region=<region>

Add the otel plugin block to bifrost.json:

{
  "plugins": [
    {
      "enabled": true,
      "name": "otel",
      "config": {
        "service_name": "bifrost-prod",
        "collector_url": "https://telemetry.cloudzero.com/v2/telemetry/bifrost",
        "trace_type": "genai_extension",
        "protocol": "http",
        "headers": {
          "Authorization": "env.CZ_API_KEY"
        }
      }
    }
  ]
}

Docker Compose

Mount the same bifrost.json shown above and set the environment variables in your compose.yml:

services:
  bifrost:
    image: maximhq/bifrost:latest
    volumes:
      - ./bifrost.json:/app/config.json
    environment:
      - CZ_API_KEY=<your-cloudzero-api-key>
      - OTEL_RESOURCE_ATTRIBUTES=cloud.account.id=<your-account-id>,cloud.region=<region>

Kubernetes and Helm

The same settings apply for Kubernetes deployments: mount bifrost.json from a ConfigMap and set CZ_API_KEY through a Kubernetes Secret. You can set OTEL_RESOURCE_ATTRIBUTES directly in the pod spec or reference it from a ConfigMap.

Export through an OpenTelemetry Collector

If you already operate an OpenTelemetry Collector (common in container and Kubernetes fleets), you can point Bifrost at your local collector and let the collector forward to CloudZero. With this topology, Bifrost never holds the CloudZero API key.

In bifrost.json, point the plugin at the local collector and omit the Authorization header. This URL does end in /v1/traces because it targets the collector's standard OTLP receiver, not the CloudZero route:

{
  "plugins": [
    {
      "enabled": true,
      "name": "otel",
      "config": {
        "service_name": "bifrost-prod",
        "collector_url": "http://localhost:4318/v1/traces",
        "trace_type": "genai_extension",
        "protocol": "http"
      }
    }
  ]
}

In the collector config, the CloudZero route and API key live in the exporter. Use the otlphttp exporter's traces_endpoint (not endpoint): traces_endpoint sends the route verbatim, whereas endpoint would append /v1/traces and miss the v2 route, returning a 403 or 404.

receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318
processors:
  resource:
    attributes:
      - key: cloud.account.id
        value: <your-account-id>
        action: insert
      - key: cloud.region
        value: <region>
        action: insert
  batch:
    timeout: 5s
exporters:
  otlphttp/cz:
    traces_endpoint: https://telemetry.cloudzero.com/v2/telemetry/bifrost
    headers:
      Authorization: ${env:CZ_API_KEY}   # raw CloudZero key, no "Bearer" prefix
    sending_queue:
      enabled: true
    retry_on_failure:
      enabled: true
service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [resource, batch]
      exporters: [otlphttp/cz]

With this topology, the collector's resource processor stamps the cloud.account.id and cloud.region attributes, so you do not need the OTEL_RESOURCE_ATTRIBUTES environment variable on the Bifrost process. Set CZ_API_KEY on the collector process instead.

Step 3: Restart and verify

  1. Restart your Bifrost gateway so the new config and environment variables take effect. If you run a collector, restart it too.
  2. Confirm that normal request traffic is hitting the gateway.
  3. Open CloudZero AI Signals and watch for activity from your service_name.

You should see activity in AI Signals within a few minutes of the gateway receiving requests. If nothing appears after five minutes of confirmed gateway traffic, work through Troubleshooting.

Recommended: redact prompt and response content at the source

By default, Bifrost includes full prompt and response content in its OTel spans, and CloudZero ingests whatever is sent. If you have data residency or compliance requirements around what content leaves your gateway, turn this off in Bifrost so the content is never exported. None of the Dimensions CloudZero uses for cost allocation or chargeback depend on it. Token counts, model, provider, cost, and virtual key attribution are all preserved.

Bifrost exposes a disable_content_logging flag. When enabled, Bifrost emits only usage metadata (latency, cost, token counts) and omits request and response bodies, including system prompts and tool payloads.

Set it in bifrost.json as a top-level client block, alongside the plugins array:

{
  "client": {
    "disable_content_logging": true
  },
  "plugins": [
    {
      "enabled": true,
      "name": "otel",
      "config": { }
    }
  ]
}

Or set it at runtime through the Bifrost config API:

curl -X PUT http://localhost:8080/api/config \
  -H "Content-Type: application/json" \
  -d '{"client_config": {"disable_content_logging": true}}'
⚠️

Set this before your first restart if content must never leave your environment. Once a span is exported with content included, that content has already reached CloudZero. Enabling the flag stops future spans; it does not retroactively remove content already sent.

Recommended: tag virtual keys for per-team attribution

If your team uses Bifrost virtual keys (one key per user, application, team, or tenant), configure the attribution fields on each key. CloudZero uses these fields to break down usage inside AI Signals. Without them, all traffic from a given model collapses into a single bucket and you lose per-team and per-application visibility.

Bifrost virtual keys use the format sk-bf-* and can be passed in requests through the x-bf-vk, Authorization, x-api-key, or x-goog-api-key headers.

Attribution toWhat to setWhere
A team, project, or customerTeam or customer attachment on the keyVirtual keys can be attached to a team or a customer in Bifrost's governance model
A specific userPer-user identity (requires Bifrost Enterprise)Per-user attribution is an Enterprise capability, not a field you set on the key. On the OSS build there is no per-user attribution.
ℹ️

Virtual key attachment is mutually exclusive in Bifrost. A key can be attached to a team or a customer, not both. Design your key structure before provisioning at scale.

Per-user attribution requires Bifrost Enterprise. Attributing traffic to an individual end user relies on Bifrost's authenticated-user machinery, which is populated only by the Enterprise auth layer. On the OSS build that identity is never set, and request headers are not a substitute. If you need per-user breakdowns, deploy Bifrost Enterprise. Team-level and customer-level attribution work on the OSS build through the virtual key configuration above.

For more detail on Bifrost's virtual key model and budget controls, see the Bifrost Virtual Keys documentation.

What to expect

After you configure the gateway, your Bifrost costs work like any other cost data in CloudZero. You can explore them in the Explorer, organize them by team or product using Dimensions, track trends in Dashboards, set Budgets, or ask questions in the AI Hub.

For each LLM request that flows through your gateway, CloudZero receives:

  • Timestamp and latency
  • Model name and provider
  • Input, output, and cache token counts
  • Cost as calculated by Bifrost
  • Virtual key and team or customer attribution, if configured (per-user attribution requires Bifrost Enterprise)
  • Success or error status

By default, CloudZero also receives prompt content and message bodies, system prompts, tool definitions and call payloads, and response content. To suppress these, set disable_content_logging as described in Redact prompt and response content.

Network requirements

The component that exports to CloudZero (the Bifrost gateway for direct export, or your OTel Collector for a sidecar) must be able to make HTTPS requests to telemetry.cloudzero.com on port 443. CloudZero does not publish a static IP range for this endpoint. No inbound connections from CloudZero are required.

Troubleshooting

If data is not appearing in AI Signals five minutes after confirmed gateway traffic, work through these in order.

Common misconfigurations

  1. Wrong protocol. "protocol": "grpc" fails silently. It must be "http". CloudZero's ingest does not accept gRPC.
  2. Bearer prefix on the API key. The Authorization header value must resolve to the raw key, not Bearer <key>.
  3. Incorrect endpoint. The route must be the full path https://telemetry.cloudzero.com/v2/telemetry/bifrost, used verbatim. Do not shorten it to the base URL and do not append /v1/traces; the route is matched exactly. A wrong value returns 403 or 404 and nothing reaches AI Signals. For direct export this is the Bifrost plugin's collector_url. With a collector sidecar it is the collector's exporter, where you should use the otlphttp exporter's traces_endpoint (not endpoint). In the sidecar case, Bifrost's own collector_url points at the local collector and legitimately ends in /v1/traces.
  4. CZ_API_KEY not set or not in scope. Bifrost (or the collector) resolves the environment variable at startup. If it is missing, the header is sent empty and CloudZero rejects the request. Confirm the variable is set on the process that exports to CloudZero, not just your shell.
  5. Gateway not restarted after a config change. Bifrost reads its config at startup. Restart the gateway (and the collector, if you run one) after any change to bifrost.json, the collector config, or environment variables.

Check your gateway logs for OTel delivery errors

If the config looks correct and data still is not flowing, tail your Bifrost gateway logs (and your collector logs, if you run a sidecar). OTel exporter failures surface there, including network errors, auth rejections, and bad endpoints.

# Docker / Compose
docker logs -f <bifrost-container>

# Kubernetes
kubectl logs -f <bifrost-pod>

# Systemd or bare process
journalctl -u <bifrost-service> -f

Look for messages from the OTel exporter around the time you sent a test request.

ℹ️

Have questions or feedback? Reach out to your account manager.


Did this page help you?