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

# Authentication

> Create a workspace API key, send it as a bearer token, scope it, and rotate it

Every `/v1` request authenticates with a **workspace API key**: a machine credential, scoped to one workspace and to explicit permissions.

## Create a key

Keys are created in the console, under **Configuration → API keys**:

1. Open your workspace, go to **Configuration**.
2. Under **API keys**, choose **Create key**, name it, and pick the scopes it needs.
3. Copy the key **now**: it is shown exactly once and stored only as a hash. It cannot be recovered; if you lose it, create another.

A key looks like:

```
wdy_live_x7Qk2m9fB3nP8dR4sT6vW1yZ_a1b2
└──┬─┘ └┬─┘ └──────────┬──────────┘ └─┬─┘
 prefix env      random secret     checksum
```

* **`live` vs `test`** is the environment segment. Use a `test` key against a test workspace and a `live` key in production; both authenticate the same way.
* The first 12 characters (`wdy_live_x7Q…`) are the **prefix** the console lists: enough to recognise a key, never enough to use it.

## Send it

As a bearer token, on every request:

```bash theme={null}
curl https://api.wondeya.com/v1/me \
  -H "Authorization: Bearer wdy_live_…"
```

`GET /v1/me` is the call to make first. It returns the workspace the key belongs to and the scopes it was granted:

```json theme={null}
{ "workspaceId": "6a1f…", "scopes": ["knowledge:read", "chat:write"] }
```

Any key failure (missing, malformed, unknown, revoked or expired) is the same `401` `wondeya.api_key.invalid`, on purpose: distinguishing them would be an oracle. A request whose key lacks a required scope is a `403` `wondeya.api_key.scope_missing`.

## Scopes

A key is granted permissions per area and per direction. Grant only what an integration needs.

| Scope                                | Grants                                                                                                                 |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| `projects:read` / `projects:write`   | Sites: read, or create / edit / re-address / delete                                                                    |
| `landings:read` / `landings:write`   | Agents and pages: read, or create / edit / publish / delete                                                            |
| `knowledge:read` / `knowledge:write` | Knowledge sources and topics; retrieval (`/query`) needs `knowledge:read`                                              |
| `assets:read` / `assets:write`       | Images                                                                                                                 |
| `brand:read` / `brand:write`         | The site brand and its navbar/footer                                                                                   |
| `analytics:read`                     | Usage (`GET /v1/usage`)                                                                                                |
| `domains:write`                      | Reserved for the custom-domain endpoints, which have not shipped on `/v1` yet; manage domains in the console meanwhile |
| `chat:write`                         | The generative turn (`POST /v1/agents/{id}/chat`), which spends a credit                                               |

The appearance **theme** rides `landings:*` (it is part of what the pages surface configures). A key with **no** scope is refused: "no scope" must never read as "every scope".

## Rotate

A workspace keeps at most **two active keys** at once, so a rotation has an overlap window:

1. Create a second key with the same scopes.
2. Deploy it everywhere the old one was used.
3. Revoke the first key in the console.

Revoking is immediate. There is no "regenerate": a rotated key is a new key, and the old secret is gone the moment it is revoked.
