Skip to main content
Getting Started

Public Trace API

VoltOps exposes a project-key authenticated trace search endpoint for applications that need to load previous runs outside the VoltOps dashboard.

This API is available on the Pro plan and above.

Use this API when you are building:

  • workflow testers that preload earlier workflow runs
  • internal debugging tools for product or support teams
  • prompt iteration UIs that compare a new run against previous traces
  • environment-aware replay tools, for example loading a production run into a staging tester

The endpoint returns persisted VoltOps traces for one project with pagination, sorting, and the same core filters used by the tracing UI.

Availability

Public trace search is available on VoltOps Pro and higher plans. Projects on plans without this feature receive an authorization or plan-limit response from the VoltOps API.

Endpoint

GET https://api.voltagent.dev/api/public/otel/v1/traces

Authenticate with project keys:

x-public-key: pk_xxxx
x-secret-key: sk_live_xxxx

The API keys determine the project scope. If a client sends a projectId query parameter, VoltOps ignores it and uses the project attached to the keys.

caution

The secret key is sensitive. Call this endpoint from your backend, a server action, or another trusted runtime. Do not expose x-secret-key in browser code.

Use from @voltagent/core

import { VoltOpsClient } from "@voltagent/core";

const voltops = new VoltOpsClient({
publicKey: process.env.VOLTAGENT_PUBLIC_KEY!,
secretKey: process.env.VOLTAGENT_SECRET_KEY!,
});

const traces = await voltops.observability.traces.list({
search: "checkout",
environments: ["dev", "prod"],
status: ["success", "error"],
limit: 20,
offset: 0,
sortBy: "start_time",
sortOrder: "desc",
});

console.log(traces.data);

The same client works for packages and applications built on top of @voltagent/core, including a workflow tester service that already has access to your VoltOps project keys.

Use from @voltagent/sdk

If your integration uses the REST SDK:

import { VoltAgentCoreAPI } from "@voltagent/sdk";

const api = new VoltAgentCoreAPI({
publicKey: process.env.VOLTAGENT_PUBLIC_KEY!,
secretKey: process.env.VOLTAGENT_SECRET_KEY!,
});

const traces = await api.observability.traces.list({
conversationId: "conv_123",
limit: 10,
});

@voltagent/sdk also exports VoltOpsClient, which extends the core client and supports the same voltops.observability.traces.list() call.

Use with fetch

const params = new URLSearchParams({
search: "invoice workflow",
environments: "production",
limit: "25",
offset: "0",
sortBy: "start_time",
sortOrder: "desc",
});

const response = await fetch(`https://api.voltagent.dev/api/public/otel/v1/traces?${params}`, {
headers: {
"x-public-key": process.env.VOLTAGENT_PUBLIC_KEY!,
"x-secret-key": process.env.VOLTAGENT_SECRET_KEY!,
},
});

if (!response.ok) {
throw new Error(`Trace search failed: ${response.status}`);
}

const traces = await response.json();

Query Options

All query parameters are optional. Values that accept multiple entries can be sent as comma-separated strings, or as arrays when using the SDK helpers.

ParameterTypeDescription
limitnumberPage size. Defaults to the API default.
offsetnumberNumber of traces to skip.
sortBystringSort field, commonly start_time.
sortOrderasc or descSort direction.
searchstringText search across trace fields.
traceIdstringMatch a specific trace ID.
agentIdstring or string[]Filter by agent or workflow entity ID.
entityTypestring or string[]Filter by entity type, such as agent or workflow.
conversationIdstringFilter traces from one conversation/session.
userIdstringFilter traces for one user.
modelstring or string[]Filter by model name.
statusstring or string[]Filter by trace status.
levelstring or string[]Filter by trace level.
tagsstring or string[]Filter by trace tags.
environmentsstring or string[]Filter by environment resource attribute.
startDatestring or DateInclude traces starting after this time.
endDatestring or DateInclude traces starting before this time.
minTokensnumberMinimum token count.
maxTokensnumberMaximum token count.
minCostnumberMinimum cost.
maxCostnumberMaximum cost.
minDurationnumberMinimum duration in milliseconds.
maxDurationnumberMaximum duration in milliseconds.
promptIdstringFilter by prompt ID.
promptVersionstringFilter by prompt version.
feedbackKeystring or string[]Filter by feedback key.
feedbackScorenumberMatch an exact feedback score.
minFeedbackScorenumberMinimum feedback score.
maxFeedbackScorenumberMaximum feedback score.
feedbackValuestringFilter by feedback value.
feedbackSourceTypestring or string[]Filter by feedback source type.

Response Shape

type TraceListResponse = {
data: Array<{
trace_id: string;
project_id: string;
agent_id?: string;
entity_type?: string;
user_id?: string | null;
conversation_id?: string | null;
start_time: string;
end_time?: string | null;
status?: string | null;
input?: unknown;
output?: unknown;
usage?: unknown;
metadata?: Record<string, unknown> | null;
tags?: string[] | null;
model?: string | null;
level?: string | null;
status_message?: string | null;
service_name?: string;
span_count?: number;
error_count?: number;
duration_ms?: number | null;
resource_attributes?: Record<string, unknown> | null;
latest_feedback_score?: number | null;
latest_feedback_key?: string | null;
latest_feedback_comment?: string | null;
latest_feedback_at?: string | null;
}>;
total: number;
pageCount: number;
};

Use total, pageCount, limit, and offset to build paginated trace pickers.

Workflow Tester Pattern

A common workflow tester flow looks like this:

  1. Search previous traces by workflow ID, environment, user, or free text.
  2. Let the user select a trace.
  3. Use the selected trace's input, metadata, conversation_id, or tags to prefill the tester.
  4. Run the workflow with modified prompts or config.
  5. Compare the new trace against the selected previous trace in VoltOps.

Example server-side loader:

import { VoltOpsClient } from "@voltagent/core";

const voltops = new VoltOpsClient({
publicKey: process.env.VOLTAGENT_PUBLIC_KEY!,
secretKey: process.env.VOLTAGENT_SECRET_KEY!,
});

export async function loadWorkflowRuns(workflowId: string, environment: string) {
return await voltops.observability.traces.list({
entityType: "workflow",
agentId: workflowId,
environments: environment,
limit: 50,
sortBy: "start_time",
sortOrder: "desc",
});
}

For cross-environment testing, use the keys for the source environment/project when loading old runs, then run the tester with the target environment's configuration.

Local Hono/Elysia Routes vs VoltOps API

The local server routes such as /observability/traces read from the local in-memory or configured observability storage attached to the running app. They are useful for local development and live debugging.

The VoltOps public trace API reads persisted traces from VoltOps. Use it when you need older runs, production runs, or traces from a different deployed environment.

Security Notes

  • Treat VOLTAGENT_SECRET_KEY as backend-only.
  • Gate access in your own application to users who should be allowed to inspect persisted traces. VoltOps enforces the project plan on the API side.
  • Scope each workflow tester deployment to the minimum project keys it needs.
  • Prefer separate VoltOps projects for dev, staging, and production if users need clear environment boundaries.
  • Log trace IDs, not full trace payloads, when debugging your tester.

Table of Contents