---
title: Authentication
description: Authenticate AI API requests with a WP Engine AI-issued API key, and choose the right key type and scopes.
url: https://developers.wpengine.com/docs/ai/api/authentication/
image: https://developers.wpengine.com/og/docs/ai/api/authentication.png
---

# Authentication

Every AI API request, except requests to the model catalog, must include a bearer token in the `Authorization` header:

```http
Authorization: Bearer <token>
```

On the `/v1/messages` endpoints, you can send an API key in an `x-api-key` header instead, as the Anthropic software development kits (SDKs) do.

The model catalog (`GET /v1/models` and `GET /v1/models/{owner}/{model}/schema`) is public and requires no authentication.

API keys are the credential for developer integrations with the HTTP API. Model Context Protocol (MCP) clients authenticate with [OAuth](https://developers.wpengine.com/docs/ai/api/authentication/#authenticate-mcp-clients-with-oauth). You create and revoke keys in the [WP Engine User Portal](https://my.wpengine.com/ai/gateways-and-api/api-keys).

## Choose a key type

The AI API has two key types. They differ in whether the key carries a person:

| Type        | Prefix     | Acts as                   | Use for                                        |
| ----------- | ---------- | ------------------------- | ---------------------------------------------- |
| **User**    | `wpe_pat_` | The person who created it | Anything acting for a person over the HTTP API |
| **Account** | `wpe_`     | Nobody                    | Service-to-service calls and automation        |

The prefix is the quickest way to tell which kind of key you pasted into a client.

> **Tip**
>
> The API and its error messages call these `personal` and `project` keys.

Only the person who created a user key can see it. Nobody else on the account can see, edit, or revoke it. To them, the key doesn’t exist. Consider this before you rely on a user key in shared infrastructure.

## Create a key

1. In the WP Engine User Portal, go to **AI** → **Gateways & API** → **API Keys**.

2. Select **Create API Key**.

3. Enter a **Name** that tells you where you use the key, such as `My Company Production`.

4. Choose the **Key Type** — **Account** or **User**.

5. Choose the **Permissions**:

   - **Full access** — write on every scope.
   - **Read only** — read on every scope.
   - **Restricted** — set **Chat**, **Image**, **Speech**, **Transcribe**, and **MCP** individually to **Write**, **Read**, or **None**.

6. Select **Create Key**, then copy the secret. The User Portal displays it **once**.

![Screenshot: Creating a User API key in the WP Engine User Portal](https://developers.wpengine.com/_astro/create-user-key.COcZrl4a_LKIHd.webp)

> **Keep your key secret**
>
> The User Portal displays a key only once. Save it somewhere safe before you close the dialog.
>
> The key is sensitive. Keep it out of source control, client-side code, logs, and screenshots. [Replace it](https://developers.wpengine.com/docs/ai/api/authentication/#rotate-a-key) if it leaks, if you suspect it might have leaked, or if you lose it.

## Choose scopes

Every endpoint requires a scope at a level: `read` or `write`. The `write` level also grants `read`. If a key doesn’t hold the level an endpoint requires, the request returns `403`.

A key’s scopes come from the permissions you chose when you created it. **Full access** and **Read only** cover every scope in the table below. **Restricted** lets you set `chat`, `image`, `speech`, `transcribe`, and `mcp` individually. The remaining scopes are `none` on a restricted key. If you need the knowledge base, agents, or sessions endpoints, choose **Full access** or **Read only**.

The `mcp` scope is deprecated. The MCP server no longer runs tools for an API key. A key holding `mcp: write` still connects, but every tool call returns a `forbidden` tool error. The MCP server refuses a key without `mcp: write`, including one holding `mcp: read`, with a `403` before it connects. Connect MCP clients with [OAuth](https://developers.wpengine.com/docs/ai/api/authentication/#authenticate-mcp-clients-with-oauth) instead.

| Scope            | Covers                                                                                                                                                                   |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `chat`           | `POST /v1/chat/completions`, `POST /v1/messages`, `POST /v1/messages/count_tokens`, `POST /v1/content/summarize`, `POST /v1/content/suggest-taxonomy`. All need `write`. |
| `image`          | `POST /v1/images/generations` and `POST /v1/images/alt-text`. Both need `write`.                                                                                         |
| `speech`         | `POST /v1/audio/speech`. Needs `write`.                                                                                                                                  |
| `transcribe`     | `POST /v1/audio/transcriptions`. Needs `write`.                                                                                                                          |
| `collections`    | Knowledge base collections under `/v1/kb/collections`, and `/v1/sites/{site_identifier}`. `read` to get or list, `write` to create, update, or delete.                   |
| `documents`      | Documents under `/v1/kb/collections/{id}/documents`. `read` to fetch one, `write` to index or delete.                                                                    |
| `search`         | `POST /v1/kb/collections/{id}/search`. Needs `read`.                                                                                                                     |
| `agents`         | `/v1/agents` and `/v1/sessions`. `read` to get or list, `write` to create, update, archive, or delete.                                                                   |
| `session_events` | Session events and response feedback under `/v1/sessions/{session_id}`. Accepted in place of `agents` on those routes only.                                              |
| `mcp`            | Deprecated. `/v1/mcp` no longer runs tools for an API key. Needs `write` to connect.                                                                                     |

> **Tip**
>
> Most endpoints require a single scope. The session event and response feedback routes are the exception. They accept either `agents` or `session_events`.

## Authenticate MCP clients with OAuth

An MCP client authenticates a user with OAuth, so each call runs as that user.

An OAuth session belongs to one user. As a result, the server attributes every call to that user and checks that user’s access on every call.

To run a WordPress ability, the user who authenticated with OAuth must connect their WordPress account to the target site. The server checks this on every call:

- `run_site_ability` checks that this user has connected their WordPress account to the target site
- `wp_connection_status` in `list_account_sites` reports whether this user is connected, site by site
- Audit logs name this user as the acting user for every ability run

To set a client up, see [Connect Claude Code](https://developers.wpengine.com/docs/ai/api/mcp/how-to/configure-claude-code/) or [Connect Claude Desktop](https://developers.wpengine.com/docs/ai/api/mcp/how-to/configure-claude-desktop/). To try the server directly, run [MCP Inspector](https://github.com/modelcontextprotocol/inspector) and use its **OAuth** sign-in:

Terminal window

```sh
npx @modelcontextprotocol/inspector https://api.ai.wpengine.com/v1/mcp
```

## Handle a rejected key

A rejected key fails at the HTTP layer, before the request reaches its target endpoint. Both responses below use the gateway’s standard error envelope, and [Errors](https://developers.wpengine.com/docs/ai/api/errors/) documents its fields. The sections below explain what each response means for your key.

### Fix a 401: key not accepted

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="ai-services"


{
  "error": {
    "message": "invalid or revoked API key",
    "type": "authentication_error",
    "code": 401,
    "request_id": "77e3a50c925779840eb98bb29d2a6c0d"
  }
}
```

> **Tip**
>
> A 401 doesn’t tell you what went wrong, by design. A mistyped key, a key from another account, and a key you revoked last week all get the same response. As a result, probing the endpoint reveals nothing about which keys exist.
>
> Under **API Keys** in the User Portal, check the key’s prefix and confirm that the key isn’t revoked. If you can’t account for the key, [rotate it](https://developers.wpengine.com/docs/ai/api/authentication/#rotate-a-key).

### Fix a 403: missing scope

```http
HTTP/1.1 403 Forbidden


{
  "error": {
    "message": "API key does not have 'write' permission for scope 'chat' (granted: 'read').",
    "type": "permission_error",
    "code": 403,
    "request_id": "3c1f9a7e05b24d6c8e2a41f0b9d7e615"
  }
}
```

Unlike a 401, a 403 names the scope and the level your key holds. To fix it, edit the key’s permissions under **API Keys** in the User Portal. Grant the scope at the level the table above lists, such as `write` for `chat`. While you’re testing, you can use **Full access** instead.

## Rotate a key

Keys don’t expire on their own, and the AI API has no rotation endpoint. To rotate a key, create a replacement and then revoke the original:

1. [Create a new key](https://developers.wpengine.com/docs/ai/api/authentication/#create-a-key) with the same type and permissions.
2. Deploy the new key to your application.
3. Confirm the new key works.
4. [Revoke the old key](https://developers.wpengine.com/docs/ai/api/authentication/#revoke-a-key).

Both keys work side by side for as long as you need, so there’s no window when neither key authenticates. Nothing forces a rotation, so put rotation on a schedule rather than rotating only after something goes wrong.

## Revoke a key

1. In the WP Engine User Portal, go to **AI** → **Gateways & API** → **API Keys**.
2. Find the key in the list and select **Delete** (Trash Can).
3. Confirm with **Revoke Key**.

A revoked key stops authenticating. From then on, requests that use it return `401`. You can’t undo a revocation or reactivate a revoked key; you can only create a new one. Only the person who created a user key can revoke it.

---

> #### Using your key
>
> - [Errors — the full response envelope and every status code](https://developers.wpengine.com/docs/ai/api/errors/)
> - [Site and WordPress account connections — the second gate a key does not open](https://developers.wpengine.com/docs/ai/api/site-and-account-connections/)
> - [MCP server — point an AI agent at your sites, authenticated with OAuth](https://developers.wpengine.com/docs/ai/api/mcp/)

## Was this helpful?

Thanks for the feedback!

```json
{"@context":"https://schema.org","@graph":[{"@type":"WebSite","@id":"https://developers.wpengine.com/#website","name":"WP Engine Product Documentation","url":"https://developers.wpengine.com/","description":"Resources for developing, deploying, and scaling on WP Engine.","inLanguage":"en-US","keywords":["API documentation","product documentation","headless WordPress","managed WordPress hosting"],"publisher":{"@id":"https://wpengine.com/#organization"}}]}
```
