> ## 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.

# SDK quickstart

> Initialize a tracing client, report one agent run, and verify it landed in Agent Registry.

This walks one agent run from `init()` to a trace you can open in Agent
Registry. It takes a few minutes and needs no framework.

## Before you start

* The SDK installed for your language — see [Install](/sdk/install).
* An Atlan API key. Load it from your environment or secret store; never commit
  it.
* The workspace ID the traces belong to. Exporting to an Atlan endpoint without
  a workspace raises at `init()`, because those traces would be unattributable.

```bash theme={null}
export ATLAN_API_KEY=...
export ATLAN_WORKSPACE_ID=...
```

## 1. Initialize once at startup

Call `init()` a single time, as early as your process allows. It is idempotent
per API key, so a duplicate call returns the existing client rather than
building a second pipeline.

<CodeGroup>
  ```python Python theme={null}
  import atlan_ai

  client = atlan_ai.init(
      service_name="support-rover",  # required; names your agent
  )
  ```

  ```typescript TypeScript theme={null}
  import * as atlan from "@atlanai/sdk";

  const client = atlan.init({
    serviceName: "support-rover", // required; names your agent
  });
  ```
</CodeGroup>

`api_key`, `workspace_id`, and `base_url` fall back to `ATLAN_API_KEY`,
`ATLAN_WORKSPACE_ID`, and `ATLAN_BASE_URL`. Pass them explicitly if your
application resolves secrets itself.

Once a client exists, everything OTel-instrumented in the process — OpenLLMetry,
OpenInference, or a framework with native OTel — exports through the same
pipeline.

## 2. Identify who the run is for (optional)

If the run served a specific person or calling agent, identify them once and
every span in scope carries the same `visitor_id`. Skip this step if the agent
has no external requester — tracing works without it.

<CodeGroup>
  ```python Python theme={null}
  visitor = client.identify(
      identity_source="slack",
      external_id="U0123ABC",
      traits={"name": "Jordan Lee", "email": "jordan@example.com"},
  )
  ```

  ```typescript TypeScript theme={null}
  const visitor = await client.identify({
    identitySource: "slack",
    externalId: "U0123ABC",
    traits: { name: "Jordan Lee", email: "jordan@example.com" },
  });
  ```
</CodeGroup>

Use a stable id from wherever the request originated — a Slack user id, a ticket
requester id, a calling agent's handle. Calls are idempotent on
`(identity_source, external_id)`: the first returns `201`, later ones `200` with
the same Visitor.

<Note>
  Two different failure modes. When Visitor identity is **not configured** — no
  API key, no workspace, invalid base URL — `identify` returns `None`/`null`,
  matching the SDK's no-op posture. When the **call fails** with any non-2xx it
  raises `VisitorIdentifyError`, carrying the status code. So an `if visitor`
  guard covers the first case only; catch the error too if a Gateway failure
  should not abort the run. Requires 0.2.0 or later.
</Note>

## 3. Trace the run

Wrap the work in a span. Inside `propagate_attributes`, every span started in
scope carries the same association attributes — including spans created by
third-party instrumentation you do not control.

<CodeGroup>
  ```python Python theme={null}
  with atlan_ai.propagate_attributes(
      session_id="ticket-4821",
      visitor_id=visitor.id if visitor else None,
  ):
      with client.start_as_current_span("handle-ticket", as_type="task") as root:
          with client.start_as_current_span("chat", as_type="llm") as generation:
              generation.update(
                  model="claude-sonnet-5",
                  provider="anthropic",
                  usage={"input_tokens": 1200, "output_tokens": 340},
                  cost={"input": 0.0036, "output": 0.0051},
              )
          root.score_trace("resolved", value=True, data_type="BOOLEAN")
  ```

  ```typescript TypeScript theme={null}
  await atlan.propagateAttributes(
    { sessionId: "ticket-4821", visitorId: visitor?.id },
    async () => {
      await client.startAsCurrentSpan("handle-ticket", { asType: "task" }, async (root) => {
        await client.startAsCurrentSpan("chat", { asType: "llm" }, async (generation) => {
          generation.update({
            model: "claude-sonnet-5",
            provider: "anthropic",
            usage: { input_tokens: 1200, output_tokens: 340 },
            cost: { input: 0.0036, output: 0.0051 },
          });
        });
        root.scoreTrace("resolved", true, { dataType: "BOOLEAN" });
      });
    },
  );
  ```
</CodeGroup>

## 4. Flush before the process exits

Long-lived services batch automatically. Short-lived processes — a script, a
job, a serverless invocation — must flush, or the last batch is lost.

<CodeGroup>
  ```python Python theme={null}
  client.flush()
  ```

  ```typescript TypeScript theme={null}
  await client.flush();
  ```
</CodeGroup>

## Expected result

One trace named `handle-ticket` with a child `chat` span. The `chat` span
carries the model, provider, token usage, and cost. The trace carries a
`resolved` score and, if you identified one, the `visitor_id`.

## Verify it landed

A successful `flush()` proves the export request was accepted. It does not
prove the trace is visible in the workspace you expected. Confirm both:

1. Open Agent Registry and find the agent named by your `service_name`.
2. Open its most recent session and confirm the trace appears with the child
   span, the recorded cost, and the score.
3. Confirm the trace is in the workspace matching `ATLAN_WORKSPACE_ID` — not
   another workspace your key can also reach.

If nothing appears, set `ATLAN_DEBUG=true` and re-run. Debug mode logs the
export decision and switches to per-span export, which surfaces a rejected
batch immediately instead of at the next flush interval.

<Card title="Diagnose missing traces" icon="magnifying-glass" href="/guides/traces/diagnose-attribution">
  Work through attribution problems when traces arrive but land in the wrong
  place.
</Card>

## Next steps

<CardGroup cols={2}>
  <Card title="Trace your agent" icon="diagram-project" href="/sdk/tracing">
    Observation types, `observe`, and deterministic trace IDs.
  </Card>

  <Card title="Add scores" icon="star" href="/sdk/scores">
    Record evaluation results on a trace.
  </Card>

  <Card title="Privacy controls" icon="shield" href="/sdk/privacy">
    Mask payloads and suppress content.
  </Card>

  <Card title="Configuration" icon="gear" href="/sdk/configuration">
    Every option and environment variable.
  </Card>
</CardGroup>
