> ## 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>

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

## When to Use Rewrite vs Forward

| Use case                                                                         | Endpoint                                                               |
| -------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| Your SDK and provider match (e.g. OpenAI SDK → OpenAI API)                       | [Forward](/gateway/forward-proxy): `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](/gateway/forward-proxy) — 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](/gateway/forward-proxy) (no translation).

<Tabs>
  <Tab title="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>

  <Tab title="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!" }]
      }’
    ```
  </Tab>
</Tabs>

## 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](/gateway/forward-proxy) endpoint — the format already matches, so rewrite is unnecessary.
</Note>

<Tabs>
  <Tab title="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>

  <Tab title="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>

  <Tab title="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`.
  </Tab>
</Tabs>

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](/gateway/forward-proxy) endpoint — the format already matches, so rewrite is unnecessary.
</Note>

<Tabs>
  <Tab title="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>

  <Tab title="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>

  <Tab title="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`.
  </Tab>
</Tabs>

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.
</Warning>

| 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

<AccordionGroup>
  <Accordion title="401 Unauthorized" icon="lock">
    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>

  <Accordion title="400 or wrong response format" icon="code">
    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>

  <Accordion title="CORS errors in browser" icon="shield">
    Lava blocks frontend requests to prevent token exposure. Always call Lava from your backend: `Frontend → Your Backend → Lava → AI Provider`.
  </Accordion>

  <Accordion title="How does path stripping work?" icon="scissors">
    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.
  </Accordion>
</AccordionGroup>

## 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

<CardGroup cols={2}>
  <Card title="Forward Proxy" icon="route" href="/gateway/forward-proxy">
    When your SDK and provider match, use the forward endpoint
  </Card>

  <Card title="Supported Providers" icon="robot" href="/gateway/supported-providers">
    List of providers you can route through Lava
  </Card>
</CardGroup>
