An API key authenticates your calls to the serving API (/v1/models, /v1/chat/completions,
the sandbox invoke endpoints). A key looks like aig_ followed by a 32-character token, and it
is bound to exactly one project — the binding is chosen at creation and never changes.
curl https://api.aigridapp.com/v1/chat/completions \
-H "Authorization: Bearer $AIGRID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"openai/gpt-oss-20b","messages":[{"role":"user","content":"hello"}]}'Runtime actors#
A runtime actor is the identity behind a call, separate from what pays for it. A human's API key is one kind of actor; a sandbox product also runs under its own actor, so its calls spend the project budget without needing a personal key. A project's Runtime actors tab lists each one with its class, environment, keys and last call; opening a record shows where it was created from and the wallet binding that decides who pays.
One key, one project#
The bound project is the execution and charging context of every call the key makes: usage,
policy checks and wallet charges all land on that project. There is no per-call project choice
for a key — the X-AIGrid-Project selector header may restate the bound project, and nothing
more. A selector that names a different project is refused with 403 project_mismatch before
any money is reserved.
The key holds no funds of its own. Every call charges the project wallet of its bound project.
Who may issue a key#
Creating a key requires, all at once: an active organization membership, an active grant on the
project's owning team, and an active project grant carrying project.invoke (Project Owner or
Project Consumer) — confirmed by the authorization adapter. Anything less answers
403 key_issuer_denied. The full rule, and how it differs from ordinary consumption, is on
Roles and permissions.
The rule is re-evaluated on every use of the key: revoke the creator's grants or suspend the person, organization or project, and the next call is refused.
Access policy: all-eligible or listed#
Each key carries one of two access policies, chosen at creation:
all-eligible(the default) reaches every resource currently eligible in the bound project — including products assigned later.listedreaches exactly the resources named inallowedModels. Every entry must be an eligible resource of the project at issue time (422 resource_not_eligibleotherwise), and eligibility is re-checked on every call — naming a resource your list excludes answers403 key_scope. An empty list never means "all".
Spending limits and expiry#
Optional controls set at creation, both enforced on admission:
| Setting | Values |
|---|---|
| Spending limit | an amount in micro-USD, resetting daily, weekly or monthly — or lifetime |
| Expiry | 1–365 days; 90 days when not specified |
Limits and expiry are part of the key and survive rotation. Wider organization spend controls apply on top.
Rotation#
Rotating a key issues a replacement secret with the same project, access policy, list,
spending limit and expiry span — and revokes the old key immediately. The registry records
the lineage (rotatedFrom / rotatedTo) and both the rotation and the revocation are audited.
Legacy unbound keys#
Keys created before project binding became mandatory have no project. They are refused
everywhere with 403 key_unbound — never auto-bound, never auto-revoked, and not rotatable.
The console marks them "Needs reissue": create a project-bound key and revoke the old one.
What happens when a key is refused#
A rejected call never reaches a provider and nothing is charged — no usage record, no
reservation, no dispatch. See Errors for the codes
(unauthenticated, project_mismatch, key_scope, key_unbound, key_issuer_denied) and
what clears each one.
Next#
- Authentication — headers, selectors and error codes
- Roles and permissions — the issuing rule in full
- Caps and spend controls — limits beyond the key