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

# How to wrap OpenAI calls and capture full agent traces

> Learn how to initialize TracePilot AI, start a trace, and wrap OpenAI chat completions to capture inputs, outputs, tokens, and latency automatically.

TracePilot AI instruments your OpenAI calls with a single wrapper function. Every call you wrap becomes a structured span in the dashboard — capturing the prompt, the completion, token usage, latency, and any errors — without changing how the rest of your code reads the response.

## Prerequisites

Install the SDK and make sure you have an OpenAI API key ready.

```bash theme={null}
npm install tracepilot-sdk openai
```

Get your TracePilot API key by signing in at [tracepilotai.com](https://tracepilotai.com) with GitHub or Google. Your key looks like `tp_live_xxxxxxxxxxxxxxxx`.

## Wrap your first call

<Steps>
  <Step title="Import and initialize">
    Import `TracePilot` alongside the OpenAI client and create instances of both.

    ```typescript theme={null}
    import { TracePilot } from 'tracepilot-sdk';
    import OpenAI from 'openai';

    const tp = new TracePilot('tp_live_YOUR_KEY');
    const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
    ```
  </Step>

  <Step title="Start a trace">
    Call `tp.startTrace` once at the beginning of each agent run. Pass a name that identifies this agent or workflow — it appears as the trace label in the dashboard.

    ```typescript theme={null}
    await tp.startTrace('customer-support-agent');
    ```
  </Step>

  <Step title="Wrap the OpenAI call">
    Replace a bare `openai.chat.completions.create` call with `tp.wrapOpenAI`. Pass the call as a function and include the messages array as the second argument.

    ```typescript theme={null}
    const messages = [
      { role: 'user', content: 'How do I reset my password?' }
    ];

    const { result, spanId } = await tp.wrapOpenAI(
      () => openai.chat.completions.create({ model: 'gpt-4o-mini', messages }),
      messages
    );

    console.log(result.choices[0].message.content);
    ```
  </Step>
</Steps>

<Note>
  `wrapOpenAI` returns the original OpenAI response object unchanged. Any code that reads `result.choices[0].message.content` or any other field on the completion response continues to work without modification.
</Note>

## Full working example

```typescript theme={null}
import { TracePilot } from 'tracepilot-sdk';
import OpenAI from 'openai';

const tp = new TracePilot('tp_live_YOUR_KEY');
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

async function runAgent() {
  await tp.startTrace('customer-support-agent');

  const messages = [
    { role: 'user', content: 'How do I reset my password?' }
  ];

  const { result, spanId } = await tp.wrapOpenAI(
    () => openai.chat.completions.create({ model: 'gpt-4o-mini', messages }),
    messages
  );

  console.log(result.choices[0].message.content);
  // Open tracepilotai.com/dashboard — your trace is already there.
}

runAgent();
```

After the function runs, open [tracepilotai.com/dashboard](https://tracepilotai.com/dashboard). The trace appears immediately with no additional configuration.

## Optional parameters

`wrapOpenAI` accepts two additional parameters that become useful once your agent has multiple steps.

### `parentSpanId`

Pass the `spanId` returned by a previous `wrapOpenAI` or `wrapToolCall` call to link the current span as a child of that span. This builds a parent-child execution tree in the dashboard.

```typescript theme={null}
// Step 1 — no parent, this is the root span
const { result: plan, spanId: planSpanId } = await tp.wrapOpenAI(
  () => openai.chat.completions.create({ model: 'gpt-4o', messages }),
  messages
);

// Step 2 — linked to the root span
const followUpMessages = [...messages, plan.choices[0].message];
const { result: answer, spanId: answerSpanId } = await tp.wrapOpenAI(
  () => openai.chat.completions.create({ model: 'gpt-4o', messages: followUpMessages }),
  followUpMessages,
  planSpanId  // parentSpanId — makes this a child of step 1
);
```

### `stepOrder`

Pass a number to control the order in which spans are displayed within the same parent. Without `stepOrder`, the dashboard sorts spans by arrival time, which can be inconsistent under concurrency.

```typescript theme={null}
const { result, spanId } = await tp.wrapOpenAI(
  () => openai.chat.completions.create({ model: 'gpt-4o-mini', messages }),
  messages,
  parentSpanId,
  1  // stepOrder — this span appears first in the tree
);
```

<AccordionGroup>
  <Accordion title="Using a custom ingest endpoint">
    If you are self-hosting the TracePilot backend, pass your endpoint as the second argument to the constructor.

    ```typescript theme={null}
    const tp = new TracePilot(
      'tp_live_YOUR_KEY',
      'https://your-server.com/api/ingest'
    );
    ```

    All other methods work identically.
  </Accordion>

  <Accordion title="Error handling">
    If the OpenAI call throws, `wrapOpenAI` captures the error as a failed span in the dashboard and re-throws the original error. Your existing `try/catch` blocks remain effective.

    ```typescript theme={null}
    try {
      const { result, spanId } = await tp.wrapOpenAI(
        () => openai.chat.completions.create({ model: 'gpt-4o-mini', messages }),
        messages
      );
    } catch (err) {
      // The error is already recorded in the dashboard.
      // Handle it here as you normally would.
      console.error(err);
    }
    ```
  </Accordion>
</AccordionGroup>
