Two kinds of credential are in play, pointing in opposite directions. Mixing them up is how a provider key ends up in a browser bundle.
your application ──ml_vat_…──▶ ManyLayers ──sk-…──▶ OpenAI
                  gateway                  provider
                  credential               credential
Provider credentialGateway credential
Looks likesk-proj-…, sk-ant-…, an AWS key pairml_pat_…, ml_vat_…, ml-…
Issued byOpenAI, Anthropic, …ManyLayers
Held byManyLayersyour application or person
Used to callthe providerManyLayers
Configured inGateway → ProvidersGateway → Access and Gateway → API Keys
A provider credential is never sent to your applications, and a gateway credential is never sent to a provider. The gateway drops the caller’s Authorization header and sets the provider’s own in its place, so a ManyLayers token cannot reach OpenAI even by accident.

Gateway credentials

Every request to https://app.manylayers.io/v1 carries one of these as Authorization: Bearer <token>. Three kinds exist:
KindPrefixActs asCreated in
Personal access tokenml_pat_You, with your role, in the one workspace it was issued forGateway → Access → Personal Access Tokens
Virtual account tokenml_vat_An application. A virtual account reaches only the models and provider accounts you list, never its creator’s access.Gateway → Access → Virtual Accounts
API keyml-A team, or a person an administrator issued it to; optionally bound to a workspaceGateway → API Keys
Use a virtual account token for anything running in production, and a personal access token for scripts, notebooks and local development.
  • A token’s plaintext is shown once, when it is issued. Only a SHA-256 hash is stored, and nothing returns the token again. Rotate or revoke it from the same page; revoking takes effect immediately.
  • Every personal access token expires, and your organization’s credential policy sets the longest lifetime. A virtual account token’s expiry is optional unless the policy limits it, and it can rotate on a schedule with a grace period for the old token.
  • A virtual account is created with explicit allowed_models and allowed_providers. Leaving either empty gives access to none.
  • Tokens are issued from a signed-in console session, never from another token.

What a client receives

Only a token and the base URL:
MANYLAYERS_API_KEY=ml_vat_xxxxxxxx
MANYLAYERS_BASE_URL=https://app.manylayers.io/v1
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.MANYLAYERS_API_KEY,
  baseURL: process.env.MANYLAYERS_BASE_URL,
});

const response = await client.chat.completions.create({
  model: "gpt-4o-mini",
  messages: [{ role: "user", content: "Hello" }],
});
The model name is a gateway model name. Whether OpenAI, Anthropic, Gemini or another provider serves it is workspace configuration, and changing it does not change this code.
Keep the token server-side. It carries your workspace’s spend and model access; a token in a browser bundle is a token anyone can use. There is no browser-safe variant, so a public client needs its own backend to hold it.

Where a provider credential lives

Both options are set when you add or edit an account in Gateway → Providers (the credential field of the account’s setup, or Credentials & prices on an existing account). Some provider types take more than one credential method: AWS accounts take an access key pair, an assumed role or a Bedrock API key; Vertex takes an API key (express mode), a service account, an external account, or the gateway’s own Google identity. See Providers.

Resolution at request time

token → workspace → gateway model → provider account → credential → provider API
A credential becomes a usable secret in one place, the model resolver. Test Credentials and Test Connection use the same code and the same credential row, so a passing test means a working request. Resolutions are cached for 30 seconds and dropped immediately when a provider, credential or model changes, so a rotation takes effect at once.

Test Connection

POST https://app.manylayers.io/admin/gateway/providers/{id}/test (with Authorization: Bearer <personal access token>) makes one authenticated call to the provider and names the stage that failed:
StageMeaning
endpointthe base URL is missing or unusable
credentialno active credential, or the reference did not resolve
reachabilitythe endpoint could not be reached: a network problem, not a key problem
authenticationthe provider rejected the credential: it resolved, but the value is wrong or revoked
upstreamthe provider answered an error
unsupportedthis provider type cannot be probed from the console
okthe provider answered
The resolved secret never appears in the result. To check individual models, see Model selection.

Workspace isolation

A workspace is the isolation boundary. Providers, credentials and models belong to one. A personal access token and a virtual account token are bound to the workspace they were issued for; an API key is either bound to one workspace or team-scoped. A workspace-bound token resolves only that workspace’s providers and models. Another workspace’s models are not listed by GET /v1/models and are not callable with it, and its provider configuration is not readable. A provider in another workspace answers 404, not 403, so a guessed id confirms nothing. See Model resolution.