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

# Tracing from Vercel into Other Services

> Follow one request from a Vercel function into services that run elsewhere, such as an API on AWS or Kubernetes, with W3C trace context and OpenTelemetry, in one trace in Bronto.

Most Vercel projects call something that doesn't run on Vercel: a backend API, a database, a queue, a payment provider. Vercel's own trace view stops at the outgoing call. It shows one `fetch` span and how long it took, but nothing about what happened inside the other service. Vercel also can't receive telemetry from services that run elsewhere.

To see the whole request, both sides send their spans to Bronto, and every call from Vercel carries the [W3C `traceparent` header](https://www.w3.org/TR/trace-context/). The other service continues that trace, and Bronto shows one trace from the Vercel edge to the database behind your backend.

```
Vercel                         elsewhere (AWS, Kubernetes, …)
edge → function → fetch ──traceparent──▶ your API → database
          │                                 │
          └── drain / OTLP ──▶ Bronto ◀── OTLP
```

## When to use this

* Your Vercel functions call an API, service or database that you run yourself somewhere else.
* You want to see where the time goes in a slow request, including inside the other service.
* You want logs from both sides to link to the same trace.

For telemetry that only covers Vercel itself, see [Vercel Drains](/integrations/vercel-drains.md) and [OpenTelemetry in Vercel Functions](/integrations/vercel-otel.md).

## How the trace is connected

Three things have to be true:

1. **The Vercel side records spans and sends them to Bronto**, through a trace drain or an OTLP exporter in your code.
2. **Every outgoing call carries `traceparent`.** It holds the trace ID and the ID of the calling span.
3. **The other service continues the trace and sends its spans to Bronto.** Any OpenTelemetry SDK or agent reads `traceparent` from incoming requests automatically.

## Step 1: Propagate trace context from Vercel

### Next.js and other frameworks with `@vercel/otel`

`@vercel/otel` instruments `fetch`, but by default it adds `traceparent` only to your deployment's own URLs. List the services you call in `propagateContextUrls`:

```ts instrumentation.ts theme={"dark"}
import { registerOTel } from "@vercel/otel";

export function register() {
  registerOTel({
    serviceName: "my-app",
    instrumentationConfig: {
      fetch: {
        // Origins (or regexes) that receive the trace context
        propagateContextUrls: ["https://api.example.com"],
      },
    },
  });
}
```

With this in place, each `fetch` to `https://api.example.com` gets a client span and a `traceparent` header. The app's spans share one trace with Vercel's own spans for the request (`Resolve Route`, `Invoke Middleware`, `Invoke Function`).

### Agents built with eve

eve takes OpenTelemetry instrumentations in `agent/instrumentation/otel.ts`. The [undici instrumentation](https://www.npmjs.com/package/@opentelemetry/instrumentation-undici) traces every `fetch` your tools make and adds `traceparent` to it:

```bash theme={"dark"}
npm install @opentelemetry/instrumentation-undici@0.33
```

```ts agent/instrumentation/otel.ts theme={"dark"}
import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
import { otel } from "eve/instrumentation/otel";

export default otel({
  instrumentations: [new UndiciInstrumentation()],
});
```

Each `fetch` from a tool then gets an HTTP client span, following the [HTTP semantic conventions](https://opentelemetry.io/docs/specs/semconv/http/http-spans/), under the tool's `execute_tool` span, and the service it calls continues the trace. Version 0.33 matches the OpenTelemetry JS 2.x packages that `@vercel/otel` 2 uses. The instrumentation adds `traceparent` to every outgoing request; to leave some out, for example calls to third-party APIs, return `true` for them from its `ignoreRequestHook` option.

### Other HTTP clients and workers

For calls that don't go through `fetch`, use the OpenTelemetry instrumentation for that library, if there is one: the [OpenTelemetry registry](https://opentelemetry.io/ecosystem/registry/?language=js\&component=instrumentation) lists them. Otherwise, add the trace context of the active span to the request's headers with the OpenTelemetry API:

```ts theme={"dark"}
import { context, propagation } from "@opentelemetry/api";

const headers: Record<string, string> = {};
propagation.inject(context.active(), headers); // adds traceparent
```

> [!NOTE]
>
> With [eve](https://eve.dev) agents, each agent turn starts a new trace. A conversation of three turns is three traces. Tool calls inside a turn carry that turn's trace ID, so the turn and everything its tools call stay together.

## Step 2: Send spans from the other service

Instrument the other service with OpenTelemetry and point it at Bronto. Every OpenTelemetry SDK and agent continues an incoming `traceparent` without extra configuration. The standard environment variables:

```bash theme={"dark"}
# One name per service; US region: ingestion.us.bronto.io
OTEL_SERVICE_NAME=orders-api
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingestion.eu.bronto.io
OTEL_EXPORTER_OTLP_HEADERS=x-bronto-api-key=<YOUR_API_KEY>
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
```

For example, a Java service with the [OpenTelemetry Java agent](https://opentelemetry.io/docs/zero-code/java/agent/) needs no code changes:

```bash theme={"dark"}
export JAVA_TOOL_OPTIONS="-javaagent:/otel/javaagent.jar"
export OTEL_SERVICE_NAME=orders-api
export OTEL_EXPORTER_OTLP_ENDPOINT=https://ingestion.eu.bronto.io
export OTEL_EXPORTER_OTLP_HEADERS=x-bronto-api-key=<YOUR_API_KEY>
# Stable database conventions (db.system.name, db.query.text);
# Java agent 2.x still emits the older db.* attributes by default
export OTEL_SEMCONV_STABILITY_OPT_IN=database
```

If the service runs several components (a reverse proxy, a database, the host), an [OpenTelemetry Collector](/agent-setup/open-telemetry.md) next to it can receive their OTLP data, read their log files, and forward everything to Bronto with one API key. Proxies such as [Caddy](https://caddyserver.com/docs/caddyfile/directives/tracing) and Envoy can create spans themselves, so the proxy hop appears in the trace too.

See [Send Traces to Bronto](/tracing/send-traces.md) for SDK examples in other languages.

## Step 3: Check the trace in Bronto

Send a request through your Vercel app, then open [Explore Traces](/tracing/explore-traces.md) and search for the trace. A connected trace shows the Vercel spans and the other service's spans in one waterfall. This one comes from the [example lab](#example): one agent turn on Vercel whose tool call goes through a proxy into a Java API and its database on AWS:

```
invoke_agent petclinic-agent          393 ms  petclinic-agent  Vercel
  agent.step                           24 ms  petclinic-agent  Vercel
    chat petclinic-basic-1              2 ms  petclinic-agent  Vercel
    execute_tool find_owner            13 ms  petclinic-agent  Vercel
      GET (HTTP client span)         10.8 ms  petclinic-agent  Vercel
        caddy                        4.74 ms  caddy            AWS
          GET /petclinic/api/owners  4.35 ms  petclinic        AWS
            select owners pets ...    469 µs  petclinic        AWS
```

![Bronto trace waterfall: invoke\_agent petclinic-agent (393 ms) with an agent.step containing execute\_tool find\_owner (13 ms), its GET client span (10.8 ms), a caddy span (4.74 ms), the petclinic server span GET /petclinic/api/owners (4.35 ms), and below it a repository call and five PostgreSQL statements of 133 to 469 µs.](https://mintcdn.com/bronto/wvpARfzA3Nq_ZZU2/images/integrations/assets/images/vercel-trace-agent-to-petclinic.png?fit=max\&auto=format\&n=wvpARfzA3Nq_ZZU2\&q=85\&s=9fc8dd5f8fb2035e2c0eeb4574708f51)

The same trace in Explore Traces: 17 spans from 3 services, from the agent turn down to the SQL statements.

If the other service's spans appear in a separate trace, its incoming requests don't carry `traceparent`: check `propagateContextUrls` (Step 1), and check that no proxy in between strips the header.

To link logs to the trace, log the trace ID. Vercel's log drain does this for you: runtime log lines from a traced request carry `traceId` and `spanId`. In the other service, OpenTelemetry log bridges and the Collector's `json_parser` (with a `trace` setting) set the trace context on log records.

## What you'll see that Vercel can't show

* **Network time to the other service.** The client span on the Vercel side minus the server span on the other side is the time spent between them. In one test, a call from a function in `iad1` to a backend in `eu-west-1` took 313 ms, of which the backend needed 26 ms. Moving the function to `dub1` brought the call down to 23 ms.
* **Where a slow request spends its time**, down to individual database statements.
* **Errors that the Vercel side hides**, for example a `400` from the backend that an agent turns into a friendly "I couldn't do that".

## Known limits

* **Vercel's trace view** (`vercel traces get <request-id>` or the dashboard) shows the outgoing call as a single span. It doesn't receive spans from other services.
* **eve starts a trace per agent turn**, and Vercel's infrastructure spans for eve's requests form their own traces. Within a turn, the trace stays connected.
* **AI Gateway continues the caller's trace** (the gateway's spans carry the same trace ID) when you also have an [AI Gateway trace drain](/integrations/vercel-ai.md). The gateway's root span refers to a parent span that isn't exported, so it shows as a second root in the same trace.

## Example

[petclinic-agent](https://github.com/bronto-community/petclinic-agent) is a runnable lab: an eve agent and a Next.js app on Vercel calling [Spring PetClinic](https://github.com/spring-petclinic/spring-petclinic-rest), which runs with Caddy, Postgres and an OpenTelemetry Collector on any Docker host outside Vercel (the example trace above came from AWS). It sends everything to Bronto (or any OTLP backend) and shows one trace from the agent's tool call to the SQL statement.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.