Authentication
All gateway endpoints require authentication. Gatewyse supports two authentication methods: API keys (for programmatic access) and JWT tokens (for dashboard and SSO-based sessions).
API Key Authentication
API keys are the primary authentication method for gateway requests. Keys are prefixed with aigw_sk_ and passed via the Authorization header as a Bearer token.
curl https://your-gateway.example.com/v1/chat/completions \ -H "Authorization: Bearer aigw_sk_your_api_key_here" \ -H "Content-Type: application/json" \ -d '{"model": "gpt-4", "messages": [{"role": "user", "content": "Hello"}]}'Which header to send
Authorization: Bearer <key> is the documented form and the one to prefer.
x-api-key: <key> is also accepted, because Anthropic’s own SDKs send it and
the Anthropic-compatible endpoint is meant to be a drop-in for them. It carries
API keys only — a session token in this header is ignored, since sessions
belong on Authorization or the admin cookie. When both headers are present,
Authorization wins.
Creating API Keys
API keys are created through the admin dashboard under API Keys. When creating a key you can configure:
| Field | Description |
|---|---|
name | A human-readable name for the key (1-200 characters). |
scopes.capabilities | Array of allowed capabilities (see table below). Defaults to ["chat"]. |
scopes.providerIds | Stored but not enforced. See the note below. |
scopes.modelIds | Stored but not enforced. See the note below. |
rateLimits.requestsPerMinute | Per-key requests-per-minute limit. |
rateLimits.requestsPerDay | Per-key requests-per-day limit. |
rateLimits.tokensPerDay | Per-key tokens-per-day limit. |
expiresAt | ISO 8601 datetime after which the key is automatically expired. |
allowedIps | Addresses this key may be used from. See below. Empty means no restriction. |
metadata | Arbitrary key-value metadata for your own tracking. |
Restricting a key by address
allowedIps accepts single addresses and CIDR ranges, IPv4 and IPv6:
{ "allowedIps": ["203.0.113.4", "10.0.0.0/8", "2001:db8::/32"] }An empty list means the key is unrestricted — the allow-list is opt-in, and its absence is not a denial.
Three behaviours worth knowing before you rely on it:
- A malformed entry matches nothing.
10.0.0/8or203.0.113.256will not admit anyone, and will not be skipped either. Dropping an unparseable entry would silently widen the list, so it stays and refuses. - Address families do not cross. An IPv4 client never matches an IPv6 range.
An IPv4-mapped address such as
::ffff:203.0.113.4is treated as the IPv4 host it represents, so203.0.113.4matches it. - The address checked is the one the gateway sees. Behind a proxy or load balancer that is the proxy’s address unless the deployment is configured to trust forwarded headers. Verify against a real request before depending on it.
Capabilities
Each API key is scoped to one or more capabilities that control which endpoints it can access.
| Capability | Endpoints |
|---|---|
chat | /v1/chat/completions, /v1/messages |
completions | /v1/completions |
embeddings | /v1/embeddings |
audio | /v1/audio/transcriptions, /v1/audio/translations |
tts | /v1/audio/speech |
images | /v1/images/generations |
rerank | /v1/rerank |
video-generation | /v1/video/generations |
files | /v1/files (upload, list, fetch, delete) |
batch | /v1/batches (submit, get, cancel, list) |
vector-stores | /v1/vector_stores (CRUD, add/remove files, search) |
responses | /v1/responses (stateful Responses sessions) |
realtime | /v1/realtime/sessions + the /v1/realtime/connect WebSocket |
usage:read | /v1/usage |
budget:read | /v1/budget |
Key Lifecycle
API keys have the following statuses:
| Status | Description |
|---|---|
active | The key is valid and can be used for requests. |
expired | The key has passed its expiresAt date. Automatically detected on use. |
revoked | The key has been manually revoked via the admin dashboard or API. |
JWT Authentication
JWT tokens are used by the admin dashboard and SSO-authenticated sessions. Tokens are signed with HS256 and must include:
sub— the user IDtenantId— the deployment the user belongs totype— must be"access"jti— a unique token identifier (required; used for revocation)
JWTs are passed as Bearer tokens in the Authorization header, or via an HttpOnly cookie named access_token.
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...Tokens without a jti claim are rejected. Revoked tokens (checked against a Redis blocklist) are also rejected.
Rate Limiting
Two layers of rate limiting are applied to every request:
- API Key rate limits — per-key limits configured when the key is created (
requestsPerMinute,requestsPerDay,tokensPerDay). - Deployment rate limits — global limits configured for the deployment, narrowed by organization and department policy.
Rate limiting uses a sliding-window algorithm backed by Redis sorted sets with server-side timestamps (preventing clock-skew bypass).
When a rate limit is exceeded, the gateway returns:
{ "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded", "details": { "level": "apiKey", "retryAfter": 12, "limit": 60, "windowMs": 60000 } }}The response sets:
- HTTP Status:
429 Too Many Requests Retry-Afterheader (seconds until the rate-limit window resets)X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Resetheaders
The details.level field is one of ip, apiKey, or tenant depending on which limiter rejected the request. tenant is the stored wire value for the deployment-wide limiter. Daily limits also use this envelope with windowMs: 86400000.
Error Responses
| HTTP Status | Code | Description |
|---|---|---|
401 | AUTH_REQUIRED | No authentication credentials provided. |
401 | AUTH_INVALID_TOKEN | JWT token is invalid or malformed. |
401 | AUTH_TOKEN_EXPIRED | JWT token has expired. |
401 | AUTH_INVALID_API_KEY | API key not found or invalid. |
401 | AUTH_API_KEY_EXPIRED | API key has passed its expiration date. |
401 | AUTH_API_KEY_REVOKED | API key has been revoked. |
403 | AUTH_FORBIDDEN | Valid credentials but insufficient permissions for the requested capability. |