Authentication and Authorization
One header for every caller. Who you are is checked at the gateway. What you may do is decided by the service that owns the resource.
One header, two kinds of tokens
Every v4 request authenticates with Authorization: Bearer <token>. The spec
declares a single bearerAuth scheme and applies it to every operation.
For CI, Terraform, scripts and backend services.
Code
For the portal, the CLI after zuplo login, and an agent you signed in to (the AI
Gateway setup wizard).
Code
Both reach the same operations with the same permissions model.
Code
Code
Code
Prerequisite
API-key callers on v4 need OpenFGA to know about API-key principals (consumers),
not only users. This is the long-lead item from the scoping document. Until it
lands, api.zuplo.com accepts the OAuth access token of a Zuplo sign-in only, as
the portal sends it.
Authorization happens in the service
Zuplo auth policies check the credential.
The service that owns the resource decides, through OpenFGA. 404 when the
caller has no access to the account at all (ERR-5).
The gateway does not make OpenFGA checks for v4 routes. With
re-rooted URLs, a path like
/v4/buckets/{bucketId}/ai/apps/{appId} names the bucket, but not the pool the
app is in, and only the service knows whether the app really is in that bucket.
So only the service can decide in one check, and it answers 404 for a resource
that is not in the container in the path. This follows the 2026-10-06 decision to
stop doing OpenFGA at the edge.
Personal resources without /me
Operations that are about the caller ("my personal keys", "my usage") use the
same URLs as everyone else's, with the caller's own userId.
Code
Code
With your own userId, returns your usage and your consumers. User app admins can
pass anyone's userId.
Code
Code
There are no /users/me/... routes. The same URL works for a person, an admin and
an API key.
Rate limits and correlation
Code
Code
A throttled request gets 429 with Retry-After. The limits are stated once, in
the spec's info.description.
Every response has a request-id header, and every error repeats it in
trace.requestId (ROB-5).
Bearer-only security schemes, declared on every operation. No Authorization, X-* or zuplo-user parameters, and no cookies.
Where authorization happens.