OpenTelemetry SDK (vendor-neutral)
If you're not using a SAP SDK, instrument directly with the OpenTelemetry SDK in any language. Boidra Platform only cares that your spans carry the GenAI attributes and reach the OTLP/HTTP endpoint.
Configure the exporter
export OTEL_EXPORTER_OTLP_ENDPOINT="https://<your-dashboard-host>/otel"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf" # if your SDK defaults to gRPCThe SDK appends /v1/traces, so spans post to /otel/v1/traces.
The attributes per span type
Set gen_ai.operation.name to classify the span, then the fields for that type.
Agent turn (invoke_agent)
gen_ai.operation.name = "invoke_agent"
gen_ai.agent.name = "support-agent"
gen_ai.request.model = "gpt-4o" # optional at the agent level
gen_ai.conversation.id = "conv-123" # optional; groups a conversationLLM call (chat)
gen_ai.operation.name = "chat"
gen_ai.request.model = "gpt-4o"
gen_ai.usage.input_tokens = 1200
gen_ai.usage.output_tokens = 340Tool / MCP call (execute_tool)
gen_ai.operation.name = "execute_tool"
gen_ai.tool.name = "get_incident"
gen_ai.tool.type = "mcp" # or "function"Nest chat/tool spans under the agent span (via the active context / parent span) so they group into one trace.
Example (Node/TypeScript)
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { trace, SpanStatusCode } from "@opentelemetry/api";
new NodeSDK({ traceExporter: new OTLPTraceExporter() }).start();
const tracer = trace.getTracer("support-agent");
export async function handleTurn(query: string) {
return tracer.startActiveSpan("invoke_agent support-agent", async (agentSpan) => {
agentSpan.setAttributes({
"gen_ai.operation.name": "invoke_agent",
"gen_ai.agent.name": "support-agent",
"gen_ai.conversation.id": "conv-123",
});
try {
// LLM call as a child span
await tracer.startActiveSpan("chat", async (chatSpan) => {
chatSpan.setAttributes({
"gen_ai.operation.name": "chat",
"gen_ai.request.model": "gpt-4o",
"gen_ai.usage.input_tokens": 1200,
"gen_ai.usage.output_tokens": 340,
});
// ... call the model ...
chatSpan.end();
});
// Tool call as a child span
await tracer.startActiveSpan("execute_tool", async (toolSpan) => {
toolSpan.setAttributes({
"gen_ai.operation.name": "execute_tool",
"gen_ai.tool.name": "get_incident",
"gen_ai.tool.type": "mcp",
});
// ... run the tool ...
toolSpan.end();
});
} catch (err) {
agentSpan.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });
throw err;
} finally {
agentSpan.end();
}
});
}Errors
Boidra Platform marks a span as ERROR when the OpenTelemetry span status is ERROR (SpanStatusCode.ERROR), or when the attribute error = true / outcome = "error" is set. The status message becomes the error message in the UI.
Other languages
The same attributes apply in Python, Java, Go, etc. — set gen_ai.* on your spans and export via OTLP/HTTP to /otel/v1/traces. See the OTLP attributes reference for the complete list.
