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

# API keys: create, list, revoke, and update

> Reference for the API Key management endpoints. Create keys for programmatic access, list existing keys, update names, and revoke keys.

The API Keys endpoints let you manage programmatic access tokens for the Shipfastai API. API keys can be used as an alternative to JWT Bearer tokens for authenticating requests to AI and RAG endpoints. All key management operations require JWT authentication — you cannot use an API key to manage other API keys.

<Note>
  API key management is available on **Pro** and **Enterprise** plans only. All endpoints are mounted under `/api/api-keys/`.
</Note>

***

## POST /api/api-keys

Create a new API key. The plaintext key is returned **only once** in the response. Store it securely — it cannot be retrieved again after creation.

**Headers:**

<ParamField header="Authorization" type="string" required>
  `Bearer <access_token>` — JWT authentication only.
</ParamField>

**Request body:**

<ParamField body="name" type="string" required>
  A descriptive name for the API key (e.g., `"Production server"`, `"CI pipeline"`).
</ParamField>

<ParamField body="expires_at" type="string">
  Optional ISO 8601 expiration timestamp. If omitted, the key does not expire.
</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl --request POST \
    --url http://localhost:8000/api/api-keys \
    --header "Authorization: Bearer <access_token>" \
    --header "Content-Type: application/json" \
    --data '{
      "name": "Production server",
      "expires_at": "2027-01-01T00:00:00Z"
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "http://localhost:8000/api/api-keys",
      headers={"Authorization": f"Bearer {access_token}"},
      json={
          "name": "Production server",
          "expires_at": "2027-01-01T00:00:00Z",
      },
  )
  data = response.json()
  print(f"Save this key: {data['key']}")
  ```
</CodeGroup>

**Response** (`201 Created`) — `ApiKeyCreated`:

<ResponseField name="id" type="string" required>
  UUID of the API key.
</ResponseField>

<ResponseField name="name" type="string" required>
  The name you assigned to the key.
</ResponseField>

<ResponseField name="key_prefix" type="string" required>
  The first 12 characters of the key, used for identification in listings.
</ResponseField>

<ResponseField name="key" type="string" required>
  The full plaintext API key. **This is the only time the full key is returned.** Store it securely.
</ResponseField>

<ResponseField name="is_active" type="boolean" required>
  Whether the key is active. `true` on creation.
</ResponseField>

<ResponseField name="created_at" type="string" required>
  ISO 8601 timestamp of when the key was created.
</ResponseField>

<ResponseField name="last_used_at" type="string">
  ISO 8601 timestamp of last usage. `null` for a newly created key.
</ResponseField>

<ResponseField name="expires_at" type="string">
  ISO 8601 expiration timestamp, or `null` if the key does not expire.
</ResponseField>

```json theme={null}
{
  "id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
  "name": "Production server",
  "key_prefix": "sk_a1b2c3d4e5",
  "key": "sk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0",
  "is_active": true,
  "created_at": "2026-04-09T14:30:00Z",
  "last_used_at": null,
  "expires_at": "2027-01-01T00:00:00Z"
}
```

***

## GET /api/api-keys

List all API keys for the authenticated user, ordered by creation date (newest first). The full key value is never included — only the prefix is shown for identification.

**Headers:**

<ParamField header="Authorization" type="string" required>
  `Bearer <access_token>` — JWT authentication only.
</ParamField>

```bash theme={null}
curl --request GET \
  --url http://localhost:8000/api/api-keys \
  --header "Authorization: Bearer <access_token>"
```

**Response** — array of `ApiKeyResponse`:

<ResponseField name="id" type="string" required>
  UUID of the API key.
</ResponseField>

<ResponseField name="name" type="string" required>
  The name assigned to the key.
</ResponseField>

<ResponseField name="key_prefix" type="string" required>
  The first 12 characters of the key.
</ResponseField>

<ResponseField name="is_active" type="boolean" required>
  Whether the key is active. Revoked keys have `is_active: false`.
</ResponseField>

<ResponseField name="created_at" type="string" required>
  ISO 8601 creation timestamp.
</ResponseField>

<ResponseField name="last_used_at" type="string">
  ISO 8601 timestamp of last usage, or `null`.
</ResponseField>

<ResponseField name="expires_at" type="string">
  ISO 8601 expiration timestamp, or `null`.
</ResponseField>

```json theme={null}
[
  {
    "id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
    "name": "Production server",
    "key_prefix": "sk_a1b2c3d4e5",
    "is_active": true,
    "created_at": "2026-04-09T14:30:00Z",
    "last_used_at": "2026-04-09T15:00:00Z",
    "expires_at": "2027-01-01T00:00:00Z"
  }
]
```

***

## DELETE /api/api-keys/{key_id}

Revoke an API key. This is a soft delete — the key is marked as inactive and can no longer be used for authentication. The key record is retained for audit purposes.

**Path parameters:**

<ParamField path="key_id" type="string" required>
  The UUID of the API key to revoke.
</ParamField>

**Headers:**

<ParamField header="Authorization" type="string" required>
  `Bearer <access_token>` — JWT authentication only.
</ParamField>

```bash theme={null}
curl --request DELETE \
  --url http://localhost:8000/api/api-keys/9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d \
  --header "Authorization: Bearer <access_token>"
```

**Response:**

```json theme={null}
{
  "message": "API key revoked"
}
```

Returns `404` if the key does not exist or does not belong to the authenticated user.

***

## PATCH /api/api-keys/{key_id}

Update an API key's display name.

**Path parameters:**

<ParamField path="key_id" type="string" required>
  The UUID of the API key to update.
</ParamField>

**Headers:**

<ParamField header="Authorization" type="string" required>
  `Bearer <access_token>` — JWT authentication only.
</ParamField>

**Query parameters:**

<ParamField query="name" type="string" required>
  The new name for the API key.
</ParamField>

```bash theme={null}
curl --request PATCH \
  --url "http://localhost:8000/api/api-keys/9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d?name=Staging%20server" \
  --header "Authorization: Bearer <access_token>"
```

**Response** — `ApiKeyResponse` with updated fields:

```json theme={null}
{
  "id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
  "name": "Staging server",
  "key_prefix": "sk_a1b2c3d4e5",
  "is_active": true,
  "created_at": "2026-04-09T14:30:00Z",
  "last_used_at": "2026-04-09T15:00:00Z",
  "expires_at": "2027-01-01T00:00:00Z"
}
```

Returns `404` if the key does not exist or does not belong to the authenticated user.

***

## Using API keys for authentication

Once you have a key, pass it in the `Authorization` header as a Bearer token, the same way you pass a JWT:

```http theme={null}
Authorization: Bearer sk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0
```

API keys are accepted on AI and RAG endpoints alongside JWTs. The backend automatically detects whether the token is a JWT or an API key.
