An allowlist in your AI gateway is necessary but not enough on its own. A model ID can be an alias, and a routing alias lets the provider choose the model. Allow only exact approved IDs, then check the model named in every response and keep that record.

The rule to take away: check the model twice, on the way in and on the way out. Treat a missing model as an error, never as a reason to fall back.

Compliance teams ask us a direct question. If you list the allowed models in your AI gateway, does serverless inference run the model ID you requested, or do you need another guardrail? It comes up when a customer contract or an internal policy names the models you may use.

Why a model allowlist alone can let the wrong model answer

A model allowlist can pass a request and still let an unapproved model answer it. A model ID is the exact string you put in the request's model field. An AI gateway is the proxy that sits between your applications and the inference API. An allowlist is the list of model IDs that gateway lets through.

The allowlist checks a name. The name and the model behind it are not always the same thing, for three reasons.

First, a provider can accept a second name for the same model, called an alias. Second, a routing alias is a name that lets the provider choose the model for each request. Third, your own gateway can rename models too. LiteLLM, an open-source gateway, controls model access per virtual key, a key the gateway issues to each app or team. Its model aliases can also upgrade or downgrade a request to a model the user did not expect.

So there are two places where the model can change: at the provider and in your own configuration. A check that only reads the request sees neither.

How model IDs, aliases and routing aliases resolve on our API

On our API, GET /models is the authoritative list of IDs the serverless API accepts, and it reflects what is served right now. It is documented in our serverless inference docs. The list holds more than plain model IDs.

It also contains the -instant variants. They are the same model at the same price, but they answer directly, without first writing out their reasoning. And it contains our routing aliases lyceum/simple, lyceum/complex and lyceum/reasoning, next to lyceum/router.

A routing alias is not a model. We choose the model for each request and bill it as that model. The Lyceum Router page says it sends each request to MiniMax M3 today. "Today" matters here: the target can change without the name changing.

When an ID cannot be served, our API does not substitute another model. An ID can stay on that list while no copy of the model is running at the moment. It then answers 404 model not found, the same as an unknown ID. So retry with backoff before you treat a 404 as permanent.

That makes GET /models the right tool for one job: checking that an approved ID is still served. It is the wrong source for the approval list itself, because it also holds aliases and variants your team may never have approved.

Four requests against a two-model allowlist

To see the gaps, take a hypothetical policy. A compliance team approves two model IDs: z-ai/glm-5.3-flash and deepseek/deepseek-v4.1-flash. Four requests reach the gateway.

We sent the same four requests to our serverless API on 30 October 2026 and read the model field of each response. That field is where the API states which model answered.

Request (model ID sent)Exact-match allowlistOur API's answer on 30 October 2026Outcome
z-ai/glm-5.3-flashAllowed200, response model z-ai/glm-5.3-flashPasses both checks
Z-AI/GLM-5.3-FLASHRejected200, response model z-ai/glm-5.3-flashSame model, unapproved spelling
lyceum/routerRejected200, response model minimax/minimax-m3A model the team never approved
qwen/qwen3-32bRejected404, model not foundRetired ID, no substitute

One request passes and the request check stops the other three. That looks like success, but the allowlist only judged names. Only the response check proves that the first request really ran on the approved model.

Row two shows why spelling matters. We also sent the ID without the slash, z-ai-glm-5.3-flash. The response named z-ai/glm-5.3-flash. If the gateway accepted loose spellings, approvals and logs would carry several names for one model. Reject anything that is not the exact approved ID.

Row three shows the bigger gap. A broader rule, such as "allow anything from our provider", would have let lyceum/router through, and MiniMax M3 would have answered.

The example has limits. It is four requests on one day, against our API only. It says nothing about how another provider resolves names, and it does not cover a dedicated endpoint, where GPUs are reserved for one customer.

Check the model in the response, not only in the request

The response check is what turns an allowlist into proof. Our API is OpenAI-compatible, so the base URL is https://api.lyceum.technology/openai/v1 and every response carries a model field.

A request goes from your app to your gateway, then to our API, which resolves the name to a model, then to the model. The gateway, the one highlighted box, makes two checks: before the request leaves, the model ID must be an exact match on your approved list; when the answer returns, the model named in the response must equal the requested ID. A note says a routing alias such as lyceum/router was answered by minimax/minimax-m3 in our test on 30 October 2026, so only the response shows which model answered.
Your gateway checks the model twice: the exact approved ID before the request leaves, and the model named in our response when it returns (simplified; our test calls of 30 October 2026).

We are a sovereign European AI inference provider for open-source models. Our serverless models run in Paris and Finland. The two checks below work the same on any OpenAI-compatible endpoint.

APPROVED = {"z-ai/glm-5.3-flash", "deepseek/deepseek-v4.1-flash"}

def check_request(model_id: str) -> None:
    # exact match only: no lower-casing, no alias resolution
    if model_id not in APPROVED:
        raise PermissionError(f"model not approved: {model_id}")

def check_response(requested: str, response: dict) -> None:
    returned = response.get("model")
    if returned != requested:
        raise RuntimeError(f"requested {requested}, answered by {returned}")

Log both values for every request: the ID you sent and the ID that came back. A gateway log can record the provider, status and token usage, and it can also include the prompt. Decide on purpose whether prompts belong in that log. For a model audit, the two IDs, the time and the token counts are enough.

Our own API gateway, the entry point of our service, sees the model you asked for. The only lasting record of a request is a usage record, as our legal page describes. It holds your organisation and user, the model, token counts and the time. In our dashboard, Inference → Serverless → Usage shows your tokens per model and API key, and you can download it as a CSV file. Compare it with your own gateway log, model by model.

When an approved model is retired, fail instead of falling back

A retired model is the case where a quiet fallback is most tempting. OWASP, a non-profit security project, lists outdated or deprecated models as a supply-chain risk for LLM applications. A supply-chain risk is a weakness in the parts your application is built from. So moving off a retired model is right, but a gateway should not pick the replacement on its own.

Our retirement process has three steps. We email you with the model we recommend, and nothing changes yet. Then we reduce capacity, so the model may be slower. Then the model is retired.

After that, requests to the old ID answer 404 model not found. Our list of retired models names a replacement for each one. For DeepSeek V4 Pro, which retires on 19 November 2026, our 29 October email said we won't switch your model automatically.

So write one rule into the gateway: a 404 on an approved ID that persists after retries opens a change request to approve the replacement. The gateway returns the error and never picks a model itself. Contract terms such as notice periods are a separate question, covered in our article on model deprecation risk.

Four steps from left to right: the response is 404 model not found; the gateway retries with backoff, because a listed ID can be briefly unserved; the 404 persists; the gateway opens a change request so the team approves the replacement named on our retired models list. That last step is highlighted. Below the retry step, a grey dashed box marks the route not taken: an automatic fallback to another model, labelled never.
When an approved model ID keeps answering 404 after retries, the gateway opens a change request for the replacement on our retired-models list instead of switching models itself.

Questions to settle before you sign off the gateway

Before the gateway goes live, settle these points with your platform team:

  1. Is the approval list written as exact model IDs, kept outside GET /models?
  2. Does the gateway reject routing aliases unless they are on that list?
  3. Does every response pass the check that its model field equals the requested ID?
  4. Are the requested and returned IDs logged for every request, and can you reconcile them with the provider's usage records?
  5. Does a lasting 404 on an approved ID fail the request and open a change request, with no automatic fallback?
  6. Has anyone checked your own gateway's alias settings for models that rename requests?

If all six hold, an auditor can trace which model answered each request. Where requests are processed is a separate question, covered in region pinning for LLM APIs.

To see the behaviour yourself, create an API key in our dashboard and send the four requests from the table to the OpenAI-compatible endpoint.

Frequently asked questions

Does the response always name the model that answered?

On our API, yes: every response we tested carried the model in its model field, including requests sent to a routing alias. That is why the response check works.

Can I use the GET /models list as my allowlist?

Use it to confirm an approved ID is still served. Keep the approval list separate, because the endpoint also returns routing aliases and instant variants your team may not have approved.

Are the instant variants a different model?

No. An instant ID runs the same model at the same price, with the reasoning phase switched off. Approve it explicitly if your applications need it.

Sources

  1. docs.litellm.ai
  2. developers.cloudflare.com
  3. genai.owasp.org