# OpenAI integration

## Base URL

```
https://www.spendline.ai/v1
```

**With `/v1`.** The OpenAI SDK appends `/chat/completions` or `/responses`
itself. Applies to every OpenAI-compatible client (DeepSeek, Qwen, Together,
Fireworks, Groq, Mistral, xAI, Gemini's OpenAI-compatible endpoint).

## Auth

Two separate credentials, two separate headers:

| Header | Value |
|---|---|
| `Authorization: Bearer …` | The application's **own OpenAI key**. Forwarded upstream. |
| `x-spendline-key` | The **Spendline** key. Consumed by Spendline, stripped before forwarding. |

## Python

```python
import os
import uuid
from openai import OpenAI

client = OpenAI(
    base_url="https://www.spendline.ai/v1",
    api_key=os.environ["OPENAI_API_KEY"],
    default_headers={
        "x-spendline-key": os.environ["SPENDLINE_API_KEY"],
        "x-agent-id": "doc-summarizer",
        "x-spendline-tags": '{"team":"platform","env":"prod"}',
    },
)

resp = client.chat.completions.create(
    model="gpt-5.6",
    messages=[{"role": "user", "content": "Summarise this."}],
    extra_headers={
        "x-customer-id": customer_id,                # per request
        "Idempotency-Key": str(uuid.uuid4()),        # per logical request
    },
)
```

## TypeScript

```ts
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://www.spendline.ai/v1",
  apiKey: process.env.OPENAI_API_KEY!,
  defaultHeaders: {
    "x-spendline-key": process.env.SPENDLINE_API_KEY!,
    "x-agent-id": "doc-summarizer",
    "x-spendline-tags": '{"team":"platform","env":"prod"}',
  },
});

const resp = await client.chat.completions.create(
  { model: "gpt-5.6", messages: [{ role: "user", content: "Summarise this." }] },
  { headers: { "x-customer-id": customerId, "Idempotency-Key": crypto.randomUUID() } },
);
```

**`Idempotency-Key` is required** on every proxied request to the hosted
service (a request without one is refused with `400 idempotency_key_required`
before anything is sent to the provider). Generate one value (a UUID) per
logical request **before the first attempt** and reuse it on every retry of
that request. The SDK's own automatic retries reuse the headers of the call, so
generating it in the call, as below, is correct. If you write your own retry
loop, create the key outside the loop: a fresh key per attempt makes each retry
a separate purchase.


## Streaming

Supported on `/v1/chat/completions`. Pass `stream: true` as normal. Spendline
streams the response through and records usage when the stream completes.

## Responses API

`POST https://www.spendline.ai/v1/responses`: same base URL.

- **Non-streaming only.** `stream: true` is rejected with a clear error.
- OpenAI models only.
- Built-in tools such as `web_search` work, and their cost is included in the
  recorded spend.

```python
resp = client.responses.create(
    model="gpt-5.6",
    input="What changed in our pricing page this week?",
    tools=[{"type": "web_search"}],
    extra_headers={"x-customer-id": customer_id, "Idempotency-Key": str(uuid.uuid4())},
)
```

## Framework wrappers

**LangChain**: `ChatOpenAI(base_url=..., default_headers={...})`.

**Vercel AI SDK**, 
`createOpenAI({ baseURL: "https://www.spendline.ai/v1", headers: {...} })`.

In every framework, `x-customer-id` must still vary per request. Pass headers
at call time where the framework allows it, or construct the client where the
customer is known.

## Not proxied

Embeddings, image generation, audio and vector search. Leave those pointed at
`api.openai.com`.
