# Issue an API key for an application (key returned once)

> Issue an API key for an application (key returned once): CustomDomain™ API reference in the applications group. Covers parameters, request body and responses.

Source: https://docs.customdomain.ai/docs/api-reference/applications/createApiKey

## `POST https://api.customdomain.ai/v1/applications/{id}/keys`

Needs `keys:manage` (admin and above). A key minted with a member's key acts with that member's role. The workspace default key (`is_default`) cannot create keys (`403`): a key it minted would not carry its admin limit.

### Authentication

`apiKey`, `managementKey`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Request body (JSON)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | no |  |
| `environment` | `production`, `staging`, `development` | no |  |

### Responses

| Status | Description |
| --- | --- |
| `201` | Created; the full secret is present only in this response |
| `401` | Error envelope: {code, title, details}. |
| `402` | Refused because of the workspace's billing. `billing_required`: the workspace has no active or trialing subscription (a new workspace that has not started its trial, a Free workspace past its grace period, a comp that ended, or a subscription Stripe marked unpaid or canceled). It stops a new connection, the start of a domain purchase and the first live credential (an application, an API key or a rotated client secret). Domains already connected keep working, and an owner or admin sets up billing in the console; `details` says where. A new workspace's first domain is the exception: while it has fewer pending, propagating or live connections than its free domains (ten, unless an operator set another number: `free_domains`), a connect and the start of a purchase go through (`first_domain_free`), and the next one is refused with a `title` saying a card is needed to connect more domains. Credentials still need the trial. `quota_exceeded` (connections only): a plan that is refused past its quota (Starter, and the retired Free) is at it and the deployment enforces quotas. `connections_unavailable`: what a connect-scoped widget token, the tenant's own end user, gets instead of either (and instead of `403 workspace_suspended`), with neutral wording, so the tenant's customers are never told about the tenant's billing or account. A delegated agent token gets the full wording, and `GET /billing/standing` tells it beforehand whether a new connection would be refused. |
| `403` | Error envelope: {code, title, details}. |
| `404` | Error envelope: {code, title, details}. |

### `201` response body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes |  |
| `application_id` | string | yes | The application the key belongs to. |
| `name` | string | yes |  |
| `prefix` | string | yes | The stored, non-secret prefix that identifies the key, for example cd_live_ab12cd34. |
| `environment` | `production`, `staging`, `development` | yes |  |
| `scopes` | array of string | no |  |
| `is_default` | boolean | yes | true on the workspace default key (at most one active per workspace). |
| `last_used_at` | string | no | When the key last authenticated a request. Absent until it has been used. |
| `created_at` | string | yes |  |
| `revoked_at` | string | no | When the key was revoked. Absent while the key is active. |
| `key` | string | no | cd_live_… or cd_test_…; shown once, stored hashed |

The full specification is served at `GET https://api.customdomain.ai/v1/openapi.json`.
