Migrate an SDK call to the Gateway
lobstack-gateway-migration
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.
Copy the file and save it at the path below, then start a new session. Any agent that reads a skills directory will pick it up; the path shown is Claude Code's. Delete the folder to uninstall.
mkdir -p .claude/skills/lobstack-gateway-migration # paste SKILL.md into .claude/skills/lobstack-gateway-migration/SKILL.md
No package, no registry client, no telemetry. 134 lines of text, licensed MIT.
Frontmatter
An agent decides whether to load a skill from name and description alone — it does not read the body first. That is why the description says when to use this, not only what it is.
--- name: lobstack-gateway-migration description: "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: MIT metadata: title: "Migrate an SDK call to the Gateway" version: 1.0.0 author: lobstack category: migration tags: [migration, openai, anthropic, sdk, gateway] ---
SKILL.md
---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.- `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.


