A key acts as you: same quota, same credit, same suspensions. It is not a back door, it is you. Which is also why it reaches neither your invoices, nor your payment methods, nor your tickets.
The token
It travels as a bearer token, on every call:
Authorization: Bearer ffxf_live_a1b2c3d4…
We store only its SHA-256 fingerprint. The console then shows only its head, enough to recognise the key in a list without it being usable. A lost key is not recovered; it is revoked.
Ten keys per account. Enough to separate concerns (monitoring, provisioning, CI) and few enough that the inventory stays readable.
Scopes
One scope per consequence, not per resource: reading costs nothing, acting interrupts a service, creating spends credit, destroying erases a disk.
| Scope | What it opens |
|---|---|
catalog.read | regions, plans and images (already public) |
vms.read | list machines and read their state |
vms.create | order, spends credit |
vms.action | start, stop, reboot, reinstall, rename |
vms.destroy | destroy a machine and its disk |
vms.console | open a console session |
network.write | IPv6 addresses, reverse DNS, private networks, firewalls and load balancers |
backups.write | restore and delete backups |
billing.read | balance, usage, quota |
billing.write | set the monthly budget |
sshkeys.write | manage the account SSH keys |
A call outside a key's scopes answers 403 insufficient_scope, and
says which one was missing.
Restricting to addresses
A key can accept calls only from listed addresses. That is the setting that makes a stolen token useless elsewhere, and it costs one line when creating the key. With no list, the key works from anywhere.
Expiry and revocation
An optional expiry date is set at creation: after that day the key stops working, and nothing else changes. Revocation deletes the key rather than disabling it: a key you no longer want should not stay one click from working again.
Anything using it stops immediately. Plan the rotation: create the new key, deploy it, then revoke the old one.
No stored key: OIDC
In GitHub Actions, GitLab CI or any other OIDC issuer, a token issued to the job for a few minutes replaces the key. You declare the issuer and a binding (conditions on the repository, branch or workflow, and scopes); the job sends its token like a key. See OIDC: CI without an API key.
Authentication refusals
| Code | What happened |
|---|---|
missing_token | no Authorization header |
invalid_api_key | unknown token |
key_disabled | key switched off |
key_expired | past its expiry date |
ip_not_allowed | call from an address outside the list |
insufficient_scope | the key lacks the required scope |
account_suspended | account closed: contact support |
invalid_oidc_token | OIDC token malformed, badly signed, expired or for an unknown audience |
oidc_claim_mismatch | valid OIDC token that no enabled binding accepts |
binding_expired | the matching OIDC binding has expired |
oidc_provider_unavailable | OIDC issuer unreachable, try again later |