---
title: "Create an API key for a user."
method: POST
path: "/api/users/{username}/api-keys"
tags: ["API Keys"]
---

# Create an API key for a user.

`POST /api/users/{username}/api-keys`

## Request

- Base URL: `/` (relative, and the document was published on a file host, not at the API’s origin)
- URL: `/api/users/{username}/api-keys`
- Auth: HTTP bearer

Mints a key for the named user with the scopes given in the body, and returns its value once: only the hash is stored, so the response is the one chance to capture it. The scopes must be a subset of those the calling key holds, so this cannot be used to widen access beyond the credential making the request. Requires an administrator as well as the scope. A key for another user who already has, or has had, a key (including a session key) also requires ADMIN_CROSS_USER_ACCESS_ENABLED, and is recorded as admin_cross_user_access as well; without it an administrator can create only a user's first key, to provision them. Recorded as an api_key_created audit event naming the calling user and API key. Requires the `api-keys:write` scope.

## Path parameters

- `username` string, required

## Headers

- `Authorization` string, required

## Request body

- CreateApiKeyRequest
  - `scopes` string[]

## Response `201`

The key was created. The value is returned here and nowhere else.

- CreatedApiKeyResponse
  - `apiKey` string
  - `id` string
  - `scopes` string[]
  - `username` string

## Other responses

- `400`: No scopes were given, or one of them is not a scope.
- `401`: The Authorization header is absent or the API key is not recognized.
- `403`: The key does not hold api-keys:write, is a session key, the caller is not an administrator, a requested scope is not held by the calling key, or the user already has or has had a key and ADMIN_CROSS_USER_ACCESS_ENABLED is off.
- `404`: There is no active user with that username.
- `406`: No representation matches Accept.
- `413`: Content Too Large
- `415`: Unsupported Media Type
- `500`: Internal Server Error
- `503`: Service Unavailable

## Changes

- **2026-10-10** `c4f85daa9208`: 14 info
  - added the optional property `field` to the response with the `400` status
  - added the optional property `field` to the response with the `401` status
  - added the optional property `field` to the response with the `403` status
  - added the optional property `field` to the response with the `413` status
  - …10 more
- **2026-10-05** `4082c17721d8`: 6 breaking, 6 info
  - removed the media type `*/*` for the response with the status `400`
  - removed the media type `*/*` for the response with the status `401`
  - removed the media type `*/*` for the response with the status `413`
  - removed the media type `*/*` for the response with the status `415`
  - …8 more
- **2026-10-05** `89b2d9d7eb05`: 3 info
  - api tag `API Keys` added
  - api tag `Users` removed
  - added the optional property `id` to the response with the `201` status
- **2026-10-05** `ca52c57b5c4c`: 2 info
  - api tag `Users` added
  - api tag `Provisioning` removed
- **2026-09-13** `387a6607d8ca`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/philterd/apis/philter-api/changes/api/users/:username/api-keys/post.md)

---

[API](https://skmtc.dev/philterd/apis/philter-api.md) - [All operations](https://skmtc.dev/philterd/apis/philter-api/llms.txt) - [OpenAPI document](https://skmtc.dev/philterd/apis/philter-api/revisions/aa3d7ad1aab4?raw)
