Skip to content

API Authentication

All authenticated API requests use a Bearer token:

curl https://api.zevcloud.net/v1/projects \
-H "Authorization: Bearer sk_live_your_api_key"

API keys are team-scoped — every key belongs to exactly one team, and that team context is set automatically by the server from the key. You do not need to (and cannot) override which team an API key acts on by passing a different x-team-id header. The server reads the key’s team from the database row, not from the request header.

Every API key is created with one of three scopes. They form a strict hierarchy: readdeployfull.

ScopeWhat it can do
readList and get resources. Read deployments, env-var keys (values are masked for secrets), logs, DNS, billing.
deployEverything read can do, plus: trigger deployments, restart/stop/start services, set or delete env vars, verify a custom domain, approve or reject pending deployments.
fullEverything deploy can do, plus: create or delete projects/services/environments, manage custom domains, run terminal commands, rotate SFTP passwords, toggle public DB access, manage DNS records on purchased domains.

deploy is the recommended scope for AI agents and CI pipelines. It lets the agent ship updates to existing services without giving it permission to delete projects, change billing, or manage the team.

The full machine-readable permissions table is also surfaced in the dashboard when you create a key.

Things no API key can do — even with full

Section titled “Things no API key can do — even with full”

The API exposes a category of routes that always require interactive human authentication. A compromised API key cannot reach any of them. They include:

  • Creating, listing, or revoking API keys
  • Deleting the team or transferring ownership
  • Inviting, removing, or changing a member’s role
  • Setting the team’s billing currency
  • Upgrading a service plan or applying credit to an invoice
  • Funding Zev Credit (top-up)
  • Buying, transferring, renewing, or cancelling a domain
  • Toggling WHOIS privacy or email forwarding addons on a domain
  • Opening the database GUI (Adminer)
  • Connecting or disconnecting GitHub
  • Accepting or declining team invites
  • Saving billing addresses
  • Managing notification preferences
  • Accepting the Fair Use Policy

API requests to any of these return 403 Forbidden with the message This action requires interactive authentication and cannot be performed with an API key.

Lifecycle: keys are bound to their creator

Section titled “Lifecycle: keys are bound to their creator”

Every API key is tied to the team member who created it. The key’s effective role is the creator’s current role on the team — looked up on every request, not snapshotted at creation.

What this means in practice:

  • If the creator was an admin when they made the key, then later got demoted to member, the key stops being able to call any route that requires admin. The key’s scope field is unchanged; the team-membership lookup just no longer satisfies the route’s role check.
  • If the creator is removed from the team (or leaves), the key is auto-revoked with reason creator-removed. The key is rejected on its next request and shows as revoked in the dashboard. There is no race window.
  • If the team owner transfers ownership, their existing keys keep working at whatever role they now have on the team (typically admin or member after the transfer). Owner-only operations done via verifyOwnership will start failing for those keys.

The practical implication for automation: rotate keys when the operator leaves. Don’t rely on a long-lived key created by a former teammate; create a fresh key under a current member.

API key management endpoints are themselves human-only (a compromised key cannot use them to create more keys or revoke other keys to lock the team out). Call these with a ZevID identity token, not an API key.

POST
/v1/api-keys

Owner-or-admin only. Human auth required.

Headers:

Authorization: Bearer <ZevID token>
x-team-id: <team-id>

Body:

{
"name": "CI/CD Pipeline",
"scope": "deploy"
}

Response:

{
"id": "uuid",
"name": "CI/CD Pipeline",
"key": "sk_live_a1b2c3d4...",
"keyPrefix": "sk_live_a1b2c3...",
"scope": "deploy",
"createdAt": "2026-03-28T00:00:00Z"
}
GET
/v1/api-keys

Owner-or-admin only. Human auth required.

Returns all active (non-revoked) keys for the team. The raw key is never returned — only the prefix, plus name, scope, creation timestamp, and last-used timestamp.

DELETE
/v1/api-keys/:keyId

Owner-or-admin only. Human auth required.

Revoked keys are immediately invalid for all subsequent requests. The row stays in the database with revokedAt set, so the audit trail and the dashboard can show when and why the key was revoked.

The API returns these auth-related status codes:

StatusMeaning
401 UnauthorizedMissing, malformed, expired, or revoked token. Also returned when the API key’s creator has been removed from the team (the key is auto-revoked at this point).
403 Forbidden — scopeThe key’s scope is below what the route requires. The error body names the required scope (e.g. requires 'deploy', key has 'read').
403 Forbidden — human-onlyThe route requires interactive human authentication and cannot be performed with an API key.

When a 403 is returned because of scope, the fix is to issue a new key with a higher scope or use a key that already has the right scope. Existing keys cannot have their scope changed — create a new one and revoke the old one.