Skip to content

Migrating from another gateway

What carries over unchanged, what is named differently, and what we do not implement.

6 min read

The swap

Coming from OpenAI directly, or from a gateway that speaks the same format, the diff is three lines.

diff
- baseURL: "https://api.openai.com/v1",
- apiKey:  process.env.OPENAI_API_KEY,
- model:   "gpt-4o",
+ baseURL: "https://api.multigrid.ai/v1",
+ apiKey:  process.env.MULTIGRID_API_KEY,
+ model:   "openai/gpt-4o",

Model ids are the one thing that always needs touching: ours are namespaced vendor/model. GET /models is public, so you can map your ids before you change any code.

What carries over

The request body is OpenAI’s and validation is deliberately shallow — we check that model is a non-empty string and that messages has at least one entry, and let everything else through. A parameter a vendor added last week reaches them untouched rather than being rejected by us for being unfamiliar.

The routing fields are named the same way OpenRouter names them, on purpose: models for a fallback chain and provider: { order, ignore, allow_fallbacks, sort } for provider preferences. A config carried over keeps working rather than being silently ignored, which would change your bill without changing your code. See provider preferences.

What is different

  • Responses carry a multigrid object. Model, provider, attempts and exact cost. Additive — clients that do not know about it ignore it.
  • Errors use OpenAI’s envelope with our codes. { error: { message, type, code, request_id } }. Existing error handling keeps parsing; the code values are ours. See the error reference.
  • Streams end with our terminal chunk. Usage and cost arrive on a final chunk before data: [DONE], so a client that stops reading at the first finish_reason will miss the numbers but not the answer.
  • HTTP-Referer and X-Title are read. OpenRouter’s two attribution headers land as the referer and title request tags, filterable in Activity and groupable in Analytics like any other tag — so the labels you have been slicing spend by survive the move without touching a call site. An explicit metadata.referer or metadata.title in the body wins over the header. There is no public app leaderboard here for them to feed; they are yours.
  • Browser calls work, and the spec is machine-readable. Every /v1 endpoint sends CORS headers and answers preflights, so a client-side integration carries over. The OpenAPI 3.1 document at /api/openapi.json generates clients, Postman collections and mocks.

What we do not implement

Two fields other gateways use are accepted and stripped before the request leaves us: transforms and route. Nothing breaks if they arrive, and nothing happens either — if you were relying on prompt transforms to fit a long context, that work is not being done here.

There is also no stored, versioned routing policy object to migrate to. Routing is expressed per request, or saved as a preset you call by name.

Checklist

  • Map every model id against GET /models.
  • Confirm we hold a route for each one: GET /models/{vendor}/{model}/endpoints lists the providers, in the order they will be tried, with prices.
  • Move gateway-specific fields you were sending into metadata or delete them. HTTP-Referer and X-Title can stay as they are.
  • Set a spend cap on the new key before you point production at it.
  • Run a playground call against each model to confirm we have a working key for its provider on this deployment.
The exit is the same size as the entrance
Whatever makes this migration one line makes the reverse one line too. Nothing in your code becomes Multigrid-shaped except four optional fields, and there is no library to uninstall.

Something here disagrees with what the API actually did? That is a bug in this page, and worth reporting.

Report it