# API keys (/reference/api-keys)

## Your keys

`GET /v1/api_keys`

Never the secret — `key_id` and `last4` are all that is stored. `created_by: molnpay_staff` marks a key our support minted for you. `limit_per_kind` and `default_scopes` are what a create call is measured against.

### Parameters

- `include_revoked` (query, optional, string, one of: 0, 1)

### Responses

- `200` — Keys, newest first.
  - `object` (required, string)
  - `data` (required, array)
  - `has_more` (required, boolean)
  - `next_cursor` (optional, string | null) — Opaque. Pass as `cursor` for the next page. Do not construct or parse it.
  - `data` (optional, array)
    - `object` (required, string)
    - `id` (required, string (uuid))
    - `key_id` (required, string) — The public display handle: prefix + first 8 hex.
    - `env` (required, string, one of: live, test)
    - `kind` (required, string, one of: checkout, payout, wallet)
    - `name` (required, string)
    - `last4` (required, string) — The token's last 4 hex, so `mp3f9c1a77…4e5a` can be recognised.
    - `scopes` (required, array, of string)
    - `ip_allowlist` (required, array, of string) — Empty = any source IP.
    - `origin_allowlist` (required, array, of string) — https origins on your verified domain, normalised (`https://shop.example.com`), or `https://*.example.com` for every subdomain (not the apex itself). A request that sends an `Origin` header naming any other site is refused with `origin_not_allowed`; a request with no `Origin` (a server) is unaffected. Empty = no origin restriction. This is a lock, not a CORS grant — the API never answers a browser holding a key.
    - `signing_enabled` (required, boolean)
    - `created_by` (required, string, one of: merchant, molnpay_staff) — `molnpay_staff` when our support minted this key for you.
    - `rotated_from` (optional, string | null (uuid))
    - `expires_at` (optional, string | null (date-time))
    - `revoked_at` (optional, string | null (date-time))
    - `last_used_at` (optional, string | null (date-time))
    - `use_count` (optional, string)
    - `created_at` (required, string (date-time))
  - `limit_per_kind` (optional, integer)
  - `default_scopes` (optional, object)
  - `verified_domain` (optional, string | null) — The domain every `origin_allowlist` entry must sit on; null while none is verified.
- `401` — Missing, malformed, expired or revoked API key.
- `403` — `session_required` — called with an API key. Keys are managed by the signed-in owner.
- `429` — Rate limit or quota exceeded. Honour `Retry-After`.
- `500` — Unexpected error.
- `503` — A dependency is unavailable. Retryable with backoff.

## Mint a key

`POST /v1/api_keys`

The response carries `token` — **the only time it is ever shown**. We store a peppered hash; nothing can return it later. A lost key is rolled, never recovered.

The key's mode follows the project: a live project mints `mp…`, a test project `mptest…`.

`scopes` defaults to the kind's read+write set (see `default_scopes` on the list). `payouts:create` is never a default. `signing_enabled` is the third factor a payout key needs to move funds, on top of kind and scope.

`ip_allowlist`: exact IPs or CIDR blocks, v4 or v6, up to 32. Empty means any source. The address judged is the one your server presents to us. Leave it empty on serverless platforms.

`origin_allowlist`: https origins (scheme and host), up to 16, each on your verified domain or a subdomain of it. `https://*.example.com` covers every subdomain of `example.com` at any depth, but not `example.com` itself — list both for the whole site (the console does, by default). **Required once your domain is verified** (`origin_required`); refused while it is not (`domain_not_verified`). A request whose `Origin` header names another site is refused; server calls send no `Origin` and are unaffected. It is a lock, not a CORS grant.

Five enabled keys per kind.

### Request body (required)

- `name` (required, string) — How you will recognise it later.
- `kind` (required, string, one of: checkout, payout, wallet)
- `scopes` (optional, array, of string) — Concrete scopes, `*`, or `<group>:*`.
- `ip_allowlist` (optional, array, of string)
- `origin_allowlist` (optional, array, of string)
- `signing_enabled` (optional, boolean)

### Responses

- `201` — Minted. `token` appears here and nowhere else.
  - `object` (required, string)
  - `id` (required, string (uuid))
  - `key_id` (required, string) — The public display handle: prefix + first 8 hex.
  - `env` (required, string, one of: live, test)
  - `kind` (required, string, one of: checkout, payout, wallet)
  - `name` (required, string)
  - `last4` (required, string) — The token's last 4 hex, so `mp3f9c1a77…4e5a` can be recognised.
  - `scopes` (required, array, of string)
  - `ip_allowlist` (required, array, of string) — Empty = any source IP.
  - `origin_allowlist` (required, array, of string) — https origins on your verified domain, normalised (`https://shop.example.com`), or `https://*.example.com` for every subdomain (not the apex itself). A request that sends an `Origin` header naming any other site is refused with `origin_not_allowed`; a request with no `Origin` (a server) is unaffected. Empty = no origin restriction. This is a lock, not a CORS grant — the API never answers a browser holding a key.
  - `signing_enabled` (required, boolean)
  - `created_by` (required, string, one of: merchant, molnpay_staff) — `molnpay_staff` when our support minted this key for you.
  - `rotated_from` (optional, string | null (uuid))
  - `expires_at` (optional, string | null (date-time))
  - `revoked_at` (optional, string | null (date-time))
  - `last_used_at` (optional, string | null (date-time))
  - `use_count` (optional, string)
  - `created_at` (required, string (date-time))
  - `token` (required, string) — `mp` or `mptest` + 64 hex. Returned ONLY here; no endpoint can read it back. Store it now.
- `400` — Validation failed — an unknown scope grant, or an entry that is not an IP or CIDR.
- `401` — Missing, malformed, expired or revoked API key.
- `403` — `session_required` — called with an API key.
- `422` — `api_key_limit` — five keys of this kind already exist. `origin_required`, `origin_not_on_domain` or `domain_not_verified` — see `origin_allowlist`.
- `429` — Rate limit or quota exceeded. Honour `Retry-After`.
- `500` — Unexpected error.
- `503` — A dependency is unavailable. Retryable with backoff.

## GET /v1/api_keys/{id}

`GET /v1/api_keys/{id}`

### Parameters

- `id` (path, required, string (uuid))

### Responses

- `200` — The key. Never the token.
  - `object` (required, string)
  - `id` (required, string (uuid))
  - `key_id` (required, string) — The public display handle: prefix + first 8 hex.
  - `env` (required, string, one of: live, test)
  - `kind` (required, string, one of: checkout, payout, wallet)
  - `name` (required, string)
  - `last4` (required, string) — The token's last 4 hex, so `mp3f9c1a77…4e5a` can be recognised.
  - `scopes` (required, array, of string)
  - `ip_allowlist` (required, array, of string) — Empty = any source IP.
  - `origin_allowlist` (required, array, of string) — https origins on your verified domain, normalised (`https://shop.example.com`), or `https://*.example.com` for every subdomain (not the apex itself). A request that sends an `Origin` header naming any other site is refused with `origin_not_allowed`; a request with no `Origin` (a server) is unaffected. Empty = no origin restriction. This is a lock, not a CORS grant — the API never answers a browser holding a key.
  - `signing_enabled` (required, boolean)
  - `created_by` (required, string, one of: merchant, molnpay_staff) — `molnpay_staff` when our support minted this key for you.
  - `rotated_from` (optional, string | null (uuid))
  - `expires_at` (optional, string | null (date-time))
  - `revoked_at` (optional, string | null (date-time))
  - `last_used_at` (optional, string | null (date-time))
  - `use_count` (optional, string)
  - `created_at` (required, string (date-time))
- `401` — Missing, malformed, expired or revoked API key.
- `403` — The key lacks the scope, its IP allowlist rejected the source, or signing is disabled.
- `404` — Not found.
- `429` — Rate limit or quota exceeded. Honour `Retry-After`.
- `500` — Unexpected error.
- `503` — A dependency is unavailable. Retryable with backoff.

## Rename, re-scope, pin to IPs, or toggle signing

`PATCH /v1/api_keys/{id}`

Takes effect within a minute everywhere. An empty `ip_allowlist` clears the restriction. `origin_allowlist` follows the create rules: it cannot be emptied while your domain is verified.

### Parameters

- `id` (path, required, string (uuid))

### Request body (required)

- `name` (optional, string)
- `scopes` (optional, array, of string)
- `ip_allowlist` (optional, array, of string)
- `origin_allowlist` (optional, array, of string)
- `signing_enabled` (optional, boolean)

### Responses

- `200` — Updated.
  - `object` (required, string)
  - `id` (required, string (uuid))
  - `key_id` (required, string) — The public display handle: prefix + first 8 hex.
  - `env` (required, string, one of: live, test)
  - `kind` (required, string, one of: checkout, payout, wallet)
  - `name` (required, string)
  - `last4` (required, string) — The token's last 4 hex, so `mp3f9c1a77…4e5a` can be recognised.
  - `scopes` (required, array, of string)
  - `ip_allowlist` (required, array, of string) — Empty = any source IP.
  - `origin_allowlist` (required, array, of string) — https origins on your verified domain, normalised (`https://shop.example.com`), or `https://*.example.com` for every subdomain (not the apex itself). A request that sends an `Origin` header naming any other site is refused with `origin_not_allowed`; a request with no `Origin` (a server) is unaffected. Empty = no origin restriction. This is a lock, not a CORS grant — the API never answers a browser holding a key.
  - `signing_enabled` (required, boolean)
  - `created_by` (required, string, one of: merchant, molnpay_staff) — `molnpay_staff` when our support minted this key for you.
  - `rotated_from` (optional, string | null (uuid))
  - `expires_at` (optional, string | null (date-time))
  - `revoked_at` (optional, string | null (date-time))
  - `last_used_at` (optional, string | null (date-time))
  - `use_count` (optional, string)
  - `created_at` (required, string (date-time))
- `400` — No fields to update, or validation failed.
- `401` — Missing, malformed, expired or revoked API key.
- `403` — The key lacks the scope, its IP allowlist rejected the source, or signing is disabled.
- `404` — Not found.
- `409` — `api_key_revoked` — a revoked key cannot be edited.
- `429` — Rate limit or quota exceeded. Honour `Retry-After`.
- `500` — Unexpected error.
- `503` — A dependency is unavailable. Retryable with backoff.

## Revoke, immediately and permanently

`DELETE /v1/api_keys/{id}`

Idempotent. The row stays, marked `revoked_at`, so the audit trail keeps its name.

### Parameters

- `id` (path, required, string (uuid))

### Responses

- `200` — Revoked.
  - `object` (required, string)
  - `id` (required, string (uuid))
  - `key_id` (required, string) — The public display handle: prefix + first 8 hex.
  - `env` (required, string, one of: live, test)
  - `kind` (required, string, one of: checkout, payout, wallet)
  - `name` (required, string)
  - `last4` (required, string) — The token's last 4 hex, so `mp3f9c1a77…4e5a` can be recognised.
  - `scopes` (required, array, of string)
  - `ip_allowlist` (required, array, of string) — Empty = any source IP.
  - `origin_allowlist` (required, array, of string) — https origins on your verified domain, normalised (`https://shop.example.com`), or `https://*.example.com` for every subdomain (not the apex itself). A request that sends an `Origin` header naming any other site is refused with `origin_not_allowed`; a request with no `Origin` (a server) is unaffected. Empty = no origin restriction. This is a lock, not a CORS grant — the API never answers a browser holding a key.
  - `signing_enabled` (required, boolean)
  - `created_by` (required, string, one of: merchant, molnpay_staff) — `molnpay_staff` when our support minted this key for you.
  - `rotated_from` (optional, string | null (uuid))
  - `expires_at` (optional, string | null (date-time))
  - `revoked_at` (optional, string | null (date-time))
  - `last_used_at` (optional, string | null (date-time))
  - `use_count` (optional, string)
  - `created_at` (required, string (date-time))
- `401` — Missing, malformed, expired or revoked API key.
- `403` — The key lacks the scope, its IP allowlist rejected the source, or signing is disabled.
- `404` — Not found.
- `429` — Rate limit or quota exceeded. Honour `Retry-After`.
- `500` — Unexpected error.
- `503` — A dependency is unavailable. Retryable with backoff.

## Replace a key without an outage

`POST /v1/api_keys/{id}/roll`

Mints a new key with identical grants and gives the old one `grace_hours` (default 24, max 168) to live — both work until then, so deploy the new key before the old one dies. The new `token` is shown only in this response.

### Parameters

- `id` (path, required, string (uuid))

### Request body

- `grace_hours` (optional, integer)

### Responses

- `201` — Rolled.
  - `object` (required, string)
  - `id` (required, string (uuid))
  - `key_id` (required, string) — The public display handle: prefix + first 8 hex.
  - `env` (required, string, one of: live, test)
  - `kind` (required, string, one of: checkout, payout, wallet)
  - `name` (required, string)
  - `last4` (required, string) — The token's last 4 hex, so `mp3f9c1a77…4e5a` can be recognised.
  - `scopes` (required, array, of string)
  - `ip_allowlist` (required, array, of string) — Empty = any source IP.
  - `origin_allowlist` (required, array, of string) — https origins on your verified domain, normalised (`https://shop.example.com`), or `https://*.example.com` for every subdomain (not the apex itself). A request that sends an `Origin` header naming any other site is refused with `origin_not_allowed`; a request with no `Origin` (a server) is unaffected. Empty = no origin restriction. This is a lock, not a CORS grant — the API never answers a browser holding a key.
  - `signing_enabled` (required, boolean)
  - `created_by` (required, string, one of: merchant, molnpay_staff) — `molnpay_staff` when our support minted this key for you.
  - `rotated_from` (optional, string | null (uuid))
  - `expires_at` (optional, string | null (date-time))
  - `revoked_at` (optional, string | null (date-time))
  - `last_used_at` (optional, string | null (date-time))
  - `use_count` (optional, string)
  - `created_at` (required, string (date-time))
  - `token` (required, string) — `mp` or `mptest` + 64 hex. Returned ONLY here; no endpoint can read it back. Store it now.
  - `old_key_expires_at` (optional, string (date-time))
- `401` — Missing, malformed, expired or revoked API key.
- `403` — The key lacks the scope, its IP allowlist rejected the source, or signing is disabled.
- `404` — Not found.
- `409` — `api_key_revoked`.
- `429` — Rate limit or quota exceeded. Honour `Retry-After`.
- `500` — Unexpected error.
- `503` — A dependency is unavailable. Retryable with backoff.