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

# Create an API key and read its secret

> Mint a key and receive its secret exactly once. Takes a signed-in console session, never an API key, so a leaked key can never mint itself a replacement.

This endpoint takes a **signed-in console session**, not an API key. Send the session cookies your browser received when you signed in, plus the `X-CSRF-Token` header set to the value of the `csrf_token` cookie. A request authenticated with an `arukz_sk_` key is refused with `403`, even when that key has the `owner` role: a key cannot manage keys. See [Managing API keys](/administration/api-keys).

The response is the only one that ever contains the key's `secret`. The server does not store it and cannot show it again. If you lose it, create a new key and revoke the old one.

You need the owner or admin role. You cannot give a key a role more senior than your own: an owner can grant any of the five roles, an admin can grant any role except `owner`, and asking for a role above your own returns `403`. Key names are unique within your organization; a name that is already taken returns `409`.

<ParamField header="X-CSRF-Token" type="string" required>
  The value of your session's `csrf_token` cookie (named `__Host-csrf_token` when the console is
  served over HTTPS without a shared cookie domain). Required on this request because it changes
  state; a missing or mismatched token returns `401`.
</ParamField>

<ParamField body="name" type="string" required>
  A label for the key, shown in listings. Leading and trailing whitespace is trimmed; the result
  must be 1 to 120 characters, with no control, invisible formatting (such as zero-width or
  bidirectional-override), private-use or line-separator characters.
</ParamField>

<ParamField body="role" type="string" default="normal">
  The role the key acts with. One of `owner`, `admin`, `editor`, `normal` or `dataset_operator`.
  `normal` is the member role: it can ask assistants questions and read answers, which is what most
  integrations need. Choose the narrowest role that works. See [Organizations and
  roles](/concepts/organizations-and-roles).
</ParamField>

<ParamField body="expires_in_days" type="integer">
  Lifetime in whole days, from 1 to 730. Omit it, or send `null`, for a key that does not expire. An
  expired key stops authenticating without further action.
</ParamField>

## Response

Returns `201 Created` with the key's metadata, its secret and a warning.

<ResponseField name="id" type="string" required>The key's ID (UUID).</ResponseField>
<ResponseField name="name" type="string" required>The key's label.</ResponseField>
<ResponseField name="prefix" type="string" required>A non-secret fragment, `arukz_sk_` followed by the first 8 characters of the ID, so you can match a listing row to the key in your configuration.</ResponseField>
<ResponseField name="role" type="string" required>`owner`, `admin`, `editor`, `normal` or `dataset_operator`.</ResponseField>
<ResponseField name="status" type="string" required>Always `active` on creation.</ResponseField>
<ResponseField name="created_at" type="string" required>When the key was created, ISO 8601 in UTC.</ResponseField>
<ResponseField name="created_by" type="string | null" required>ID of the user who created the key.</ResponseField>
<ResponseField name="expires_at" type="string | null" required>When the key expires, or `null` if it does not.</ResponseField>
<ResponseField name="revoked_at" type="string | null" required>Always `null` on creation.</ResponseField>
<ResponseField name="last_used_at" type="string | null" required>Always `null` on creation.</ResponseField>
<ResponseField name="secret" type="string" required>The full credential, `arukz_sk_<id>_<secret>`. Send it as `Authorization: Bearer <secret>`. Returned only in this response.</ResponseField>
<ResponseField name="warning" type="string" required>A reminder that the secret is shown once and cannot be retrieved again.</ResponseField>

<RequestExample>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  # cookies.txt holds the cookies from your signed-in console session.
  curl --request POST "$MITHUNAI_URL/arukz/api/v1/api-keys" \
    --cookie cookies.txt \
    --header "X-CSRF-Token: $MITHUNAI_CSRF_TOKEN" \
    --header "Content-Type: application/json" \
    --data '{"name": "CI pipeline", "role": "normal", "expires_in_days": 90}'
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import os
  from http.cookiejar import MozillaCookieJar

  import requests

  # cookies.txt holds the cookies from your signed-in console session.
  jar = MozillaCookieJar("cookies.txt")
  jar.load()

  response = requests.post(
      f"{os.environ['MITHUNAI_URL']}/arukz/api/v1/api-keys",
      cookies=jar,
      headers={"X-CSRF-Token": os.environ["MITHUNAI_CSRF_TOKEN"]},
      json={"name": "CI pipeline", "role": "normal", "expires_in_days": 90},
      timeout=30,
  )
  response.raise_for_status()
  key = response.json()
  print(key["secret"])  # Store this now. It is never shown again.
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  // Runs in a page on the MITHUNAI console's own origin, while signed in.
  // csrfToken is the value of the csrf_token cookie.
  const response = await fetch('/arukz/api/v1/api-keys', {
    method: 'POST',
    credentials: 'include',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-Token': csrfToken,
    },
    body: JSON.stringify({ name: 'CI pipeline', role: 'normal', expires_in_days: 90 }),
  })
  const key = await response.json()
  console.log(key.secret) // Store this now. It is never shown again.
  ```
</RequestExample>

<ResponseExample>
  ```json 201 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "id": "3f2a1c9e-8b4d-4e6f-9a1b-2c3d4e5f6a7b",
    "name": "CI pipeline",
    "prefix": "arukz_sk_3f2a1c9e",
    "role": "normal",
    "status": "active",
    "created_at": "2026-09-20T14:03:11.482190+00:00",
    "created_by": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "expires_at": "2026-12-19T14:03:11.482190+00:00",
    "revoked_at": null,
    "last_used_at": null,
    "secret": "arukz_sk_3f2a1c9e-8b4d-4e6f-9a1b-2c3d4e5f6a7b_SYUCMXvMc88oKrt2Jfhbg2aHdLCH5e1dhJi7mx6du8M",
    "warning": "This secret is shown once and cannot be retrieved again. Store it securely."
  }
  ```

  ```json 400 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "validation_error", "message": "That is not a role an API key may be given." }
  ```

  ```json 401 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "authentication_error", "message": "Authentication is required." }
  ```

  ```json 403 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "authorization_error", "message": "API key management requires a signed-in user." }
  ```

  ```json 409 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "conflict", "message": "An API key with that name already exists." }
  ```
</ResponseExample>
