---
title: "Create a new API key. Plaintext returned exactly once."
method: POST
path: "/v1/api-keys"
tags: ["api-keys"]
---

# Create a new API key. Plaintext returned exactly once.

`POST /v1/api-keys`

## Request

- Base URL: `https://api.andish.ru` (the document declares no server; this is the origin it was published from)
- URL: `https://api.andish.ru/v1/api-keys`
- Auth: none declared

Issue a new API key for the authenticated tenant.

The plaintext key is returned in this response EXACTLY ONCE (AC #1).
After this call the plaintext is irrecoverable — the database stores
only the bcrypt hash (STRIDE I1/I3).

Scopes default to ["read", "write"] when not supplied — parity with the
onboarding key (AC #3).  Pass scopes=["read"] explicitly for a read-only key.

Raises 412 when the tenant's tier is empty or unknown (ADR-083 amendment 1;
fail closed, no key is created).
Raises 422 api_key_limit_reached when the tenant's active key count
reaches the tier limit (AC #10).

## Request body

- ApiKeyCreateRequest: Request body for POST /v1/api-keys. scopes defaults to ["read", "write"] — matching the onboarding key so that Settings-created keys work out of the box for both uploads and queries. The caller may pass ["read"] explicitly to create a read-only key (e.g. for a dashboard widget). An empty list is rejected (min_length=1) so the caller cannot accidentally issue a zero-scope key that fails all auth checks. "admin" scope is blocked at this layer (STRIDE E2): admin keys are issued only by the system, never through self-service. Duplicate values are deduplicated by the service layer. name is an optional human-readable label (BUG-APIKEY-DISPLAY / ADR-059). Validated: trimmed, control chars stripped, empty-after-trim → None, max 64 chars. Unicode (including Cyrillic) is allowed.
  - `scopes` string[]: Permission scopes for the new key. Allowed values: read, write. Defaults to [read, write] — parity with the onboarding key. Pass [read] explicitly for a read-only key.
  - `name` string, nullable: Optional human-readable label for the key (≤ 64 chars after trimming). ASCII control characters are stripped. Empty string or whitespace-only is treated as no label (None).

## Response `201`

Successful Response

- ApiKeyCreateResponse: Response for POST /v1/api-keys. api_key is the plaintext credential — returned EXACTLY ONCE (AC #1). After this response the plaintext is unrecoverable; the database only stores the bcrypt hash.
  - `id` string, uuid, required
  - `prefix` string, required
  - `scopes` string[], required
  - `created_at` string, date-time, required
  - `api_key` string, required: Plaintext API key — shown ONCE on creation. Copy it now; it cannot be retrieved again.

## Other responses

- `401`: Not authenticated or API-key auth used (JWT required)
- `412`: The workspace account is closed for issuing keys (empty or unknown tier). No key is created. Body: machine code + short message, no tier, no tenant id.
- `422`: api_key_limit_reached — tier limit of active keys exceeded

---

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