---
title: "Issue a programmatic API key for the org (owner/admin; returns the secret ONCE)"
method: POST
path: "/v1/orgs/{org_id}/api-keys"
tags: ["api-keys"]
---

# Issue a programmatic API key for the org (owner/admin; returns the secret ONCE)

`POST /v1/orgs/{org_id}/api-keys`

## Request

- Base URL: `https://api.wavehouse.cloud`
- URL: `https://api.wavehouse.cloud/v1/orgs/{org_id}/api-keys`
- Auth: one of:
  - API key in cookie `whc_session`
  - HTTP bearer

Issues an API key scoped to this org, bound to either all projects or an explicit project-id allow-list, acting as `role` and optionally narrowed by `limits` (`read-only`: no changes; `no-sql`: no raw SQL; `no-destructive`: no deletes, revokes, hand-overs, payments, or destructive SQL). The secret (`whc_key_…`) is returned ONCE in the response and cannot be retrieved again — store it securely. Present it later as `Authorization: Bearer <key>` or `X-API-Key: <key>` to call the customer + engine-proxy surface.

## Path parameters

- `org_id` string, required

## Headers

- `Idempotency-Key` string: A unique value you choose per logical request (a random UUID). A retry with the same key and the same request within 24 hours returns the first response, marked Idempotency-Replayed: true, instead of acting twice. The same key with a different request is refused with 422; one sent while the first is still running gets 409.

## Request body

- CreateAPIKeyInput
  - `all_projects` boolean: grant the key access to EVERY project in the org; else set project_ids
  - `limits` string[]: narrow the key below its role: read-only (no changes), no-sql (no raw SQL), no-destructive (no deletes, revokes, hand-overs, payments, or destructive SQL)
  - `name` string, required: human-readable key name
  - `project_ids` string[]: the project id(s) this key may touch; required unless all_projects=true
  - `role` 'admin' | 'member': org role the key acts as (default admin). member = read-only data surface; admin = full data surface + project mutations. owner is never issuable to a key.

## Response `201`

Created

- CreateAPIKeyOutput
  - `all_projects` boolean, required: true = the key may touch every project in the org
  - `created_at` string, date-time, required
  - `created_by` string, required: user id that created the key
  - `id` string, required
  - `key_prefix` string, required: the key's first 14 characters (whc_key_ + 6), to tell keys apart
  - `last4` string, required: the key's last four characters
  - `last_used_at` string, date-time: absent until first authenticated use; updated at most once a minute
  - `limits` string[], required: what the key may not do even where its role could; empty = only its role applies
  - `name` string, required
  - `project_ids` string[], required: the project id(s) the key may touch (empty when all_projects)
  - `role` string, required: org role the key acts as (admin | member)
  - `secret` string, required: the API key — shown ONCE; store it now, it cannot be retrieved again

## Other responses

- `429`: Rate limited, per client IP, API key or org; a client IP is also refused on every route for a while after repeated authentication failures. A limited operation carries RateLimit-Policy and RateLimit on every response; wait Retry-After seconds before retrying.
- `default`: Error

---

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