Skip to main content
This topic describes how to instrument your agent by using the standard OpenTelemetry Python SDK and export spans over OTLP/HTTP to the watsonx Orchestrate trace ingestion endpoint. Use this approach when:
  • Your agent is built with any agentic framework.
  • You prefer not to add a watsonx Orchestrate-specific SDK dependency.
  • You want a high-level view of requests and responses, without per-call detail such as LLM calls or tool calls.
For an approach that uses the ibm-watsonx-orchestrate-sdk observability decorators to instrument a LangGraph-based agent, see Exporting observability traces with the Observability SDK.

How it works

The following diagram shows the data flow from your agent to the Analytics dashboard:
The setup follows four steps:
  1. Initialize a TracerProvider with a Resource that describes your service.
  2. Configure an OTLPSpanExporter with the watsonx Orchestrate trace ingestion URL and an Authorization header containing a bearer token obtained from the IAM or MCSP token endpoint.
  3. Create spans manually by using tracer.start_as_current_span(...). Nest spans to reflect the logical steps of the agent run. The outermost span is the root span and represents the entire agent run. Child spans represent individual steps.
  4. Attach a BatchSpanProcessor and call force_flush() at the end of the run to drain the buffer before the process exits.
After watsonx Orchestrate receives the spans, they appear in Analyze > [Agent name] > Overview and Conversations, showing total conversations, token counts, average duration, and the usage trend chart.

Prerequisites

Complete the following setup before you instrument your agent:
  1. Register your agent in watsonx Orchestrate to create an agent entry and obtain a valid Agent ID. Use one of the following CLI methods:
    • YAML import: For any agent type, create an agent configuration file with kind: external and run:
      BASH
      For details on authoring the configuration file, see Connect to external agents.
    • Agent discovery: For A2A-compatible agents that publish an agent card, run:
      BASH
      For details, see Import external agents from a URL.
    For a full reference of import and create options, see Importing and deploying agents.
  2. Copy the Agent ID from the registered agent.
  3. Copy the Tenant ID for your instance. The Tenant ID has the format <account-id>_<instance-id>. Obtain it in one of the following ways:
    • From the instance CRN in Profile > About.
    • From the browser DevTools console, run WO_Meta to retrieve its properties.
  4. Obtain an API key and the corresponding IAM or MCSP token URL that is authorized to call the trace ingestion endpoint. For more information, see Authenticating to the API.
  5. Note the instance URL for your watsonx Orchestrate instance. This URL is the base URL of the ingestion endpoint and has the format https://api.watson-orchestrate.ibm.com/instances/<instance-id>. For more information, see Getting the API endpoint.
Save the Agent ID, Tenant ID, API key, token URL, and instance URL values for use in the procedure.

Procedure

1

Installing dependencies

Install the standard OpenTelemetry packages listed in the following table. No watsonx Orchestrate-specific package is required.
BASH
2

Setting environment variables

Set the following environment variables in the process that runs your external agent. You can define them in a shell script, a Kubernetes Secret, or a CI/CD variable store.The following example shell script sets all required variables. Source it before you start the agent process.
BASH
Treat API_KEY as a secret. Inject it by using a secrets manager or CI/CD variable store rather than committing it to source control.
OTEL_RESOURCE_ATTRIBUTES is a standard OpenTelemetry environment variable that the SDK reads automatically. It adds tenant.id and deployment.environment to every span so that the ingestion endpoint routes the trace to the correct tenant.The code in the next step also calls Resource.create(RESOURCE_ATTRIBUTES) with a Python dictionary that sets service.name, service.version, and application. The SDK merges both sets of attributes so that all of them appear on every span.
3

Defining resource and span attributes

Define the service identity and the per-run attributes that watsonx Orchestrate requires to associate each span with the correct agent and conversation.The SDK merges RESOURCE_ATTRIBUTES with the attributes defined in OTEL_RESOURCE_ATTRIBUTES in the previous step, so both the service identity and tenant routing attributes appear on every span.
Python
Set the attributes that are returned by build_span_attributes() on the root span of every agent run:
4

Authenticating and configuring the exporter

Exchange your API key for a bearer token, and then construct the OTLPSpanExporter and TracerProvider.
Python
Token lifetime
IAM and MCSP bearer tokens are short-lived. get_bearer_token() fetches the token once at startup. This is sufficient for short-lived scripts or request handlers that complete within the token lifetime. When a token expires, the ingestion endpoint returns 401 Unauthorized. For long-running processes, catch this response, re-fetch the token, rebuild the exporter with the new token, and retry the request.
The following authentication paths are also supported:
  • MCSP_v2: Uses the X_API_KEY and MCSP_V2_TOKEN_URL environment variables for SaaS environments that require the MCSP v2 token type.
  • Legacy x-api-key: Passes the API key directly in an x-api-key request header without exchanging it for a bearer token first. Use this path only if your instance does not support token-based authentication.
5

Creating nested spans for the agent run

Wrap each logical step of your agent’s execution in a span, and nest child spans to reflect the call hierarchy. Call build_span_attributes() to retrieve the root span attributes, and then set them on the root span before you create any child spans.
Python
Always call force_flush() at the end of the run. If you omit this call, the BatchSpanProcessor background thread might not flush all pending spans before the process exits, and trace data may be lost.

Result

After the script completes and flushes successfully, the conversation and its nested spans are visible in watsonx Orchestrate under Analyze > [Agent name] > Overview and Conversations.

Troubleshooting

Verifying trace ingestion
If traces do not appear on the Analytics page, use the Get Traces API to confirm whether the ingestion endpoint received them. If the API returns your traces, ingestion was successful. If the Get Traces API returns an empty list, the spans were not received. Check the following items:
  • 401 Unauthorized: The bearer token has expired. Fetch a new token and rebuild the exporter as described in Authenticating and configuring the exporter.
  • Spans missing after process exit: force_flush() was not called. The BatchSpanProcessor background thread may not have flushed all pending spans before the process exited. Ensure force_flush() is called at the end of every run.
  • Wrong endpoint: Confirm that OTEL_EXPORT_URL points to the correct instance URL. The correct format is https://api.watson-orchestrate.ibm.com/instances/<instance-id>/v1/orchestrate/inject/traces.
  • Missing resource attributes: Confirm that OTEL_RESOURCE_ATTRIBUTES includes both tenant.id and deployment.environment. Without these attributes, the ingestion endpoint cannot route the trace to the correct tenant.