> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blobhub.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Create API Key

Create a new API key for a target. The key secret is returned only once.

## **POST** `/api-keys/target/:target/:target_id`

### Path Parameters

| Parameter   | Type   | Required | Description                              |
| :---------- | :----- | :------- | :--------------------------------------- |
| `target`    | string | Yes      | Resource type: `org`, `blob`, or `user`. |
| `target_id` | string | Yes      | The unique ID of the target resource.    |

<Note>
  `target=user` mints a key that authenticates **as** that user rather than as the caller — the only way to
  act as a [service account](/general/service-accounts). `target_id` names the account, not the caller.
</Note>

### Request Body

| Parameter     | Type   | Required | Description                       |
| :------------ | :----- | :------- | :-------------------------------- |
| `role`        | string | Yes      | `read`, `write`, or `admin`.      |
| `description` | string | No       | Description of the key's purpose. |

<Note>
  A key never grants more than this role, even when its user separately holds a higher-role membership on
  the target — see [Role Cap](/rest-api/authentication#role-cap).
</Note>

### Response

| Parameter | Type   | Description                                      |
| :-------- | :----- | :----------------------------------------------- |
| `api_key` | object | The created API key object including the secret. |

### Errors

| Status | Error                      | Cause                                                                                        |
| :----- | :------------------------- | :------------------------------------------------------------------------------------------- |
| 400    | `cannot_mint_while_acting` | `target` is `org` or `blob` and the caller is an [acting token](/rest-api/auth/impersonate). |
| 403    | `forbidden`                | Missing `admin` access to the target, or no human behind the request.                        |

Minting is a human act: a service account's own key gets `403` for every target, even one it otherwise
administers with `admin` — this stops a leaked automation key from issuing itself a permanent replacement.
Use an [acting token](/rest-api/auth/impersonate) when automation genuinely has to mint on a human's behalf;
it carries that human, so it is admitted here at `target=user`. This is one of ten operations on the
[access perimeter](/general/service-accounts#the-access-perimeter), all refused on the same terms.

**`target=user` is untouched, and is the case acting mode exists for**: a human acting as an account, minting
that account its own key. `org` and `blob` are refused with `400`, because such a key authenticates as
*whoever mints it* (`user_id`, below), and while acting that is the impersonated account rather than the
human — a key every perimeter operation would then refuse for the rest of its life, indistinguishable from a
working one until it fails. Mint those as yourself; an account is given reach into an Organization or a Blob
by membership, not by that target's own key.

```json theme={null}
{
  "status": "failure",
  "error": "cannot_mint_while_acting",
  "message": "An org- or blob-scoped API key authenticates as whoever mints it, so one minted while acting as a service account would have no human behind it. Mint it as yourself; a service account reaches an org or blob through membership, not through the target's own key."
}
```

### Example

<CodeGroup>
  ```bash Request theme={null}
  curl -X POST https://api.blobhub.io/v1/api-keys/target/user/fe827336-3548-429c-a37c-a06d9b689eac \
    -H "X-API-Key: $ORG_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "role": "write"
    }'
  ```

  ```json Response theme={null}
  {
    "status": "success",
    "api_key": {
      "id": "d5fd416d-…",
      "role": "write",
      "target": "user",
      "target_id": "fe827336-3548-429c-a37c-a06d9b689eac",
      "target_target_id": "user#fe827336-3548-429c-a37c-a06d9b689eac",
      "user_id": "fe827336-3548-429c-a37c-a06d9b689eac",
      "created_by_user_id": "619621fb-…",
      "key_prefix": "LGc13pgG***",
      "created_at": "2026-08-03T21:10:55.908131Z",
      "key": "LGc13pgG..."
    }
  }
  ```
</CodeGroup>

`target_target_id` is an internal index key the API happens to serialize — safe to ignore, the same as
`owner_target_target_id` on a [service account](/general/service-accounts). `key` is returned only in this
response — store it now.

Two fields carry the identity of the key, and they are easy to confuse. `user_id` is the principal the key
**authenticates as**: the target user for a `target=user` key, or the caller for `target=org` and
`target=blob`, exactly as before. `created_by_user_id` is provenance — the human who **minted** it. They
are never the same field: conflating them is the bug service accounts close, where a key's `user_id` was
always its creator, so an automation key always looked, in the platform's own records, like whoever set it
up.
