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

# API Keys

> Create and revoke API keys programmatically via the control plane.

Manage API keys programmatically. All three endpoints require the `control:read` scope.

## List API keys

```
GET https://api.usehasp.com/v1/api_keys
Authorization: Bearer hasp_api_live_...
```

Returns all non-revoked keys for your org, cursor-paginated.

### Query parameters

| Parameter | Description                                                                  |
| --------- | ---------------------------------------------------------------------------- |
| `cursor`  | Cursor from a previous response `meta.next_cursor`. Omit for the first page. |

### Response

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": "01JQKEY000000000000000000",
      "name": "production-chat-service",
      "prefix": "hasp_api_live_",
      "last_four": "a3f9",
      "scopes": ["ai:chat"],
      "last_used_at": "2026-05-01T14:22:00+00:00",
      "created_at": "2026-04-15T09:00:00+00:00",
      "revoked_at": null
    }
  ],
  "meta": {
    "next_cursor": null,
    "has_more": false
  }
}
```

The full token is never returned after creation.

***

## Create an API key

```
POST https://api.usehasp.com/v1/api_keys
Authorization: Bearer hasp_api_live_...
Content-Type: application/json
```

### Body

```json theme={null}
{
  "name": "production-chat-service",
  "scopes": ["ai:chat"]
}
```

| Field    | Type   | Required | Description                                   |
| -------- | ------ | -------- | --------------------------------------------- |
| `name`   | string | Yes      | Human-readable label. Max 100 characters.     |
| `scopes` | array  | Yes      | One or more scope strings. Must be non-empty. |

**Valid scopes:** `ai:chat`, `ai:documents`, `ai:embeddings`, `ai:summarize`, `control:read`, `control:webhooks`

### Response (201)

```json theme={null}
{
  "success": true,
  "data": {
    "id": "01JQKEY000000000000000000",
    "name": "production-chat-service",
    "plaintext": "hasp_api_live_a3f9c2...",
    "prefix": "hasp_api_live_",
    "last_four": "a3f9",
    "scopes": ["ai:chat"],
    "created_at": "2026-05-02T10:00:00+00:00"
  }
}
```

`data.plaintext` is the full token. **This is the only time it is returned.** Copy it immediately — HASP stores only the hash.

***

## Revoke an API key

```
DELETE https://api.usehasp.com/v1/api_keys/{key_id}
Authorization: Bearer hasp_api_live_...
```

Revocation is immediate. Any in-flight request using the revoked key will fail on the next middleware check. Keys belonging to a different org return `404`.

### Response

```json theme={null}
{
  "success": true,
  "data": {
    "id": "01JQKEY000000000000000000",
    "revoked_at": "2026-05-02T10:01:00+00:00"
  }
}
```

***

## Error codes

| Code                 | HTTP | Description                                                      |
| -------------------- | ---- | ---------------------------------------------------------------- |
| `INVALID_API_KEY`    | 401  | Caller's key is invalid or revoked                               |
| `MISSING_SCOPE`      | 403  | Key lacks `control:read` scope                                   |
| `VALIDATION_FAILED`  | 422  | Request body invalid (missing name, empty scopes, unknown scope) |
| `RESOURCE_NOT_FOUND` | 404  | Key ID not found or belongs to another org                       |
