Project API Keys
A project API key belongs to a project, not to a person. Your server code and your pipelines hold one instead of your personal account token, so a leaked key exposes one project’s application surface — not everything you can reach.
What they are
- Project-owned. A key is created inside one project and never moves. It reaches that project’s resources and nothing else.
- Prefixed
gsk_. A key is 60 characters:gsk_, a random body, and a checksum that lets the API reject a truncated or mistyped key before it looks anything up. Lists show only the first 12 characters. - One per server or pipeline. Name a key after where it lives —
production server,github actions— so revoking it never takes down more than the one thing you meant. - Shown once. The key is displayed when you create it and never again. If you lose it, roll it.
Create one
Open Project → Settings → API keys in the console :
| Field | What it does |
|---|---|
| Name | How you recognize the key later, e.g. production server. Up to 100 characters. |
| Purpose | Application — server code: user administration for the project’s auth apps, plus storage and databases. CI/CD — deploys only, for GitHub Actions and other pipelines. |
| Advanced → Restrict to specific auth apps | Application keys only. Tick the apps this key may administer. Leave it empty and the key reaches every auth app in the project. |
| Advanced → Expires in (days) | Optional. Empty means the key never expires. |
Press Create key and copy the value straight away — it is not shown again.
Anyone with the read role on the project can see the list. Creating, rolling
and revoking need admin or owner. A project API key can never manage keys,
including itself: those routes answer 403 for a key.
Auth apps belong to the project, not to one site. An Application key can administer the users of every auth app in the project unless you restrict it under Advanced — worth doing when the project runs more than one app.
What a key can reach
Deny by default. A key reaches an explicit allowlist of routes and nothing else. The purpose you pick maps to capabilities, and each capability opens one part of that list.
Application — auth, storage, databases
| Capability | The key may |
|---|---|
auth | List the project’s auth apps; read an app and its statistics; list, read, disable, enable and delete its end users; reset a user’s 2FA; set a user’s app_metadata; mint a password-reset link |
storage | List buckets; read a bucket and its S3 credentials; list, upload, download, inspect and delete objects, including batch and prefix deletes; mint presigned upload and download URLs |
databases | List databases; read one; read its credentials; read its metrics |
CI/CD — deploy
| Capability | The key may |
|---|---|
deploy | Upload a deploy; list a project’s deployments; read a deployment and its logs; roll a deployment back; read the project, its logs, its sites and one site |
What it cannot
Everything that creates, destroys or reconfigures infrastructure stays with a
human, in the console or the ghayma CLI:
- Create, update or delete an auth app, rotate its signing keys, or touch its server key.
- Create, roll or revoke API keys.
- Create, delete, expose or unexpose a bucket, rotate its credentials, or edit its CORS rules.
- Create, delete, stop, start, resize, retier, rotate, expose or unexpose a database, change which sites reach it, or take and restore backups.
- Reach a database through the API’s data plane — collections, documents, queries, tables. An app talks to its database over the wire with the credentials, not through this API.
- Billing, account, teams, account tokens and domains. A key is never granted those scopes.
Two refusals are worth recognizing: another project’s resource answers
404, and a route outside the allowlist answers 403. An allowlisted
route the key has no capability for also answers 403, with insufficient token scope.
Managed keys
You do not create every key. The platform mints one for each site connected to
a storage bucket, a database, or an auth app at level admin, and injects it
into that site’s container as GHAYMA_API_KEY on every deploy — the hand-copy
step disappears.
- Named
site:<site>. One per site, listed alongside the keys you created and marked managed. - Capabilities follow the connections.
storagefor a bucket,databasesfor a database, andauthfor an auth app connected atadmin— narrowed to those apps, so the project’s other auth apps are never swept in. Connect or disconnect a service and the key is re-minted to match; disconnect the site’s last one and the key is revoked with it. A change reaches the running pods without a redeploy. - Not yours to roll. Roll and revoke answer
409for a managed key: rolling one by hand would leave the running app holding a dead credential, and the next deploy would mint a replacement beside it anyway. Disconnect the site instead. - Not stored as an env var. Like the database and storage variables, it is
derived at deploy time, so it never appears in
ghayma env listand there is nothing to edit.
A managed key carries auth only for the auth apps its site is connected to at
level admin; at client — where a new connection starts — it carries none, and
administering that app’s end users needs a key you create yourself. So does any
server that runs outside Ghayma.
Use it
On your server, hand the key to the SDK:
import { Ghayma } from "@ghayma/sdk";
const ghayma = new Ghayma({ apiKey: process.env.GHAYMA_API_KEY });Zero-argument new Ghayma() reads GHAYMA_API_KEY from the environment, so a
site that already has the variable needs no argument at all — and a site
connected to a bucket or a database already has it.
When you still set GHAYMA_API_KEY yourself. It is injected automatically
only for a site connected to a bucket, a database, or an auth app at admin.
For a server hosted outside Ghayma, create an Application key and set the
variable by hand — in the console, or with
ghayma env set GHAYMA_API_KEY=gsk_....
CLI support for project keys is not shipped yet. A CI/CD key
authenticates the deploy API routes today, but ghayma deploy with a project
key comes in a later CLI release. Keep your pipeline on an account token
until then.
Roll and revoke
Roll issues a fresh key with the same name (suffixed (rolled)), the same
capabilities, the same auth-app restriction and the same remaining lifetime.
The old key keeps working until you revoke it, which is the point: deploy the
new value, confirm it works, then revoke the old one. No downtime, no window
where nothing authenticates.
Revoke takes a key out of service. The row is kept for audit and shows as
revoked in the list. Keys are validated against a 30-second cache, so a
revoke takes effect everywhere within 30 seconds.
Both refuse a managed key with a 409 — it is retired by
disconnecting the site, not by hand.
Account tokens vs project keys
Account token (gh_…) | Project API key (gsk_…) | |
|---|---|---|
| Belongs to | You | One project |
| Reaches | Every project you can reach, plus your account routes | That project’s allowlisted application routes |
| Created in | Settings → API Tokens | Project → Settings → API keys |
| Use it for | The ghayma CLI on your own machine | Your app’s server code, and CI |
An account token still authenticates @ghayma/sdk and logs one warning per
process telling you to switch. Keep account tokens for the CLI on your own
machine, where acting as you is exactly what you want.
Restricted keys
An Application key can be narrowed to a subset of the project’s auth apps. The
console lists them with checkboxes; over the API, auth_app_ids takes the
UUIDs of auth apps in the same project — an app’s slug is rejected with a
400, and so is an app from another project. Your code may still address an
app by slug or UUID, because the restriction is checked against the app the
request resolves to. An app outside the restriction answers 404.
Keys created before storage and databases were enabled keep their capabilities. The Application preset is expanded once, when the key is created, and rolling a key copies the capabilities it already has. Create a new Application key to pick up capabilities added since.