Cloud and BYOC for Orca Agent Engine are in Private Preview — request an invite
Docs

Tutorial: Connect Langfuse

Send session-derived traces from Orca Agent Engine to a Langfuse project through OTLP HTTP/JSON.

Langfuse receives session-derived traces from Orca Agent Engine through its OpenTelemetry endpoint. This tutorial creates a Workspace custom binding with metadata-only capture, then uses a new session to verify delivery. You do not install a Langfuse SDK in your agent.

Before you begin

  • Run an exporter with Kafka Transcript access and Registry internal authentication. For a self-hosted deployment, follow deploy the exporter.
  • Create a dedicated Langfuse project. In Project Settings > API Keys, create project-scoped public and secret keys. Do not use organization-scoped keys. See Langfuse API keys.
  • Select a test Workspace without an existing custom binding. Have an organization admin key with observability:read and observability:write, or org:admin.
  • Have a runnable agent and environment in that same Workspace.
  • Install curl, jq, and uuidgen; examples use Bash.

If you start with ork local, its default Compose stack uses Postgres Transcript and does not start an exporter. Add Kafka and an exporter with matching Transcript settings before following this tutorial. Keep such changes in a separate Compose overlay: ork local start rewrites its generated Compose file.

Export these variables from your credential manager or shell setup. Do not commit keys or enable shell tracing.

VariableValue
LANGFUSE_BASE_URLYour project's regional Langfuse HTTPS origin, without a trailing slash or path
LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEYProject-scoped keys for the dedicated project
ORCA_ADMIN_URLAdmin listener origin, without a path suffix
ORCA_ORG_ADMIN_KEYOrganization admin API key
WORKSPACE_IDTest Workspace ID in that organization
ORCA_REGISTRY_URLPublic registry origin for that Workspace
ORCA_API_KEYPublic Workspace API key, sent as x-api-key
AGENT_ID, ENVIRONMENT_IDExisting runnable agent and environment IDs in that Workspace

Choose the Langfuse endpoint

Use the region where you created the project. Langfuse documents these Cloud origins in its OTLP configuration:

Project regionLANGFUSE_BASE_URL
EUhttps://cloud.langfuse.com
UShttps://us.cloud.langfuse.com
Japanhttps://jp.cloud.langfuse.com
HIPAAhttps://hipaa.cloud.langfuse.com

The binding below appends /api/public/otel/v1/traces. Do not use the SDK/Collector base endpoint /api/public/otel as the binding's endpoint_url; Orca does not append the traces suffix for you.

Orca uses otlp_http, langfuse, http/json, none, and Basic authentication with the public key as username and secret key as password. The exporter constructs the Authorization header and adds x-langfuse-ingestion-version: 4. Langfuse's OTEL_EXPORTER_OTLP_* examples configure an OpenTelemetry SDK or Collector, not this exporter. Store project keys through the registry admin API below, not in exporter environment variables, Helm values, or agent metadata.

Langfuse's local HTTP examples are not supported Orca targets. A self-hosted Langfuse instance must expose the exact traces path over publicly reachable HTTPS with a trusted certificate. Orca rejects private DNS addresses, follows no redirects, and does not use ambient proxies. Langfuse support for HTTP/protobuf does not enable it in Orca; use HTTP/JSON.

Verify the project credentials

Langfuse's Public API guide uses GET /api/public/projects to check project-scoped credentials. Extract the returned project ID for the binding rather than guessing it:

Resolve the project associated with the keys
set -euo pipefail
project=$(curl -fsS "$LANGFUSE_BASE_URL/api/public/projects" \
  --user "$LANGFUSE_PUBLIC_KEY:$LANGFUSE_SECRET_KEY")
LANGFUSE_PROJECT_ID=$(jq -er '
  .data | if length == 1 then .[0].id else error("expected one project") end
  | select(type == "string" and length > 0)
' <<< "$project")
export LANGFUSE_PROJECT_ID
jq '.data[] | {id, name}' <<< "$project"

Confirm this is the dedicated project. This checks the keys and base URL from your shell, not the exporter's network path or OTLP delivery. external_project_id records project identity in Orca; the project keys determine where Langfuse ingests the spans.

Create the Workspace binding

Admin endpoint

These operations use the registry admin listener, not the public Workspace API. Set ORCA_ADMIN_URL to its origin without a path suffix. Organization operations use ORCA_ORG_ADMIN_KEY; platform policy operations use a separate ORCA_PLATFORM_KEY. Both examples send x-api-key. Never use a Workspace key or an exporter workload token here. See observability administration for OIDC and scopes.

Read the current settings and retain the opaque ETag, including quotes:

Read Workspace settings and ETag
headers=$(mktemp)
state=$(curl -fsS -D "$headers" \
  "$ORCA_ADMIN_URL/v1/organizations/workspaces/$WORKSPACE_ID/agent_observability" \
  -H "x-api-key: $ORCA_ORG_ADMIN_KEY")
WORKSPACE_ETAG=$(awk 'tolower($1) == "etag:" { sub(/\r$/, "", $2); print $2 }' "$headers")
rm -f "$headers"
jq '{configured, effective}' <<< "$state"

If configured.mode is already custom, stop and select another test Workspace or follow the replacement rules. This example creates a new binding; a same-active-target replacement must omit credentials.

Create a metadata-only Langfuse binding
IDEMPOTENCY_KEY=$(uuidgen)
binding=$(jq -n '{
  mode: "custom",
  capture_ceiling: "metadata_only",
  target: {
    adapter_type: "otlp_http",
    endpoint_kind: "traces_endpoint",
    endpoint_class: "public",
    endpoint_url: (env.LANGFUSE_BASE_URL + "/api/public/otel/v1/traces"),
    external_project_id: env.LANGFUSE_PROJECT_ID
  },
  config: {
    semantic_profile: "langfuse",
    protocol: "http/json",
    compression: "none",
    timeout_ms: 10000,
    sample_rate: 1,
    capture_mode: "metadata_only",
    environment: "test",
    release: "langfuse-check"
  },
  credentials: {
    type: "basic",
    username: env.LANGFUSE_PUBLIC_KEY,
    password: env.LANGFUSE_SECRET_KEY
  }
}' | curl -fsS -X PUT \
  "$ORCA_ADMIN_URL/v1/organizations/workspaces/$WORKSPACE_ID/agent_observability" \
  -H "x-api-key: $ORCA_ORG_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -H "If-Match: $WORKSPACE_ETAG" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" --data-binary @-)
BINDING_ID=$(jq -er '.configured.binding.id' <<< "$binding")
jq '{configured, effective}' <<< "$binding"

A new binding returns 201. Confirm effective.source is workspace_custom, effective.status is enabled, and effective.capture_mode is metadata_only. A successful PUT does not contact Langfuse or prove delivery. For a retry, retain the original request body, ETag, and idempotency key; for a new mutation, read a fresh ETag and use a new key.

Run a new session and inspect the trace

Use the public registry endpoint and Workspace key. Create a new session after the binding is enabled; existing sessions do not adopt the new selection.

Create a session and send synthetic input
session=$(jq -n --arg agent "$AGENT_ID" --arg environment "$ENVIRONMENT_ID" '{
  agent: $agent, environment_id: $environment, title: "Langfuse export check"
}' | curl -fsS "$ORCA_REGISTRY_URL/v1/sessions" \
  -H "x-api-key: $ORCA_API_KEY" \
  -H "Content-Type: application/json" --data-binary @-)
SESSION_ID=$(jq -er '.id' <<< "$session")

curl -fsS "$ORCA_REGISTRY_URL/v1/sessions/$SESSION_ID/events" \
  -H "x-api-key: $ORCA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"events":[{"type":"user.message","content":[{"type":"text","text":"Reply with the word ready."}]}]}'

Use the event stream or event list to confirm acceptance and turn completion. After asynchronous delivery and indexing, open the same Langfuse project and check:

  • The session ID is $WORKSPACE_ID:$SESSION_ID, with a separate trace for each projected turn.
  • The root observation is named orca.agent.turn and has type agent.
  • Newly attributed metadata identifies $BINDING_ID, the agent revision, test environment, and langfuse-check release. Tool/evaluator children appear only when corresponding events exist.
  • Input/output are absent in metadata-only mode. To include content, follow enable raw capture explicitly and test another new session with synthetic data.

Model summaries are explicitly typed span, not generation. Langfuse's attribute mapping documents model/usage/cost fields as generation-only. Do not use the presence of model-summary attributes as proof of generation-specific UI statistics or automatic pricing. Orca's outcome evaluations are evaluator observations, not Langfuse score records or configured Langfuse evaluators.

Troubleshoot and stop the test

SymptomCheck
Project credential check failsUse project keys from the same region and confirm they have not expired or been deleted.
Binding is enabled, but the project is emptyCheck exporter deployment, sampling, and a new session's completed turn. Ready and PUT success do not prove ingestion.
A self-hosted endpoint is rejectedCheck public HTTPS, DNS addresses, certificate trust, the exact traces path, and absence of redirects.
OTLP returns 401 or 403Check the project keys and use Orca's credential-rotation API; repeated rejection after one credential refresh is terminal.
OTLP returns HTTP 200, but some spans are missingOrca treats a valid response with positive partialSuccess.rejectedSpans as terminal, without retrying the rejected spans. Check receiving-side records.
OTLP returns an empty or non-JSON responseOrca requires HTTP 200 with a valid OTLP JSON response and application/json; an invalid response is terminal, not success.

Kafka runtime diagnostics cover recovery and resource budgets, not per-delivery outcome history. Inspect Langfuse or ingress-side records when investigating acceptance or rejection. See sampling and delivery for retry behavior.

To stop the test, read a fresh Workspace ETag and PUT {"mode":"disabled","capture_ceiling":"metadata_only"} with a new idempotency key. See disable a Workspace. Disabling the organization default does not stop a Workspace custom binding, and neither operation deletes traces already stored in Langfuse.

What's next

On this page