> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://respan.ai/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://respan.ai/docs/_mcp/server.

# Writer (tracing)

> Trace Writer SDK calls with Respan in Python or TypeScript.

[Writer](https://writer.com/) is an AI content platform with models for text, vision, translation, and knowledge graph workflows. Respan instruments Writer SDK calls and exports chat and text-generation spans for observability.

#### Set up Respan

Create an account at [platform.respan.ai](https://platform.respan.ai) and grab an [API key](https://platform.respan.ai/platform/gateway/api-keys).

Run `npx @respan/cli setup` to set up with your coding agent.

#### Example projects

* [Python examples](https://github.com/respanai/respan-example-projects/tree/main/python/tracing/writer)
* [TypeScript examples](https://github.com/respanai/respan-example-projects/tree/main/typescript/tracing/writer)

## Setup

#### Install packages

**`Python`**

```bash Python
pip install respan-ai respan-instrumentation-writer writerai
```

**`TypeScript`**

```bash TypeScript
npm install @respan/respan @respan/tracing @respan/instrumentation-writer writer-sdk dotenv
```

#### Set environment variables

```bash
export RESPAN_API_KEY="YOUR_RESPAN_API_KEY"
export WRITER_API_KEY="YOUR_WRITER_API_KEY"
```

Optional:

```bash
export RESPAN_BASE_URL="https://api.respan.ai/api"
export WRITER_MODEL="palmyra-x5"
```

#### Initialize and run

**`Python`**

```python Python
import os

from respan import Respan, workflow
from respan_instrumentation_writer import WriterInstrumentor
from writerai import Writer

respan = Respan(
    api_key=os.environ["RESPAN_API_KEY"],
    base_url=os.getenv("RESPAN_BASE_URL", "https://api.respan.ai/api"),
    instrumentations=[WriterInstrumentor()],
)

client = Writer(api_key=os.environ["WRITER_API_KEY"])


@workflow(name="writer_chat.workflow")
def run_writer_chat() -> str:
    completion = client.chat.chat(
        model=os.getenv("WRITER_MODEL", "palmyra-x5"),
        messages=[{"role": "user", "content": "Write one line about tracing."}],
        max_tokens=64,
    )
    return completion.choices[0].message.content


print(run_writer_chat())
respan.shutdown()
```

**`TypeScript`**

```typescript TypeScript
import "dotenv/config";
import Writer from "writer-sdk";
import { Respan } from "@respan/respan";
import { WriterInstrumentor } from "@respan/instrumentation-writer";

const respan = new Respan({
  apiKey: process.env.RESPAN_API_KEY,
  baseURL: process.env.RESPAN_BASE_URL,
  instrumentations: [new WriterInstrumentor()],
});
await respan.initialize();

const writer = new Writer({ apiKey: process.env.WRITER_API_KEY });

try {
  const completion = await respan.withWorkflow({ name: "writer_chat.workflow" }, async () => {
    return writer.chat.chat({
      model: process.env.WRITER_MODEL ?? "palmyra-x5",
      messages: [{ role: "user", content: "Write one line about tracing." }],
      max_tokens: 64,
    });
  });
  console.log(completion.choices[0]?.message?.content ?? "");
} finally {
  await respan.shutdown();
}
```

#### View your trace

Open the [Traces page](https://platform.respan.ai/platform/traces) and search for workflow name `writer_chat.workflow`.

## Configuration

| Parameter             | Type                  | Default     | Description                                                                                                 |
| --------------------- | --------------------- | ----------- | ----------------------------------------------------------------------------------------------------------- |
| `api_key`             | `str \| None`         | `None`      | Falls back to `RESPAN_API_KEY` env var.                                                                     |
| `base_url`            | `str \| None`         | `None`      | Falls back to `RESPAN_BASE_URL` env var.                                                                    |
| `instrumentations`    | `list`                | `[]`        | Plugin instrumentations to activate, such as `WriterInstrumentor()` or `new WriterInstrumentor()`.          |
| `sdkModule`           | `object \| undefined` | `undefined` | TypeScript only. Pass the imported `writer-sdk` module for runtimes that resolve multiple module instances. |
| `customer_identifier` | `str \| None`         | `None`      | Default customer identifier for all spans.                                                                  |
| `metadata`            | `dict \| None`        | `None`      | Default metadata attached to all spans.                                                                     |
| `environment`         | `str \| None`         | `None`      | Environment tag, such as `"production"`.                                                                    |

## Supported calls

| SDK call                         | Traced                                                 |
| -------------------------------- | ------------------------------------------------------ |
| `client.chat.chat(...)`          | Chat completion spans, including streaming responses   |
| `client.chat.parse(...)`         | Structured-output chat spans                           |
| `client.chat.stream(...)`        | Streaming chat spans after the stream is consumed      |
| `client.completions.create(...)` | Text completion spans, including streaming completions |

## Attributes

### In Respan()

**`Python`**

```python Python
from respan import Respan
from respan_instrumentation_writer import WriterInstrumentor

respan = Respan(
    instrumentations=[WriterInstrumentor()],
    customer_identifier="user_123",
    metadata={"service": "writer-api", "version": "1.0.0"},
)
```

**`TypeScript`**

```typescript TypeScript
import { Respan } from "@respan/respan";
import { WriterInstrumentor } from "@respan/instrumentation-writer";

const respan = new Respan({
  instrumentations: [new WriterInstrumentor()],
});
```

### With propagate_attributes

**`Python`**

```python Python
import os

from respan import Respan, propagate_attributes
from respan_instrumentation_writer import WriterInstrumentor
from writerai import Writer

respan = Respan(instrumentations=[WriterInstrumentor()])
client = Writer(api_key=os.environ.get("WRITER_API_KEY", "local-mock-token"))


def run_writer_with_attributes(user_id: str):
    with propagate_attributes(
        customer_identifier=user_id,
        thread_identifier="writer_thread",
        metadata={"plan": "enterprise"},
    ):
        completion = client.chat.chat(
            model=os.getenv("WRITER_MODEL", "palmyra-x5"),
            messages=[{"role": "user", "content": "Write one sentence for trace demos."}],
            max_tokens=32,
        )
        print(completion.choices[0].message.content)
```

**`TypeScript`**

```typescript TypeScript
async function runWriterWithAttributes(userId: string) {
  await respan.propagateAttributes(
    {
      customer_identifier: userId,
      thread_identifier: "writer_thread",
      metadata: { plan: "enterprise" },
    },
    async () => {
      const completion = await writer.chat.chat({
        model: process.env.WRITER_MODEL ?? "palmyra-x5",
        messages: [{ role: "user", content: "Write one sentence for trace demos." }],
        max_tokens: 32,
      });
      console.log(completion.choices[0]?.message?.content ?? "");
    }
  );
}
```

| Attribute             | Type   | Description                                           |
| --------------------- | ------ | ----------------------------------------------------- |
| `customer_identifier` | `str`  | Identifies the end user in Respan analytics.          |
| `thread_identifier`   | `str`  | Groups related messages into a conversation.          |
| `metadata`            | `dict` | Custom key-value pairs. Merged with default metadata. |