---
title: "Create an API key"
method: POST
path: "/v1/api-keys"
tags: ["Account"]
---

# Create an API key

`POST /v1/api-keys`

## Request

- Base URL: `https://api.spacefast.com`
- URL: `https://api.spacefast.com/v1/api-keys`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.spacefast.com/v1/auth/oauth2/authorize`, token URL `https://api.spacefast.com/v1/auth/oauth2/token`; client credentials, token URL `https://api.spacefast.com/v1/auth/oauth2/token`, scopes: `teams:create`)

Creates an API key with a finite scope and permission set. The policy cannot exceed the caller's current access. A device-login credential without standing key-mint access must send Idempotency-Key. The server then returns a browser approval step. After approval, send the same request body and Idempotency-Key with X-Spacefast-Continuation-Token. The response returns the full secret one time.

## Headers

- `Idempotency-Key` string
- `X-Spacefast-Continuation-Token` string

## Request body

- object
  - `name` string: Human-readable key name.
  - `scope` union, required: Explicit, finite policy target. A space scope narrows the derived policy to exactly that space, never the owning team's whole tree.
    - object
      - `kind` 'team', required
      - `teamId` string, required
    - object
      - `kind` 'space', required
      - `spaceId` string, required
  - `permissions` string[], required: Finite permission set the derived policy grants, and nothing beyond it.
  - `metadata` object: Caller-supplied metadata.
  - `ipAllowlist` string[], nullable: CIDR/IP allowlist for the new key.
  - `expiresAt` union: Expiry instant.
    - string, date-time
    - string
  - `notBefore` union: Activation instant.
    - string, date-time
    - string

## Response `201`

Response for status 201

- object
  - `$schema` string, uri: Canonical JSON Schema URL for this response body. The server also sends it in the `Link: rel="describedby"` response header.
  - `data` object, required
    - `id` string, required: API key id.
    - `name` string, nullable, required: Human-readable key name.
    - `tenantId` string, nullable, required: Tenant the key belongs to.
    - `mode` 'live' | 'test', required: `test` when the key belongs to a linked test tenant, `live` otherwise.
    - `subject` object, nullable, required: Subject the key acts as.
      - `type` 'user' | 'team' | 'external' | 'system', required: Subject kind the key acts as in audit records.
      - `id` string, required: Stable subject id.
    - `ownerPrincipal` object, nullable, required: Owning principal (ownership metadata, never the authorization gate).
      - `type` 'team' | 'external', required: Principal type that owns this key.
      - `id` string, required: Stable principal id (Better Auth team id, or external id).
    - `createdByUserId` string, nullable, required: User who created the key, or null for automation. Tenant-key creators are recorded for audit and do not own the key's authority.
    - `collection` 'team' | 'tenant', required: Disjoint resource collection this key addresses.
    - `preset` 'ci_deploy' | 'space_publisher' | 'space_admin' | 'domain_manager' | 'team_admin' | 'billing_viewer' | 'partner_admin', nullable, required: Preset compiled into a concrete access policy when the key is created.
    - `teamScope` union, required: Membership-following team scope for a device-login key, or null for legacy keys.
      - object
        - `mode` 'all', required
      - object
        - `mode` 'teams', required
        - `teamIds` string[], required
      - object
        - `mode` 'none', required
    - `policy` object, nullable, required: IAM-lite access policy evaluated for every credentialed request.
      - `version` '2026-06-09', required: Must be the current platform policy version.
      - `statements` object[], required: Evaluation order: explicit deny wins, explicit allow permits, otherwise implicit deny.
        - `effect` 'allow' | 'deny', required: Explicit deny always wins over allow.
        - `actions` string[], required: Actions this statement covers.
        - `resources` string[], required: Resource URIs this statement covers (wildcards cover subtrees).
        - `conditions` object: AND condition attributes. Unresolvable attributes fail closed: no match for allow, and match for deny.
          - `request.client` 'dashboard' | 'cli' | 'api' | 'worker': Restricts the statement to one resolved request client.
    - `metadata` object, required: Caller-supplied metadata.
    - `secretPreview` string, required: Masked secret preview (prefix and last characters).
    - `notBefore` string, date-time, nullable, required: Key is rejected before this instant.
    - `expiresAt` string, date-time, nullable, required: Key is rejected from this instant.
    - `ipAllowlist` string[], nullable, required: CIDR/IP allowlist. Requests from other addresses are rejected.
    - `revokedAt` string, date-time, nullable, required: Revocation instant.
    - `enabled` boolean, required: Whether the key currently authenticates.
    - `createdAt` string, date-time, required: Creation instant.
    - `updatedAt` string, date-time, required: Last update instant.
    - `lastRequestAt` string, date-time, nullable, required: Last authenticated request instant.
    - `secret` string, required: Full secret. Returned once, at creation only.

## Other responses

- `default`: Problem details (RFC 9457). Any 4xx/5xx response is one `application/problem+json` document.

---

[API](https://skmtc.dev/spacefast/apis/spacefast-api.md) · [All operations](https://skmtc.dev/spacefast/apis/spacefast-api/llms.txt) · [OpenAPI document](https://skmtc.dev/spacefast/apis/spacefast-api/revisions/bd165bc7c983?raw)
