> ## Documentation Index
> Fetch the complete documentation index at: https://platform.atlan.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> To act on Atlan objects, use the Atlan MCP server at https://api.atlan.com/mcp or the SDK. To install or publish skills from a terminal, use the `aireg` CLI; `aireg --map json` prints its command map. The `atlanai` CLI is deprecated. Run a read-only identity check before any write.
> The docs MCP server at /mcp searches these docs only. It cannot read or change Atlan objects.
> SDK packages: Python `atlanai` (PyPI) and TypeScript `@atlanai/sdk` (npm). Show Python first, then TypeScript.
> These docs cover Atlan AI Registry, Atlan's hosted product. The AI registry specification and aireg CLI are maintained separately. /open-source lists publicly reachable signed installers and explains that the source repository and hosted CLI reference currently require GitHub access. Do not claim the repository or its reference docs are publicly accessible. Use these docs for Atlan-specific setup such as the adapter, sign-in and workspaces.

# Trace attributes

> The request headers and OpenTelemetry attributes Atlan reads to set a trace's owner, workspace, session, visitor, skill, model, tokens, and cost.

Atlan reads a fixed set of headers and attributes when it ingests a trace. This page lists them, the order Atlan tries alias keys in, and what happens when a value is missing. The atlanai SDK sets these keys for you; set them yourself when you send from another OpenTelemetry producer.

Where a field has several keys, the **first key present** on the span wins, in the order listed. Keys are read from the span that carries them; a child span does not inherit its parent's model, skill, or tool.

## Owner and workspace

These come from the request, not from attributes. No attribute can change them.

| Field | Comes from | When missing |
| - | - | - |
| Owner (agent or person) | The API key that sent the request. An agent's key files the trace under the agent; a personal key files it under that person. | Not possible: the request is refused with `401`. |
| Workspace | The `X-Atlan-Workspace-Id` header. The sender must be able to create in that workspace, or the request is refused with `403`. | The sending credential's own workspace. An agent's key uses the agent's workspace. If Atlan cannot resolve exactly one workspace, the trace is not readable from any workspace, so always send the header. |
| Account | The sending credential. | Not possible. A request cannot select another account. |

`service.name`, `gen_ai.agent.name`, and `user.id` never decide the owner. See [How trace attribution works](/registry/traces/concepts/attribution).

## Resource attributes

| Attribute | Type | Required | Meaning |
| - | - | - | - |
| `service.name` | string | Yes, for SDK batches | Labels the process in traces. It does not choose the agent. An SDK batch without it is refused with `422`. |
| `atlan.sdk.schema_version` | string | Set by the SDK | Marks an atlanai SDK batch. Only `agent-sdk.v1` is accepted; any other value is refused with `422`. |
| `atlan.consent.store_content` | bool | No | `false` drops prompts, responses, tool arguments and results, and `atlan.metadata.*` from an SDK batch at ingest. The SDK sets it from `trace_content=False` or `ATLAN_TRACE_CONTENT=false`. |
| `atlan.harness.id` | string | No | The coding harness that produced the session. Atlan uses it to list a harness's traces. |
| `user.id`, `user.email`, `org.name` | string | No | Reserved. On SDK batches, Atlan removes these from the resource and records the verified sender as `user.id`. |

## Session, visitor, skill, and eval

| Field | Keys, in order | When missing |
| - | - | - |
| Session | `atlan.session.id`, `session.id`, `session_id`, `gen_ai.conversation.id`, `gcp.vertex.agent.session_id` | The SDK sets `session.id` on each trace root to the trace ID, so a run is its own session. Other producers' traces have no session. |
| Visitor | `atlan.visitor.id` | No visitor on the span. The SDK sets it from `visitor_id`; see [Identify visitors](/registry/agents/how-tos/identify-visitors). |
| Skill name | `atlan.skill.name`, `claude_code.skill.name` | No skill attribution for that span. |
| Skill version | `atlan.skill.version`, `claude_code.skill.version` | Attribution can still match by content or name. |
| Skill content | `atlan.skill.source_digest`, `atlan.skill.skillmd_sha256` | Atlan cannot make an exact content match and falls back to weaker matches. See [How skill usage works](/registry/skills/concepts/usage). |
| Eval experiment | `atlan.eval.experiment_id` | The trace is not listed under an experiment. `Eval` sets it for each case. |

No attribute sets an **agent version**. Traces and the SDK's session records name the agent, not a version. To compare versions, record the version in an eval experiment's `config`.

## Span type and model

| Field | Keys, in order | When missing |
| - | - | - |
| Span type | `atlan.span.type`, `gen_ai.operation.name`, `llm.span_type`, `openinference.span.kind`, `span.type`, `traceloop.span.kind` | Atlan guesses from the span name, else `unknown`. Types are `task`, `llm`, `tool`, `function`, `session`, `turn`, and `score`. |
| Provider | `gen_ai.system`, `gen_ai.provider.name`, `atlan.provider`, `llm.provider`, `openai.provider`, `anthropic.provider`, `provider` | No provider shown. Pricing does not use the provider. |
| Requested model | `gen_ai.request.model`, `atlan.request.model`, `llm.request.model`, `openai.request.model`, `anthropic.request.model`, `request_model` | See **Model**. |
| Response model | `gen_ai.response.model`, `atlan.response.model`, `llm.response.model`, `openai.response.model`, `anthropic.response.model`, `response_model` | See **Model**. |
| Model | The response model, else the requested model, else `atlan.model`, `llm.model_name`, `model` | The call is not priced. |
| Tool | `gen_ai.tool.name`, `gen_ai.tool.type` | The span is not counted as a tool call. |

The SDK's `update(model=..., provider=...)` writes `gen_ai.request.model` and `gen_ai.provider.name`.

## Tokens

All token counts are integers. Atlan stores four separate counts, so cached tokens are never counted twice.

| Field | Keys, in order | When missing |
| - | - | - |
| Input | `gen_ai.usage.input_tokens`, `gen_ai.usage.prompt_tokens`, `atlan.usage.input_tokens`, `llm.usage.prompt_tokens`, `openai.usage.prompt_tokens`, `llm.token_count.prompt` | `0` |
| Output | `gen_ai.usage.output_tokens`, `gen_ai.usage.completion_tokens`, `atlan.usage.output_tokens`, `llm.usage.completion_tokens`, `openai.usage.completion_tokens`, `llm.token_count.completion` | `0` |
| Total | `gen_ai.usage.total_tokens`, `atlan.usage.total_tokens`, `llm.usage.total_tokens`, `openai.usage.total_tokens`, `llm.token_count.total` | Input plus output, as sent |
| Cache read | `gen_ai.usage.cache_read_input_tokens`, `gen_ai.usage.cache_read.input_tokens`, `gen_ai.usage.cache_read_tokens`, `atlan.usage.cache_read_tokens`, `llm.token_count.prompt_details.cache_read` | `0` |
| Cache write | `gen_ai.usage.cache_creation_input_tokens`, `gen_ai.usage.cache_creation.input_tokens`, `gen_ai.usage.cache_write.input_tokens`, `gen_ai.usage.cache_write_tokens`, `atlan.usage.cache_write_tokens`, `llm.token_count.prompt_details.cache_write`, `llm.token_count.prompt_details.cache_create` | `0` |

Every input key except `atlan.usage.input_tokens` is read as the **whole prompt**, cached tokens included. Atlan subtracts the cache-read and cache-write counts from it to get fresh input. `atlan.usage.input_tokens` is read as fresh input already. The SDK's `usage={"input_tokens": ...}` writes `gen_ai.usage.input_tokens`, so pass the whole prompt count there.

A span's `usage.input_tokens`, read back through the API with `fields=usage`, is the fresh count.

## Cost

| Field | Keys, in order | When missing |
| - | - | - |
| Input cost (USD) | `gen_ai.usage.input_cost`, `atlan.usage.input_cost` | `0` |
| Output cost (USD) | `gen_ai.usage.output_cost`, `atlan.usage.output_cost` | `0` |
| Total cost (USD) | `gen_ai.usage.cost`, `gen_ai.usage.total_cost`, `atlan.usage.total_cost` | Input cost plus output cost |

A cost you record is kept only when Atlan cannot price the call itself. When the span has a model and at least one token count, Atlan prices it at your organization's rates and replaces the recorded cost. If no rate applies, the call is **Unpriced** at zero. See [How cost is calculated](/finops/concepts/how-cost-is-calculated).

## Example

One model-call span from an OpenTelemetry producer, shown as OTLP JSON attributes:

```json theme={null}
[
  {"key": "session.id", "value": {"stringValue": "ticket-4821"}},
  {"key": "atlan.visitor.id", "value": {"stringValue": "<visitor-id-from-identify>"}},
  {"key": "gen_ai.operation.name", "value": {"stringValue": "chat"}},
  {"key": "gen_ai.provider.name", "value": {"stringValue": "anthropic"}},
  {"key": "gen_ai.request.model", "value": {"stringValue": "claude-sonnet-5"}},
  {"key": "gen_ai.usage.input_tokens", "value": {"intValue": "12000"}},
  {"key": "gen_ai.usage.cache_read.input_tokens", "value": {"intValue": "8000"}},
  {"key": "gen_ai.usage.output_tokens", "value": {"intValue": "600"}}
]
```

Atlan records this span as an `llm` call in session `ticket-4821`, with 4,000 fresh input tokens, 8,000 cache-read tokens, and 600 output tokens, then prices it.

## Related

* [How trace attribution works](/registry/traces/concepts/attribution): Owner, workspace, and what Atlan never infers.
* [Create spans for agent run](/tools/sdk/how-tos/tracing): Set these values with the SDK.
* [Trace limits and privacy](/registry/traces/references/limits-and-privacy): Size limits, content policy, and deletion.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.