---
title: "Mint a key. The raw secret is in THIS response only. A key can never mint one broader than itself; a test key mints only test keys; a live secret key may mint the sandbox pair."
method: POST
path: "/keys"
---

# Mint a key. The raw secret is in THIS response only. A key can never mint one broader than itself; a test key mints only test keys; a live secret key may mint the sandbox pair.

`POST /keys`

## Request

- Base URL: `https://widget.reelquoteai.com/api/v1`
- URL: `https://widget.reelquoteai.com/api/v1/keys`
- Auth: HTTP bearer

## Request body

- object
  - `kind` 'publishable' | 'secret'
  - `mode` 'live' | 'test': Default: the calling key's mode.
  - `scopes` string[], nullable: null (default) = everything the kind allows
  - `label` string: Up to 80 chars, e.g. 'CRM sync'

## Response `201`

Minted. `key.secret` is shown once.

- object
  - `key` ApiKey
    - `id` string, required: Key id (ULID) — the handle for rotate and revoke
    - `kind` 'publishable' | 'secret', required: pk_ is browser-safe and drives the funnel; sk_ is server-only.
    - `mode` 'live' | 'test', required: test keys create sandbox jobs: fixed inventory at /submit, no billing, no email, webhooks say livemode:false, and they see only test jobs.
    - `prefix` string, required: The first 12 characters, for recognising it in a list
    - `label` string, nullable: Free text you gave it
    - `scopes` string[], nullable, required: null = everything its kind allows
    - `status` 'active' | 'revoked', required
    - `created_at` string, date-time, required
    - `last_used_at` string, date-time, nullable
    - `expires_at` string, date-time, nullable: Set by rotation: the key works until this instant
    - `revoked_at` string, date-time, nullable
    - `rotated_to` string, nullable: The id of the key that replaced it
    - `secret` string: ONLY in the response that minted or rotated it: the raw key. Store it now; it is never shown again.

## Other responses

- `400`: `invalid_request` — A field is missing, malformed or not allowed in this state. `message` says which. `secret_key_in_url` — A secret key was sent as `?k=`. Secret keys go in the Authorization header only.
- `401`: `unauthorized` — No key, an unknown key, or a revoked key.
- `403`: `secret_key_required` — The endpoint is server-only; a publishable key cannot reach it. `insufficient_scope` — The key does not carry the scope this call needs (named in `message`). Mint one that does from the keys page or POST /keys. `key_rotated` — The calling key has been rotated and is in its grace period: it still serves jobs and quotes, but key management and the delivery log belong to its replacement. `mode_mismatch` — A test key tried to manage a live key. Live keys manage both modes; test keys manage test keys only.
- `429`: `rate_limited` — Per-key limit (60/min publishable, 300/min secret), the mover's daily ballpark or video-analysis cap, or a visitor's daily catalog-search cap. Obey `Retry-After`. Carries `Retry-After` in seconds.

---

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