Authentication
How a Public API consumer identifies itself, where the key comes from, and what a rejected request tells you.
Salyro has two ways in, and they have nothing to do with each other. People sign in to the dashboard to configure a gateway. Code authenticates to the Public API with a Salyro API key. This page is about the second one.
Keeping them apart matters because they fail differently, they are held in different places, and one is never a fallback for the other. A dashboard session cannot call the Public API, and an API key cannot open the dashboard.
Two separate systems
| Dashboard | Public API | |
|---|---|---|
| Who uses it | A person, in a browser | Your server-side code |
| Where | app.salyro.com | api.salyro.com |
| Credential | Your email and password | A Salyro API key |
| Scope | Your whole account | One gateway |
| How it is presented | A session, in the browser | Authorization: Bearer on each request |
The two are separated at the front door, by which host and which route a request arrives on, and neither path is tried if the other one fails. A request to the Public API with anything other than a valid Salyro API key is rejected — it is never re-checked as if it might be a person signing in.
The key comes from a gateway
A Salyro API key is issued by a gateway, in the dashboard, under that gateway's API Keys. It authenticates as that gateway and reaches that gateway's provider credentials — nothing else.
This is why there is no gateway id in the URL, no X-Gateway header and no
project parameter anywhere in the API. The key already says which gateway it is;
adding a second way to say the same thing would create a way for the two to
disagree.
The full key is shown once, at the moment it is issued. Salyro keeps a one-way hash of it and cannot show it again — see API keys for the lifecycle around that, including how to replace one without downtime.
Sending the key
A standard bearer token on every request:
POST /v1/chat/completions HTTP/1.1
Host: api.salyro.com
Authorization: Bearer sk-sly-...
Content-Type: application/json
Every Salyro key starts with sk-sly-. The marker is there so that a key found
in a log, a screenshot or a public repository is identifiable as Salyro's at a
glance, which is what makes a leak reportable at all.
In practice you set it once and never write it in a request again:
export SALYRO_API_KEY="sk-sly-..."
curl https://api.salyro.com/v1/models \
-H "Authorization: Bearer $SALYRO_API_KEY"
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.SALYRO_API_KEY,
baseURL: 'https://api.salyro.com/v1',
});
const models = await client.models.list();
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["SALYRO_API_KEY"],
base_url="https://api.salyro.com/v1",
)
models = client.models.list()
Where to keep it
A Salyro key carries the authority to spend against your provider accounts. Treat it as you would the provider key it stands in for:
- In an environment variable or a secret store on the server. Not in source control, and not in a configuration file that is committed.
- Never in browser code, a mobile app, or anything else shipped to a user. A key in a client is a published key, and no amount of obfuscation changes that.
- One key per environment, so that revoking or rotating one does not disturb the others.
When a key is rejected
An unaccepted key produces 401 with an OpenAI-shaped body:
{
"error": {
"message": "Incorrect API key provided.",
"type": "authentication_error",
"code": "invalid_api_key",
"param": null
}
}
There are only a few ways to arrive here, and all of them are the same kind of problem:
The header was not sent, or not in this form
The value must be
Bearerfollowed by the key. A bare key with noBearerprefix, or the key in a different header, is a missing key.The key is mistyped or truncated
Usually a copy that lost characters, or a shell that ate part of it. Check that the value starts with
sk-sly-and that nothing wrapped it in quotes that ended up inside the string.The key has been revoked
Revocation is immediate and final. A key that stopped working after being revoked cannot be restored — issue a new one.
It is the right key for a different gateway
Keys do not span gateways. A staging key against a production gateway is not a permissions problem to be granted; it is the wrong key.
A provider failure is not an authentication failure
The two credentials in play are easy to confuse when something breaks, so it is worth stating which is which.
Your Salyro key authenticates you to Salyro. Your provider credentials are what the gateway uses to call OpenAI, Anthropic, Gemini or Grok on your behalf. They fail separately and they mean different things:
- A rejected Salyro key is a
401from Salyro. Nothing was sent to a provider, and nothing was spent. - A provider that rejects the gateway's credential — expired, revoked at the
provider, out of quota — is a provider failure. It is reported as one, with
the provider's condition normalised into the error body, and it is not a
401.
The distinction is what tells you where to go: the first is fixed in your own configuration, the second in the gateway's provider credentials.
Rotating a key
Because revocation takes effect at once, the order matters when the key is in use: issue the replacement, deploy it, confirm requests are succeeding, and only then revoke the old one. A gateway can hold more than one active key, which is what makes that overlap possible.
The exception is a key you believe is exposed — there the interruption is the cheaper cost. Respond to a leaked API key is that case as a procedure.
