This guide walks you end-to-end through sending agent telemetry to Agent 365 directly over OpenTelemetry (OTLP/HTTP+JSON). Before starting, read Agent 365 observability concepts to understand the model, the auth flows, and the surfaces your data lands in.
Important
The direct OTel path is the exception, not the default. Use it only if you already have an OpenTelemetry pipeline, your framework can't use the Agent 365 SDK, or your agent is in a language the SDK doesn't yet support (such as Java). For everyone else, the recommended path is the Microsoft OpenTelemetry Distro, which provides a unified observability SDK across Agent 365, Microsoft Foundry, Azure Monitor, and more. The earlier Observability SDK continues to work without breaking changes but is no longer recommended for new integrations; migration guidance for existing SDK users is coming.
Prerequisites
Ensure the following configurations are in place before any telemetry flows.
| Who | What |
|---|---|
| Tenant admin | Sign up for Agent 365 and grant consent for your agent app. See Onboard to Agent 365. Without a licensed tenant, ingestion is silently dropped - the request returns 200 OK with partialSuccess: null but data never appears downstream. |
| Tenant admin | Assign a Microsoft 365 E7 or Microsoft Agent 365 license to at least one user in the tenant. The SKU being present isn't enough. Assignment to a user starts the Defender backend workflow that enables ingestion. Without an assigned license, requests return 200 OK with partialSuccess: null and data is silently dropped. |
| Tenant admin | Grant tenant consent. See Grant agents access to Microsoft 365 resources. Without it, tokens are issued without the role/scope and requests return 403. |
| Your dev team | Register your app (standard Microsoft Entra app or blueprint). See Get started with Agent 365 development. |
| Your dev team | Add Agent365.Observability.OtelWrite under API permissions (app role for S2S, scope for delegated). For blueprints, see Configure inheritable permissions. Coordinate with the Agent 365 onboarding team to enable the permission. |
Authentication recipes
All four recipes use the standard Microsoft Entra token endpoint:
| Field | Value |
|---|---|
| Token endpoint | https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token |
Resource (aud in returned token) |
9b975845-388f-4429-889e-eab1ef63949c (also accepts api://9b975845-388f-4429-889e-eab1ef63949c) |
| S2S scope | 9b975845-388f-4429-889e-eab1ef63949c/.default |
| OBO scope | 9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite |
The recipes below show raw HTTP for clarity. In production, prefer Microsoft.Identity.Web or another MSAL library, which handles token refresh and caching.
Which recipe do I need?
| My app model | My OAuth flow | Go to |
|---|---|---|
| Standard Microsoft Entra app registration | S2S (client credentials) | S2S, Standard Microsoft Entra app |
| Standard Microsoft Entra app registration | OBO (delegated) | OBO, Standard Microsoft Entra app |
| Blueprint-derived agent identity | S2S (client credentials) | S2S, Blueprint-derived agent identity |
| Blueprint-derived agent identity | OBO / AI teammate | OBO, Blueprint-derived agent identity |
S2S, Standard Microsoft Entra app
One POST to the tenant's token endpoint with grant_type=client_credentials. Authenticate the app by using a client secret, a certificate (signed JWT assertion), or a managed identity or federated credential.
POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={your-app-id}
&scope=9b975845-388f-4429-889e-eab1ef63949c%2F.default
&client_secret={secret}
&grant_type=client_credentials
The returned token has appid/azp = {your-app-id}, roles containing Agent365.Observability.OtelWrite, and aud = 9b975845-.... Use it on the /observabilityService/.../traces route.
For certificate-based authentication, replace client_secret={secret} with client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion={signed-jwt}.
S2S, Blueprint-derived agent identity
Agent identities don't have credentials of their own. The agent identity blueprint holds the credentials (managed identity FIC, certificate, or client secret) and mints tokens on behalf of its child agent identities through a two-step exchange. For more information, see autonomous app OAuth flow.
The blueprint authenticates and gets a federated identity exchange token
T1:{blueprint-credential}is the blueprint's MSI token, certificate-signed JWT, or secret exchange-token assertion - per blueprint configuration.
POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token Content-Type: application/x-www-form-urlencoded client_id={blueprint-app-id} &scope=api%3A%2F%2FAzureADTokenExchange%2F.default &fmi_path={agent-identity-app-id} &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion={blueprint-credential} &grant_type=client_credentialsThe agent identity exchanges
T1for the Agent 365 Observability resource token:POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token Content-Type: application/x-www-form-urlencoded client_id={agent-identity-app-id} &scope=9b975845-388f-4429-889e-eab1ef63949c%2F.default &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion={T1} &grant_type=client_credentials- The returned token has
appid/azp={agent-identity-app-id},rolescontainingAgent365.Observability.OtelWrite, andaud=9b975845-.... - Use this token on the
/observabilityService/.../tracesroute. - The URL
{agentId}is the agent identity appId, not the blueprint appId.
- The returned token has
OBO, Standard Microsoft Entra app
Receive the user's incoming token Tc from your upstream caller (Bearer or PFAT), then exchange it:
POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={your-app-id}
&scope=9b975845-388f-4429-889e-eab1ef63949c%2FAgent365.Observability.OtelWrite
&client_secret={secret}
&grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion={Tc}
&requested_token_use=on_behalf_of
For certificate auth, replace client_secret={secret} with the same client_assertion_type + client_assertion pair as in S2S.
The returned token has appid/azp = {your-app-id}, scp containing Agent365.Observability.OtelWrite, and aud = 9b975845-.... Use it on the /observability/.../traces route. A refresh token is returned alongside; cache and reuse it instead of rerunning the exchange on every call.
OBO, Blueprint-derived agent identity (including AI teammate)
There are three main steps to the on-behalf-of flow. For more information, see Agent OAuth flows: On behalf of flow.
Receive the user token
Tc. For an AI teammate, this token represents the agent's own user account; otherwise, it represents the human caller.The blueprint authenticates and gets
T1, the same as the S2S blueprint-derived agent identity flow.The agent identity exchanges
T1andTcfor a delegated resource token:POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token Content-Type: application/x-www-form-urlencoded client_id={agent-identity-app-id} &scope=9b975845-388f-4429-889e-eab1ef63949c%2FAgent365.Observability.OtelWrite &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion={T1} &grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer &assertion={Tc} &requested_token_use=on_behalf_of
The returned token has appid/azp = {agent-identity-app-id}, scp containing Agent365.Observability.OtelWrite, and represents the agent's user. Use it on the /observability/.../traces route. The URL {agentId} is the agent identity appId, not the blueprint appId. A refresh token is returned alongside; cache and reuse it.
Required claims on the returned token
S2S route (/observabilityService/...) - app-only token:
| Claim | Required value |
|---|---|
aud |
9b975845-388f-4429-889e-eab1ef63949c (or api://9b975845-...) |
roles |
Must contain Agent365.Observability.OtelWrite |
appid (v1) or azp (v2) |
Must equal URL {agentId} |
scp |
Must be absent |
Delegated route (/observability/...) - user-delegated token (Bearer or PFAT):
| Claim | Required value |
|---|---|
aud |
9b975845-388f-4429-889e-eab1ef63949c (or api://9b975845-...) |
scp |
Must contain Agent365.Observability.OtelWrite |
appid / azp |
Must equal URL {agentId} |
The delegated route accepts both Bearer and MSAuth1.0 PFAT tokens. Direct callers should use Bearer. If you don't know which one you have, use Bearer.
Endpoints
Two routes; pick by how your service authenticates, not by what the user is doing:
POST https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1 # S2S
POST https://agent365.svc.cloud.microsoft/observability/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1 # OBO
Headers:
Authorization: Bearer <token> # or MSAuth1.0 ... for delegated PFAT
Content-Type: application/json
URL parameters
{tenantId}- the customer tenant GUID. The server treats this as authoritative; if your spans setmicrosoft.tenant.idand it disagrees, the request is rejected.{agentId}- the calling application's appId (also the OAuthclient_id). For blueprint-derived identities, this is the agent identity appId, not the blueprint appId. Must equal theappid/azpclaim of your token.api-version=1- required.
Request body encoding
The body is the standard OTLP/HTTP+JSON shape: an ExportTraceServiceRequest with resourceSpans → scopeSpans → spans. Keep in mind the following details:
traceId(16 bytes) andspanId(8 bytes) are sent as lowercase hex strings.startTimeUnixNano/endTimeUnixNanoare strings holding Unix epoch nanoseconds.kindis the integer OTLP enum value (for example1forINTERNAL);status.codeis the integer enum (for example1forOK,2forERROR).- All attribute values are sent as
stringValue.
Response shape
A successful call returns 200 OK:
{ "partialSuccess": null }
If some spans were rejected by the per-span filter:
{
"partialSuccess": {
"rejectedSpans": 2,
"errorMessage": "Dropped 2 non-A365 span(s) ..."
}
}
Field names are camelCase on the wire. Always check partialSuccess: a 200 with all your spans rejected is a real outcome you must surface. Limits and drop conditions lists the silent drop cases where a 200 returns with partialSuccess: null despite no data appearing downstream.
Smallest possible request
The simplest end-to-end test sends a single invoke_agent span. This span is the smallest body that lands in Microsoft Defender.
Step 1. Get a Bearer token. For S2S, use client credentials with scope 9b975845-388f-4429-889e-eab1ef63949c/.default (see Authentication recipes for the full recipe).
Step 2. POST a single span:
TOKEN="$(./get-token.sh)"
TENANT_ID="<customer-tenant-guid>"
AGENT_ID="<your-agent-app-id>"
curl -i -X POST \
"https://agent365.svc.cloud.microsoft/observabilityService/tenants/${TENANT_ID}/otlp/agents/${AGENT_ID}/traces?api-version=1" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
--data @- <<EOF
{
"resourceSpans": [{
"scopeSpans": [{
"scope": { "name": "my-instrumentation", "version": "1.0.0" },
"spans": [{
"traceId": "0102030405060708090a0b0c0d0e0f10",
"spanId": "1111111111111111",
"parentSpanId": "",
"name": "invoke_agent",
"kind": 1,
"startTimeUnixNano": "1736175600000000000",
"endTimeUnixNano": "1736175601500000000",
"status": { "code": 1 },
"attributes": [
{ "key": "gen_ai.operation.name", "value": { "stringValue": "invoke_agent" } },
{ "key": "gen_ai.agent.id", "value": { "stringValue": "${AGENT_ID}" } },
{ "key": "gen_ai.agent.name", "value": { "stringValue": "MyAgent" } },
{ "key": "microsoft.a365.agent.blueprint.id", "value": { "stringValue": "${AGENT_ID}" } },
{ "key": "gen_ai.conversation.id","value": { "stringValue": "conv-001" } },
{ "key": "microsoft.channel.name","value": { "stringValue": "web" } },
{ "key": "user.id", "value": { "stringValue": "<entra-user-objectid>" } },
{ "key": "client.address", "value": { "stringValue": "10.1.2.80" } },
{ "key": "server.address", "value": { "stringValue": "myagent.example.com" } },
{ "key": "server.port", "value": { "stringValue": "443" } },
{ "key": "gen_ai.input.messages", "value": { "stringValue": "[{\"role\":\"user\",\"content\":\"hi\"}]" } },
{ "key": "gen_ai.output.messages","value": { "stringValue": "[{\"role\":\"assistant\",\"content\":\"hello\"}]" } }
]
}]
}]
}]
}
EOF
Step 3. Expect 200 OK with this body:
{ "partialSuccess": null }
Step 4. Confirm the data actually landed. A 200 OK is not proof of ingestion; Verifying ingestion walks through the verification flow. To POST a saved body file instead, replace --data @- <<EOF ... EOF with --data @./otlp-request.json.
Agent run example
A user on Microsoft Teams asks "What's the weather in Seattle?". Your agent calls a GetWeather function, asks an LLM to format the answer, and replies. That single run is four spans:
graph TD
A["<b>invoke_agent</b> · spanId=A · parentSpanId=∅<br/><i>root - the run itself</i>"]
B["<b>chat</b> · spanId=B · parentSpanId=A<br/><i>LLM picks the tool / formats reply</i>"]
C["<b>execute_tool</b> · spanId=C · parentSpanId=A<br/><i>the GetWeather call</i>"]
D["<b>output_messages</b> · spanId=D · parentSpanId=A<br/><i>final reply emitted to the user</i>"]
A --> B
A --> C
A --> D
Run-wide attributes set on every span:
| Attribute | Example value |
|---|---|
traceId |
0102030405060708090a0b0c0d0e0f10 |
gen_ai.conversation.id |
19:abc@thread.tacv2 |
microsoft.session.id |
session-1234 |
microsoft.channel.name |
msteams |
gen_ai.agent.id |
<AGENT_APP_ID> |
gen_ai.agent.name |
WeatherBot |
microsoft.a365.agent.blueprint.id |
<BLUEPRINT_APP_ID> |
user.id |
<entra-user-objectid> |
client.address |
10.1.2.80 |
server.address |
weatherbot.example.com |
server.port |
443 |
Important
These run-wide attributes are not propagated automatically. You must set gen_ai.conversation.id, microsoft.channel.name, and microsoft.session.id on every span yourself.
Span A: invoke_agent (root)
{
"traceId": "0102030405060708090a0b0c0d0e0f10",
"spanId": "1111111111111111",
"parentSpanId": "",
"name": "invoke_agent",
"kind": 1,
"startTimeUnixNano": "1736175600000000000",
"endTimeUnixNano": "1736175601500000000",
"status": { "code": 1 },
"attributes": [
{ "key": "gen_ai.operation.name", "value": { "stringValue": "invoke_agent" } },
{ "key": "gen_ai.execution.type", "value": { "stringValue": "HumanToAgent" } },
{ "key": "gen_ai.input.messages", "value": { "stringValue": "[{\"role\":\"user\",\"content\":\"What's the weather in Seattle?\"}]" } },
{ "key": "gen_ai.output.messages", "value": { "stringValue": "[{\"role\":\"assistant\",\"content\":\"It's 65F and partly cloudy in Seattle.\"}]" } },
{ "key": "user.email", "value": { "stringValue": "alice@contoso.com" } }
/* plus all the run-wide attributes listed above */
]
}
Span B: chat (LLM call)
{
"traceId": "0102030405060708090a0b0c0d0e0f10",
"spanId": "2222222222222222",
"parentSpanId": "1111111111111111",
"name": "chat",
"kind": 1,
"startTimeUnixNano": "1736175600200000000",
"endTimeUnixNano": "1736175600900000000",
"status": { "code": 1 },
"attributes": [
{ "key": "gen_ai.operation.name", "value": { "stringValue": "chat" } },
{ "key": "gen_ai.request.model", "value": { "stringValue": "gpt-4o" } },
{ "key": "gen_ai.provider.name", "value": { "stringValue": "openai" } },
{ "key": "gen_ai.usage.input_tokens", "value": { "stringValue": "42" } },
{ "key": "gen_ai.usage.output_tokens", "value": { "stringValue": "23" } }
/* plus all the run-wide attributes */
]
}
Span C: execute_tool
{
"traceId": "0102030405060708090a0b0c0d0e0f10",
"spanId": "3333333333333333",
"parentSpanId": "1111111111111111",
"name": "execute_tool",
"kind": 1,
"startTimeUnixNano": "1736175600950000000",
"endTimeUnixNano": "1736175601200000000",
"status": { "code": 1 },
"attributes": [
{ "key": "gen_ai.operation.name", "value": { "stringValue": "execute_tool" } },
{ "key": "gen_ai.tool.name", "value": { "stringValue": "GetWeather" } },
{ "key": "gen_ai.tool.type", "value": { "stringValue": "function" } },
{ "key": "gen_ai.tool.call.id", "value": { "stringValue": "call-001" } },
{ "key": "gen_ai.tool.call.arguments", "value": { "stringValue": "{\"location\":\"Seattle\"}" } },
{ "key": "gen_ai.tool.call.result", "value": { "stringValue": "{\"tempF\":65,\"condition\":\"partly cloudy\"}" } }
/* plus all the run-wide attributes */
]
}
Span D: output_messages
{
"traceId": "0102030405060708090a0b0c0d0e0f10",
"spanId": "4444444444444444",
"parentSpanId": "1111111111111111",
"name": "output_messages",
"kind": 1,
"startTimeUnixNano": "1736175601400000000",
"endTimeUnixNano": "1736175601500000000",
"status": { "code": 1 },
"attributes": [
{ "key": "gen_ai.operation.name", "value": { "stringValue": "output_messages" } },
{ "key": "gen_ai.output.messages", "value": { "stringValue": "[{\"role\":\"assistant\",\"content\":\"It's 65F and partly cloudy in Seattle.\"}]" } }
/* plus all the run-wide attributes */
]
}
Sending telemetry
Using an OTel SDK
Most partners send traces through an OTel SDK rather than hand-rolled HTTP. The SDK handles batching, retry, and the OTLP/HTTP+JSON encoding for you. Set the exporter endpoint and inject the Authorization header.
The exporter endpoint is the route URL itself, including the query string:
https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1
(Use /observability/... instead of /observabilityService/... for the delegated route.)
Python
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
exporter = OTLPSpanExporter(
endpoint="https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1",
headers={"Authorization": f"Bearer {token}"},
)
Package: opentelemetry-exporter-otlp-proto-http.
Node.js / TypeScript
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
const exporter = new OTLPTraceExporter({
url: "https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1",
headers: { Authorization: `Bearer ${token}` },
});
Package: @opentelemetry/exporter-trace-otlp-http.
.NET
using OpenTelemetry.Exporter;
services.AddOpenTelemetry().WithTracing(b => b
.AddOtlpExporter(o =>
{
o.Endpoint = new Uri("https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1");
o.Headers = $"Authorization=Bearer {token}";
o.Protocol = OtlpExportProtocol.HttpJson;
}));
Package: OpenTelemetry.Exporter.OpenTelemetryProtocol.
Manual HTTP
If you can't or don't want to use an OTel SDK, build the OTLP/HTTP+JSON request yourself and POST it. The body shape is defined by the OpenTelemetry OTLP/HTTP+JSON spec:
{
"resourceSpans": [{
"resource": { "attributes": [ ... ] }, // optional
"scopeSpans": [{
"scope": { "name": "<your-instrumentation>", "version": "1.0.0" },
"spans": [ <span>, <span>, ... ]
}]
}]
}
Each <span> is an object whose required fields are traceId, spanId, name, kind, startTimeUnixNano, endTimeUnixNano, attributes, and (for non-root spans) parentSpanId. See Endpoints and Request body encoding for the encoding rules (string-encoded times, hex traceId / spanId, integer kind / status.code, all attribute values as stringValue).
The set of attributes to set on each span is defined in Message contracts. See Attribute reference for the full attribute list. Refer to the Agent run example for an end-to-end working sample with the Bearer token in the header and the body inline.
You can send all the spans of a run in a single POST body (preferred - one request, one trace) or across multiple POSTs. The server reconstructs the run from traceId + parentSpanId + gen_ai.conversation.id, so each span carries enough to be correlated either way.
Message contracts
This section defines what spans you can emit and which attributes go on each. For the full attribute-by-attribute spec, see the Attribute reference.
Operation types
Every span you send must carry gen_ai.operation.name set to one of these four values (case-insensitive). Any span with a missing or unrecognized value is silently dropped and counted in partialSuccess.rejectedSpans.
gen_ai.operation.name |
Meaning | Most-googled gotcha |
|---|---|---|
invoke_agent |
An invocation of an agent. The "root" of an agent run. | Required for the run to appear in Microsoft Defender agent-activity views or the Microsoft 365 admin center. Without it, the telemetry only lands in Microsoft Defender advanced hunting (CloudAppEvents). |
execute_tool |
A tool / function call performed by an agent. | -- |
chat |
An LLM inference call. | Use the literal chat, NOT inference. |
output_messages |
A final emitted output message. | -- |
Span hierarchy and run grouping
Agent 365 reconstructs a run from the standard OTLP span graph (traceId, spanId, parentSpanId) plus the run-wide attributes from the Attribute reference.
Six rules:
- Always set
parentSpanIdon every non-root span. Without it, the run's tree structure can't be reconstructed. - Reuse the same
traceIdacross every span in a run. - Set
gen_ai.conversation.idon every span with the same value. This is the primary join key for "all spans in this run". It is not propagated automatically. - Set
microsoft.channel.nameon every span with the same value. Tool spans missing the channel / conversation can inherit them from their parentinvoke_agentonly if the parent is in the same OTLP request, so set them on every span yourself. - Set
microsoft.session.idon every span when you have a logical session. - For agent-to-agent calls where the child agent is in a separate request, reuse the same
gen_ai.conversation.idand use themicrosoft.a365.caller.agent.*attributes (see Attribute reference) to capture the caller-agent context.
The four-span tree in the Agent run example is the canonical shape.
Common run shapes
| Shape | Spans to emit | Notes |
|---|---|---|
| Single-agent chatbot (no tools, no LLM span) | One invoke_agent only |
Set run-wide attributes plus gen_ai.input.messages and gen_ai.output.messages. Identical to Smallest possible request. |
| Agent with tools (most common) | invoke_agent root + chat, execute_tool, output_messages children |
All children share the root's traceId and set parentSpanId = root.spanId. All carry the same run-wide attributes. See Agent run example for a full example. |
| Agent-to-agent | Each agent emits its own invoke_agent |
Reuse the same gen_ai.conversation.id across both agents. On the target's invoke_agent, set gen_ai.execution.type = "Agent2Agent" and the microsoft.a365.caller.agent.* attributes (calling agent's appId, name, blueprint appId, user id, and email). If the calling agent has no Entra registration, use microsoft.a365.caller.agent.platform.id and gen_ai.caller.agent.type instead. |
Onboarding checklist
Run through this checklist before going to production.
| Category | Check |
|---|---|
| Auth | Your Entra app (or blueprint) is registered and you can mint tokens for it. |
| Auth | Your app has been granted Agent365.Observability.OtelWrite (app role for S2S, scope for delegated). |
| Auth | Each agent has its own Entra appId as {agentId} in the URL. For blueprint-derived identities, that appId is the agent identity appId, not the blueprint appId. If the agent has no Entra registration, see Picking values. |
| Auth | A tenant admin has granted consent for Agent365.Observability.OtelWrite. Without consent, tokens are issued without the role/scope and requests are rejected with 403. |
| Licensing | At least one user in the customer tenant has a Microsoft 365 E7 or Microsoft Agent 365 license assigned (assignment, not just SKU presence in the tenant). Without an assigned license, ingestion is silently dropped. See Prerequisites. |
| Spans | Every span sets the run-wide essentials (Span hierarchy and run grouping). |
| Spans | invoke_agent spans set gen_ai.input.messages and gen_ai.output.messages. |
| Spans | execute_tool spans set gen_ai.tool.name, gen_ai.tool.type, gen_ai.tool.call.id, gen_ai.tool.call.arguments, gen_ai.tool.call.result. |
| Spans | chat spans set gen_ai.request.model and gen_ai.provider.name (and ideally gen_ai.usage.input_tokens / gen_ai.usage.output_tokens - string-encoded). |
| Spans | All non-root spans set parentSpanId; all spans in a run share the same traceId. |
| Payload | Request body is ≤ 1 MB. |
| Verification | You parse partialSuccess on every response and log rejections. |
| Verification | You ran the verification flow in Verifying ingestion against your first runs. |
Next steps
- Attribute reference - Per-attribute spec and value-picking guidance.
- Troubleshooting - Verifying ingestion, common pitfalls, and error responses.