# Anthropic

> Track Anthropic SDK calls from Python or TypeScript

## Language support

| Setup option | Python | TypeScript |
| - | :-: | :-: |
| Voxmith skill | ✓ | ✓ |
| Voxmith SDK | ✓ | ✓ |
| OpenTelemetry | Automatic | Manual |

## Choose a setup method

| Method | Use it when |
| - | - |
| **Voxmith skill** | You want your coding agent to inspect the app, make the change, and verify it. |
| **Voxmith SDK** | You want explicit control over the interaction boundaries and fields. |
| **OpenTelemetry** | The framework already emits useful spans or you operate an OTel pipeline. |

Start with one method for a call path. Combining SDK tracking with framework
OpenTelemetry on the same call can create duplicate interactions.

## Recommended: Voxmith skill

<Note>
  **Recommended.** Choose this path when you want your coding agent to inspect the
  project, select a supported transport, make the smallest instrumentation change,
  send a test event, and verify that it reached Voxmith.
</Note>

Install the skill once in your coding-agent environment:

```bash theme={null}
npx skills add Voxmith/skills --skill voxmith
```

Run the framework-specific prompt below from the application root. Review the
resulting diff before deploying it.

```text theme={null}
Use the voxmith skill to add Voxmith analytics to this Anthropic application.
Org ID: your-org-id
Instrument the real messages.create() path and verify one fresh interaction.
```

## Manual setup: Voxmith SDK

<CodeGroup>
  ```bash Python theme={null}
  pip install voxmith
  ```

  ```bash TypeScript theme={null}
  npm install voxmithai
  ```
</CodeGroup>

<CodeGroup>
  ```python Python theme={null}
  import voxmith
  from anthropic import Anthropic

  voxmith.init("your-org-id")
  client = Anthropic()
  interaction = voxmith.begin(user_id="u-42", agent_name="anthropic-support", input=prompt)
  try:
      response = client.messages.create(model="claude-sonnet-4-5", max_tokens=512,
                                        messages=[{"role": "user", "content": prompt}])
      interaction.end(output="".join(block.text for block in response.content if block.type == "text"))
  except Exception as exc:
      interaction.end(output=str(exc), success=False)
      raise
  finally:
      voxmith.shutdown()
  ```

  ```typescript TypeScript theme={null}
  import Anthropic from '@anthropic-ai/sdk';
  import * as voxmith from 'voxmithai';

  voxmith.init('your-org-id');
  const client = new Anthropic();
  const interaction = voxmith.begin({
    userId: 'u-42',
    agentName: 'anthropic-support',
    input: prompt,
  });

  try {
    const response = await client.messages.create({
      model: 'claude-sonnet-4-5',
      max_tokens: 512,
      messages: [{ role: 'user', content: prompt }],
    });
    const output = response.content
      .filter((block) => block.type === 'text')
      .map((block) => block.text)
      .join('');
    interaction.end(output);
  } catch (error) {
    interaction.end(String(error), false);
    throw error;
  } finally {
    await voxmith.shutdown();
  }
  ```
</CodeGroup>

## Manual setup: OpenTelemetry

### Python: automatic instrumentation

### Install

```bash theme={null}
pip install anthropic openinference-instrumentation-anthropic \
            opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
```

### Setup

```python theme={null}
import os
from anthropic import Anthropic
from openinference.instrumentation.anthropic import AnthropicInstrumentor
from openinference.instrumentation import using_attributes
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter

os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://otel.voxmith.com"
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = "X-Voxmith-Org-ID=<your-org-id>"

provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(provider)
AnthropicInstrumentor().instrument(tracer_provider=provider)

client = Anthropic()
with using_attributes(session_id="sess-123", user_id="u-42"):
    client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Hi"}],
    )
```

Tool-use blocks are unpacked. You get `llm.input_messages.*`, `llm.output_messages.*`, `tool.name`, `tool.parameters`, and `llm.token_count.{prompt,completion}`.

### Alternative

The Traceloop alternative (`opentelemetry-instrumentation-anthropic`) emits `gen_ai.*` and `traceloop.*`: also recognized by Voxmith.

### Verify

Run one `client.messages.create` call, then open **Events** in Voxmith. Confirm message and tool-use attributes appear as expected.

### Troubleshooting

* Confirm `AnthropicInstrumentor().instrument(...)` runs before Anthropic calls.
* Confirm `OTEL_EXPORTER_OTLP_HEADERS` contains `X-Voxmith-Org-ID=<your-org-id>`.
* Confirm `using_attributes` wraps the call you want grouped.

### References

* [Enable OpenTelemetry export](https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-anthropic)
* [Add custom metadata](https://arize.com/docs/phoenix/tracing/how-to-tracing/add-metadata/customize-spans)

### TypeScript: manual instrumentation

Install the OpenTelemetry packages alongside your existing Anthropic SDK:

```bash theme={null}
npm install @opentelemetry/api @opentelemetry/sdk-node \
  @opentelemetry/exporter-trace-otlp-proto
```

Start OpenTelemetry before creating the Anthropic client, then wrap the call you
want to track:

```typescript theme={null}
import Anthropic from '@anthropic-ai/sdk';
import { SpanStatusCode, trace } from '@opentelemetry/api';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: 'https://otel.voxmith.com/v1/traces',
    headers: { 'X-Voxmith-Org-ID': process.env.VOXMITH_ORG_ID! },
  }),
});
await sdk.start();

const client = new Anthropic();
const tracer = trace.getTracer('anthropic-app');

const response = await tracer.startActiveSpan('anthropic.messages.create', async (span) => {
  span.setAttribute('gen_ai.operation.name', 'chat');
  span.setAttribute('gen_ai.provider.name', 'anthropic');
  span.setAttribute('gen_ai.request.model', 'claude-sonnet-4-5');
  span.setAttribute('gen_ai.conversation.id', 'conversation-123');
  span.setAttribute('user.id', 'user-42');
  span.setAttribute('input.value', prompt);

  try {
    const message = await client.messages.create({
      model: 'claude-sonnet-4-5',
      max_tokens: 512,
      messages: [{ role: 'user', content: prompt }],
    });
    const output = message.content
      .filter((block) => block.type === 'text')
      .map((block) => block.text)
      .join('');
    span.setAttribute('output.value', output);
    return message;
  } catch (error) {
    span.recordException(error as Error);
    span.setStatus({ code: SpanStatusCode.ERROR });
    throw error;
  } finally {
    span.end();
  }
});

await sdk.shutdown();
```

Run one message call, then open **Events** in Voxmith and confirm the input,
output, model, user, and conversation fields are present.

## Next steps

* [Conversations](/using-conversations): review the complete Anthropic interaction.
* [Events](/using-events): inspect the SDK or OpenTelemetry records.
* [Intents](/using-intents): organize production conversations by what users wanted.
