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

# Rewrite Endpoint (Cross-Format Proxy)

> Use one SDK format against any provider: OpenAI SDK → Claude, Anthropic SDK → GPT, Bedrock, Google, and more

The **rewrite** endpoint lets you keep your existing SDK (OpenAI, Anthropic, Google, or Bedrock) and point it at a different provider. Lava translates request and response formats so your code stays unchanged while the underlying model and billing go through Lava.

> [!NOTE]
>
> **Unlike the forward endpoint,** Lava **does** translate request and response bodies on rewrite. You send your SDK’s format (e.g. OpenAI `messages` + `model`); Lava converts to the provider’s format and back. The provider never sees your SDK’s schema.

> [!NOTE]
>
> **Prerequisites:** You need a Lava secret key (same auth as the [forward proxy](https://lava.so/docs/gateway/forward-proxy.md)). Use your secret key directly or a forward token for customer billing. Pass via `Authorization: Bearer` or `x-api-key`.

## When to Use Rewrite vs Forward

| Use case | Endpoint |
| - | - |
| Your SDK and provider match (e.g. OpenAI SDK → OpenAI API) | [Forward](https://lava.so/docs/gateway/forward-proxy.md): `POST /v1/forward?u=<provider_url>` |
| Your SDK and provider differ (e.g. Anthropic SDK → OpenAI, OpenAI SDK → Bedrock) | **Rewrite**: `POST /v1/rewrite/{clientFormat}/{providerUrl}` |

With **forward**, you send the provider’s native format. With **rewrite**, you send your SDK’s format and Lava converts to/from the provider’s format.

## URL Format

```
POST https://api.lava.so/v1/rewrite/{clientFormat}/{providerUrl}
```

* **`clientFormat`** — The format your SDK sends and expects: `openai` | `anthropic` | `google` | `bedrock`
* **`providerUrl`** — The provider base URL and path (same style as forward), e.g. `api.openai.com/v1/chat/completions` or `api.anthropic.com/v1/messages`

Your SDK’s base URL is the full rewrite URL including the provider path. The SDK appends its own path (e.g. `/v1/chat/completions`); Lava strips that suffix and forwards to the provider path you put in the URL.

## Authentication

Same as the [forward endpoint](https://lava.so/docs/gateway/forward-proxy.md) — pass your Lava secret key or forward token. When using the OpenAI, Anthropic, or Google SDKs, set `apiKey` to your Lava token and the SDK sends the right header automatically.

## Examples

The simplest way to use the rewrite endpoint — pass `format` to `gateway()` and Lava handles URL construction and token generation. Omit `format` to use the [forward endpoint](https://lava.so/docs/gateway/forward-proxy.md) (no translation).

> [tab] SDK
>
> ```typescript theme={null}
> import { Lava } from ‘@lavapayments/nodejs’;
>
> const lava = new Lava();
>
> // Send OpenAI-format requests to Claude
> const data = await lava.gateway(‘https://api.anthropic.com/v1/messages’, {
>   format: ‘openai’,
>   body: {
>     model: ‘claude-haiku-4-5’,
>     messages: [{ role: ‘user’, content: ‘Hello!’ }],
>   },
> });
>
> // With customer billing
> const billed = await lava.gateway(‘https://api.anthropic.com/v1/messages’, {
>   format: ‘openai’,
>   body: {
>     model: ‘claude-haiku-4-5’,
>     messages: [{ role: ‘user’, content: ‘Hello!’ }],
>   },
>   customer_id: ‘conn_xyz789’,
>   meter_slug: ‘my-meter’,
> });
> ```

> [tab] cURL
>
> ```bash theme={null}
> # Generate a forward token (base64-encoded JSON)
> FORWARD_TOKEN=$(echo -n ‘{"secret_key":"aks_live_abc123..."}’ | base64)
>
> # Send OpenAI-format request to Claude via rewrite
> curl -X POST ‘https://api.lava.so/v1/rewrite/openai/api.anthropic.com/v1/messages’ \
>   -H ‘Content-Type: application/json’ \
>   -H "Authorization: Bearer $FORWARD_TOKEN" \
>   -d ‘{
>     "model": "claude-haiku-4-5",
>     "messages": [{ "role": "user", "content": "Hello!" }]
>   }’
> ```

## Using the OpenAI SDK

With the official `openai` npm package, you use the rewrite endpoint by setting **baseURL** and **apiKey** when creating the client. No other code changes are needed — all `client.chat.completions.create()` calls go through Lava, and Lava translates to the provider you put in the URL.

1. **Install the SDK** (if needed): `npm install openai`
2. **Create the client** with Lava’s rewrite URL as `baseURL` and your Lava token as `apiKey`.
3. **Call the API** as you normally would — use the **provider’s model name** (e.g. `claude-haiku-4-5` when targeting Anthropic, or `us.amazon.nova-lite-v1:0` for Bedrock).

The `baseURL` must be the full rewrite path for the provider you want. The OpenAI SDK appends `/v1/chat/completions`; Lava strips that and forwards to the path in your URL.

> [!NOTE]
>
> To call **OpenAI** with the OpenAI SDK, use the [forward](https://lava.so/docs/gateway/forward-proxy.md) endpoint — the format already matches, so rewrite is unnecessary.

> [tab] Call Claude (Anthropic)
>
> ```javascript theme={null}
> import OpenAI from ‘openai’;
>
> const client = new OpenAI({
>   baseURL: ‘https://api.lava.so/v1/rewrite/openai/api.anthropic.com/v1/messages’,
>   apiKey: process.env.LAVA_FORWARD_TOKEN  // or your Lava secret key
> });
>
> const completion = await client.chat.completions.create({
>   model: ‘claude-haiku-4-5’,
>   max_tokens: 256,
>   messages: [{ role: ‘user’, content: ‘Hello!’ }]
> });
> ```

> [tab] Call Bedrock (e.g. Nova)
>
> ```javascript theme={null}
> import OpenAI from ‘openai’;
>
> const client = new OpenAI({
>   baseURL: ‘https://api.lava.so/v1/rewrite/openai/bedrock-runtime.us-east-1.amazonaws.com/model/us.amazon.nova-lite-v1:0/converse’,
>   apiKey: process.env.LAVA_FORWARD_TOKEN
> });
>
> const completion = await client.chat.completions.create({
>   model: ‘us.amazon.nova-lite-v1:0’,
>   messages: [{ role: ‘user’, content: ‘Hello!’ }]
> });
> ```

> [tab] Call Gemini (Google)
>
> ```javascript theme={null}
> import OpenAI from ‘openai’;
>
> const client = new OpenAI({
>   baseURL: ‘https://api.lava.so/v1/rewrite/openai/generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent’,
>   apiKey: process.env.LAVA_FORWARD_TOKEN
> });
>
> const completion = await client.chat.completions.create({
>   model: ‘gemini-2.5-flash’,
>   messages: [{ role: ‘user’, content: ‘Hello!’ }]
> });
> ```
>
> For **streaming**, use base URL `.../gemini-2.5-flash:streamGenerateContent` (not `:generateContent`) and pass `stream: true`.

Streaming works the same: pass `stream: true` in the request. Tools, `response_format`, and image content are translated automatically when the provider supports them.

## Using the Anthropic (Claude) SDK

With the official `@anthropic-ai/sdk` package, you set **baseURL** and **apiKey** when creating the `Anthropic` client. The SDK sends Anthropic-format requests; Lava translates them to the provider in the URL (OpenAI, Bedrock, Google, etc.) and back to Anthropic format in the response.

1. **Install the SDK** (if needed): `npm install @anthropic-ai/sdk`
2. **Create the client** with Lava’s rewrite URL as `baseURL` and your Lava token as `apiKey`. Use **x-api-key** style (the Anthropic SDK sends it by default when you set `apiKey`).
3. **Call the API** with `client.messages.create()` as usual. Use the **provider’s model name** (e.g. `gpt-4o-mini` when targeting OpenAI).

The Anthropic SDK appends `/v1/messages` to the base URL. Your `baseURL` must include the full provider path so that after Lava strips `/v1/messages`, the remaining path is the correct provider endpoint.

> [!NOTE]
>
> To call **Anthropic (Claude)** with the Anthropic SDK, use the [forward](https://lava.so/docs/gateway/forward-proxy.md) endpoint — the format already matches, so rewrite is unnecessary.

> [tab] Call GPT (OpenAI)
>
> ```javascript theme={null}
> import Anthropic from '@anthropic-ai/sdk';
>
> const client = new Anthropic({
>   baseURL: 'https://api.lava.so/v1/rewrite/anthropic/api.openai.com/v1/chat/completions',
>   apiKey: process.env.LAVA_FORWARD_TOKEN
> });
>
> const message = await client.messages.create({
>   model: 'gpt-4o-mini',
>   max_tokens: 256,
>   messages: [{ role: 'user', content: 'Hello!' }]
> });
> ```

> [tab] Call Bedrock (e.g. Nova)
>
> ```javascript theme={null}
> import Anthropic from '@anthropic-ai/sdk';
>
> const client = new Anthropic({
>   baseURL: 'https://api.lava.so/v1/rewrite/anthropic/bedrock-runtime.us-east-1.amazonaws.com/model/us.amazon.nova-lite-v1:0/converse',
>   apiKey: process.env.LAVA_FORWARD_TOKEN
> });
>
> const message = await client.messages.create({
>   model: 'us.amazon.nova-lite-v1:0',
>   max_tokens: 256,
>   messages: [{ role: 'user', content: 'Hello!' }]
> });
> ```

> [tab] Call Gemini (Google)
>
> ```javascript theme={null}
> import Anthropic from '@anthropic-ai/sdk';
>
> const client = new Anthropic({
>   baseURL: 'https://api.lava.so/v1/rewrite/anthropic/generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent',
>   apiKey: process.env.LAVA_FORWARD_TOKEN
> });
>
> const message = await client.messages.create({
>   model: 'gemini-2.5-flash',
>   max_tokens: 256,
>   messages: [{ role: 'user', content: 'Hello!' }]
> });
> ```
>
> For **streaming**, use base URL `.../gemini-2.5-flash:streamGenerateContent` (not `:generateContent`) and pass `stream: true`.

For streaming, set `stream: true` in `messages.create()`. Tool use, system prompts, and image blocks are translated to the target provider where supported.

## Streaming

Set `stream: true` (or the provider’s equivalent) in the request body. Lava streams responses back in your SDK’s format (e.g. OpenAI SSE or Anthropic SSE). **Bedrock:** pointing at `/converse` with `stream: true` automatically uses `/converse-stream` on the provider side. **Google:** for streaming, use the stream endpoint in the path (e.g. `.../gemini-2.5-flash:streamGenerateContent` instead of `:generateContent`).

## Input Format Override

The endpoint infers the client format from the URL path. If you need to override (e.g. custom client), set:

```
X-Lava-Input-Format: openai | anthropic | google | bedrock
```

## Structured Output (JSON / JSON Schema)

Lava translates structured-output options across providers:

* **`response_format: { type: "json_object" }`** (OpenAI) is mapped to provider-specific mechanisms (e.g. system prompt for Anthropic/Bedrock, `responseMimeType` for Google).
* **`response_format: { type: "json_schema", json_schema: { ... } }`** (OpenAI) is mapped to Anthropic’s `output_config`, Google’s `responseSchema`, or Bedrock’s system prompt as appropriate.

You can send OpenAI-style `response_format` when using the OpenAI SDK against Anthropic or Google and get valid structured responses.

## Images

* **URLs:** For providers that require base64 (Anthropic, Google, Bedrock), Lava fetches image URLs and converts them to the format the provider expects.
* **Data URLs** (`data:image/...;base64,...`) work without fetching.
* **Anthropic base64 image blocks** are converted to OpenAI-style `image_url` or data URLs when the provider is OpenAI.

## Tool Calls

Tool definitions and tool-call/tool-result messages are translated between OpenAI, Anthropic, Google, and Bedrock formats. Use your SDK’s normal tool schema; Lava maps to the provider’s format.

## Model Fallbacks

The rewrite endpoint supports automatic failover to a different provider or model when the primary request fails. Pass a fallback chain in the **`x-lava-fallbacks`** request header as a JSON array of `{url, model}` objects:

```
x-lava-fallbacks: [{"url":"api.openai.com/v1/chat/completions","model":"gpt-4o"}]
```

Each entry specifies a scheme-less provider URL (same format as the rewrite path) and the model to use at that provider. Entries are tried in order — the first successful response wins.

**Trigger conditions** — a fallback attempt is made when the primary provider returns:

* A **5xx** server error (500, 502, 503, etc.)
* **429** rate limiting
* A **network-level failure** (connection refused, timeout, etc.)

Client errors (400, 401, 402, 403, 404) do not trigger fallback.

**Cross-provider failover** — fallbacks can cross provider formats. For example, if your primary is Claude (Anthropic format) and your fallback is GPT (OpenAI format), Lava re-translates the original request body into the fallback provider’s format automatically.

**Example: Claude primary with GPT fallback**

```bash theme={null}
curl -X POST ‘https://api.lava.so/v1/rewrite/openai/api.anthropic.com/v1/messages’ \
  -H ‘Content-Type: application/json’ \
  -H ‘Authorization: Bearer $LAVA_FORWARD_TOKEN’ \
  -H ‘x-lava-fallbacks: [{"url":"api.openai.com/v1/chat/completions","model":"gpt-4o"}]’ \
  -d ‘{
    "model": "claude-haiku-4-5",
    "messages": [{ "role": "user", "content": "Hello!" }]
  }’
```

If Anthropic returns 5xx or 429, Lava retries the request against OpenAI with `model: "gpt-4o"`. No request translation is needed since the fallback also accepts OpenAI format.

**Multi-hop chain** — chain multiple fallbacks for maximum resilience:

```
x-lava-fallbacks: [
  {"url":"api.anthropic.com/v1/messages","model":"claude-haiku-4-5"},
  {"url":"generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent","model":"gemini-2.5-flash"}
]
```

> [!WARNING]
>
> **Unmanaged provider keys are not supported with fallbacks.** If you pass a provider API key directly in the request, Lava returns a `400 rewrite_fallback_unmanaged_key` error. Fallbacks require Lava-managed credentials so Lava can authenticate against any fallback provider. Use a Lava secret key or forward token instead.

| Error code | Status | Cause |
| - | - | - |
| `rewrite_fallback_header_invalid` | 400 | `x-lava-fallbacks` is present but not a valid JSON array of `{url, model}` objects |
| `rewrite_fallback_unmanaged_key` | 400 | Fallbacks requested but auth uses an unmanaged provider key |

## Error Handling

| Status | Cause | Retryable |
| - | - | - |
| **401** | Invalid or malformed token, or wrong auth header | No — check credentials and use Bearer or x-api-key |
| **402** | Insufficient credit balance | No — add funds first |
| **400** | Invalid `clientFormat`, unsupported translation, or malformed body | No — check URL path and request body |
| **429** | Rate limit exceeded | Yes — back off and retry |
| **500/502/503** | Provider or Lava server error | Yes — retry with backoff |

Provider error responses are translated back into your SDK’s format when possible.

## Troubleshooting

> [accordion] 401 Unauthorized
>
> Verify your Lava token (forward token or secret key), ensure the correct header (`Authorization: Bearer` or `x-api-key`) is set, and try the forward token from **Gateway > Secrets** to isolate the issue.

> [accordion] 400 or wrong response format
>
> Check that the URL path uses the correct `clientFormat` (openai, anthropic, google, bedrock) and that the provider path matches the provider you intend to call. The request body must be in your SDK’s format — Lava translates it to the provider.

> [accordion] CORS errors in browser
>
> Lava blocks frontend requests to prevent token exposure. Always call Lava from your backend: `Frontend → Your Backend → Lava → AI Provider`.

> [accordion] How does path stripping work?
>
> When using the OpenAI or Anthropic SDK with `baseURL`, the SDK appends its own path (e.g. `/v1/chat/completions` or `/v1/messages`). Lava automatically strips these suffixes so the provider sees the correct endpoint.
>
> | Client format | SDK appends | Lava strips |
> | - | - | - |
> | `openai` | `/v1/chat/completions` | That suffix |
> | `anthropic` | `/v1/messages` | That suffix |
> | `google` | Varies (no fixed suffix) | Nothing — put the full provider path in the URL |
> | `bedrock` | Varies (`/converse`, `/invoke`) | When using OpenAI/Anthropic SDK → Bedrock, Lava strips the SDK suffix as above |
>
> **Example:** OpenAI SDK → Anthropic with `baseURL` set to `https://api.lava.so/v1/rewrite/openai/api.anthropic.com/v1/messages`:
>
> 1. SDK sends: `.../api.anthropic.com/v1/messages/v1/chat/completions`
> 2. Lava strips: `/v1/chat/completions`
> 3. Provider receives: `https://api.anthropic.com/v1/messages`
>
> This is handled automatically — if you use `lava.gateway({ format })`, you don't need to think about this at all.

## Fallback: Query Parameter

You can still use the `?u=` style as a fallback:

`POST /v1/rewrite?u=<encoded_provider_url>`

The URL does not include `clientFormat`, so you must send the **`X-Lava-Input-Format`** header (`openai`, `anthropic`, `google`, or `bedrock`) so Lava knows how to interpret the request body. The **body and headers must be in your client’s SDK format** (e.g. OpenAI or Anthropic), not the provider’s; Lava will translate to the provider. The path-based form above is preferred for SDK base URLs.

## Next Steps

> [card] [Forward Proxy](https://lava.so/docs/gateway/forward-proxy.md)
>
> When your SDK and provider match, use the forward endpoint

> [card] [Supported Providers](https://lava.so/docs/gateway/supported-providers.md)
>
> List of providers you can route through Lava


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.