Skip to content

Migration · For developers · MIT

Migrate an SDK call to the Gateway

Move existing OpenAI or Anthropic SDK code onto the Lobstack Gateway by changing the base URL and the key, and verify it actually routed.

Installing a skill

When an agent loads it

Use when asked to put an app behind Lobstack, consolidate providers behind one endpoint, or add per-call cost data to code that already calls a model API.

Inside the file

  • The two lines
  • The trap that costs an afternoon
  • Coming from the Anthropic SDK
  • What changes, and what does not
  • Verify it actually routed
  • Rolling back

The file

v1.0.0
# In Lobstack: Skills › Add a skill, then paste the file or drop the folder
# In Claude Code:
mkdir -p .claude/skills/lobstack-gateway-migration
# paste SKILL.md into .claude/skills/lobstack-gateway-migration/SKILL.md
lobstack-gateway-migration/SKILL.md140 lines
---name: lobstack-gateway-migrationdescription: "Move existing OpenAI or Anthropic SDK code onto the Lobstack Gateway by changing the base URL and the key, and verify it actually routed. Use when asked to put an app behind Lobstack, consolidate providers behind one endpoint, or add per-call cost data to code that already calls a model API."license: MITmetadata:  title: "Migrate an SDK call to the Gateway"  version: 1.0.0  author: lobstack  category: migration  tags: [migration, openai, anthropic, sdk, gateway]--- # Migrate an SDK call to the Lobstack Gateway The Gateway is OpenAI-compatible: POST /v1/chat/completions, same requestbody, same response body, streaming included. A migration is two lines, and thenone verification step that people skip. ## The two lines ```tsimport OpenAI from "openai"; const client = new OpenAI({  apiKey: process.env.LOBSTACK_API_KEY,          // lsk_live_… or lsk_test_…  baseURL: "https://www.lobstack.ai/api/gateway/v1",}); // Unchanged from here down.const res = await client.chat.completions.create({  model: "auto",                                  // or a specific Lobstack key  messages: [{ role: "user", content: "hello" }],});``` ```pythonfrom openai import OpenAI client = OpenAI(    api_key=os.environ["LOBSTACK_API_KEY"],    base_url="https://www.lobstack.ai/api/gateway/v1",)``` ```bashcurl https://www.lobstack.ai/api/gateway/v1/chat/completions \  -H "Authorization: Bearer $LOBSTACK_API_KEY" \  -H "Content-Type: application/json" \  -d '{"model":"auto","messages":[{"role":"user","content":"hello"}]}'``` ## The trap that costs an afternoon Use the host with the www. The bare apex 307s to it, and[RFC 9110](https://www.rfc-editor.org/rfc/rfc9110#name-redirection-3xx) requiresevery HTTP client to drop the `Authorization` header when a redirect changeshost. curl, requests, httpx (so the OpenAI Python SDK), Go, Java and PowerShellall honour it. So an apex base URL answers a caller holding a perfectly valid key with a 401reading *missing credentials*, and the obvious conclusion to draw is that the keyis bad. It is not. The host is. ## Coming from the Anthropic SDK The Gateway exposes the OpenAI shape only. There is no /v1/messages on it, so@anthropic-ai/sdk and anthropic cannot be pointed at it by changing a baseURL. Switch the call site to the OpenAI SDK (or plain fetch) and keep askingfor the Anthropic model by its Lobstack key — the Gateway translates toAnthropic's native Messages API on the way out, including streaming. | Anthropic SDK | Gateway equivalent || --- | --- || `messages.create({ system, messages })` | one `{ role: "system" }` message at the head of `messages` || `max_tokens` (required) | `max_tokens` (optional) || `content: [{type:"text"}]` blocks | a plain string, or OpenAI content parts || `tools[].input_schema` | `tools[].function.parameters` || `stop_reason: "tool_use"` | `finish_reason: "tool_calls"` | ## What changes, and what does not Does not change: the request body, the response body, streaming, tool calling,system messages, multi-turn history, your error handling for 4xx and 5xx. Changes: - The model string. Ask GET /api/gateway/v1/models for the keys this  deployment serves rather than guessing. "auto" hands model selection to the  router; a specific key pins it. An unknown key is a 400, including after alias  resolution.- You get a receipt. Cost, the tier, the model actually served, and the  request id come back on every call. See the lobstack-receipts skill; the  point of the migration is largely this.- `temperature` is only forwarded when you set it. The Gateway does not  invent a sampling default, because doing so silently changes the character of  the output. And it is not forwarded to models that reject it — Anthropic  deprecated the sampling parameters on Opus 4.7 and everything after, and those  models return a hard 400 rather than ignoring the field. When one is dropped  the response says x-lobstack-dropped-params: temperature.- Other OpenAI parameters go where the provider accepts them. user,  response_format, tool_choice, parallel_tool_calls, top_p,  stop, seed and the two penalties are forwarded per provider; any the  serving provider will not receive is named in x-lobstack-dropped-params.  The matrix is on /docs/gateway/chat. n and logprobs are ignored with  no header.- `stream_options.include_usage` is ignored. The final chunk carries usage  and cost regardless. ## Verify it actually routed This is the step people skip, and skipping it is how a migration "succeeds"while every call still goes direct to a provider. ```bashcurl -sD /dev/stderr https://www.lobstack.ai/api/gateway/v1/chat/completions \  -H "Authorization: Bearer $LOBSTACK_API_KEY" \  -H "Content-Type: application/json" \  -d '{"model":"auto","messages":[{"role":"user","content":"2+2"}]}' \  -o /dev/null 2>&1 | grep -i x-lobstack``` Three things must be true: 1. x-lobstack-request-id is present. If it is not, you are not talking to the   Gateway at all.2. x-lobstack-priced: true. If it is false, the model key is outside the   registry and this traffic is invisible on the bill.3. x-lobstack-mode says what you expect — managed on Lobstack keys,   byok on your own. Then grep the repository for the provider hostnames to find the call sites themigration missed. See the audit-unmetered-model-calls skill; on any codebasewith more than one team in it, there is always at least one. ## Rolling back Reverse the two lines. Nothing else changed, no data was migrated, and noprovider-side state exists on the Lobstack side to unwind. Treat that as thereason to migrate incrementally: one service, verified, then the next.

More for developers

5
  • Migration

    Put Cursor or Aider behind the Gateway

    Route Cursor, Aider, Continue or any OpenAI-compatible tool through the Lobstack Gateway with a local proxy, and see a cost line per request.

  • Cost

    Read a Lobstack receipt

    Read the receipt Lobstack returns on every model call — cost_usd, savings_usd, baseline_reason — and report cost from it instead of estimating.

  • Cost

    Pick a model tier, not a flagship

    Choose the cheapest model tier that can actually do the task instead of defaulting to a flagship, and verify the choice before spending on it.

  • Cost

    Keep an agent inside a budget

    Hold an agent, job or run inside a hard dollar budget using the per-call cost the Gateway returns, and handle the 402 when an allowance runs out.

  • Cost

    Write a cost-aware system prompt

    Write or review a system prompt that does not quietly multiply the bill — bounded output, no re-sent bulk, tool schemas counted as the input tokens they are.

All developer skills →