# Scopes

_Category: Authentication_

A scope is a permission that a credential holds. REST API endpoints each require a scope, and API keys and OAuth access tokens are checked against the same list: a request that needs `content.write` fails the same way for a Read only key as for a token without that scope.

```
{
  "status": "error",
  "code": "insufficient_scope",
  "message": "This action requires the content.write scope."
}
```

## The scopes

The third column is what the consent screen shows a person when an OAuth app asks for the scope.

| Scope | Allows | Consent screen text |
| --- | --- | --- |
| `content.read` | Reading the help center: sites, articles, categories, images, comments, staged changes and versions, change sets, interface translations and exports. | Read your help-center categories and articles. |
| `content.write` | Every change to content: creating, updating and deleting articles and categories, uploading and deleting images, replying to, moderating and deleting comments, staging and publishing changes, restoring versions, change sets, interface translations and article feedback. | Create and update your help-center articles. |
| `analytics.read` | The analytics reports. | Read your help-center analytics reports. |
| `webhooks.manage` | Listing, creating and deleting webhooks. | Register webhooks that notify it when your content changes. |
| `notes.read` | Reading the private notes your team leaves on articles. | Read the private notes your team leaves on drafts. Readers never see these. |
| `notes.write` | Replying to and resolving those notes. | Reply to and resolve the private notes your team leaves on drafts. |
| `design.read` | Reading the help center's design: the draft, the customization guide, the themes and the published versions. | See your help center's design: its theme, page layouts, components, custom CSS and scripts. |
| `design.write` | Every change to the design: editing the draft, applying themes, discarding the draft, restoring versions into it, and publishing it. That includes the custom CSS and scripts every visitor's browser runs once the design is published. | `Change your help center's design — theme, layouts, components, custom CSS and scripts — and publish it.` |

`content.write` covers every change to content, including deleting articles and categories, deleting images and moderating comments. If your app asks for it, tell your users what the app will do with it.

`design.read` and `design.write` aren't part of `content.read` and `content.write`: a credential needs them to read or change the design, whatever content scopes it holds. See [Design API](https://developers.helpcenter.io/content/design-api).

## Endpoints by scope

Each endpoint requires exactly one scope, except two that any valid credential may call and the single-use image upload URL, which is its own credential.

| Scope | Endpoints |
| --- | --- |
| None (any valid credential) | `GET /v1`, `GET /v1/account` |
| `content.read` | Sites: `GET /v1/sites`<br><br><br>Articles: `GET /v1/articles`, `GET /v1/articles/{articleId}`, `GET /v1/articles/{articleId}/staged`, `GET /v1/articles/{articleId}/versions`<br><br><br>Categories: `GET /v1/categories`<br><br><br>Images: `GET /v1/images`, `GET /v1/images/{imageId}`<br><br><br>Comments: `GET /v1/comments`, `GET /v1/articles/{articleId}/comments`<br><br><br>Change sets (API keys only): `GET /v1/change-sets`, `GET /v1/change-sets/{changeSetId}`, `GET /v1/change-sets/{changeSetId}/status`<br><br><br>Other: `GET /v1/translations`, `GET /v1/export` |
| `content.write` | Articles: `POST /v1/articles`, `POST /v1/articles/bulk`, `PATCH /v1/articles/{articleId}`, `DELETE /v1/articles/{articleId}`, `POST /v1/articles/{articleId}/feedback`<br><br><br>Staged changes and versions: `PATCH /v1/articles/{articleId}/staged`, `DELETE /v1/articles/{articleId}/staged`, `POST /v1/articles/{articleId}/staged/publish`, `POST /v1/articles/{articleId}/versions/{versionId}/restore`<br><br><br>Categories: `POST /v1/categories`, `PATCH /v1/categories/{categoryId}`, `DELETE /v1/categories/{categoryId}`, `PUT /v1/categories/order`, `PUT /v1/categories/{categoryId}/articles/order`<br><br><br>Images: `POST /v1/images`, `POST /v1/images/bulk`, `POST /v1/images/uploads`, `DELETE /v1/images/{imageId}`<br><br><br>Comments: `POST /v1/articles/{articleId}/comments`, `PATCH /v1/comments/{commentId}`, `DELETE /v1/comments/{commentId}`<br><br><br>Interface translations: `POST /v1/translations`, `POST /v1/translations/{translationKey}`<br><br><br>Change sets (API keys only): `POST /v1/change-sets`, `PATCH /v1/change-sets/{changeSetId}`, `DELETE /v1/change-sets/{changeSetId}`, `POST /v1/change-sets/{changeSetId}/items`, `DELETE /v1/change-sets/{changeSetId}/items/{itemId}`, `POST /v1/change-sets/{changeSetId}/publish`, `DELETE /v1/change-sets/{changeSetId}/schedule` |
| `analytics.read` | `GET /v1/analytics/summary`, `GET /v1/analytics/content`, `GET /v1/analytics/searches` |
| `webhooks.manage` | `GET /v1/webhooks`, `POST /v1/webhooks`, `DELETE /v1/webhooks/{webhookId}` |
| `notes.read` | `GET /v1/articles/{articleId}/notes` |
| `notes.write` | `POST /v1/articles/{articleId}/notes/{noteId}/replies`, `POST /v1/articles/{articleId}/notes/{noteId}/resolve` |
| `design.read` | `GET /v1/template`, `GET /v1/template/draft`, `GET /v1/template/schema`, `GET /v1/template/themes`, `GET /v1/template/themes/{themeKey}`, `GET /v1/template/versions` |
| `design.write` | `PATCH /v1/template/draft`, `POST /v1/template/draft/discard`, `POST /v1/template/themes/{themeKey}/apply`, `POST /v1/template/publish`, `POST /v1/template/versions/{versionId}/restore` |
| None (the URL is the credential) | `POST /v1/images/uploads/{intent}`, the upload URL that `POST /v1/images/uploads` returns |

Change sets accept only API keys: an OAuth token that holds the right scope still gets `401` with the message `Unknown API key.` See [Change sets](https://developers.helpcenter.io/content/change-sets-api).

## How API keys map to scopes

An API key doesn't list scopes itself. Its scope in the dashboard, and its **Team notes** and **Design** settings, give it these:

| API key | Scopes it holds |
| --- | --- |
| **Read only** | `content.read`, `analytics.read` |
| **Read & write** | `content.read`, `content.write`, `analytics.read`, `webhooks.manage` |
| **Read only** with **Team notes** | The read-only scopes, plus `notes.read` |
| **Read & write** with **Team notes** | The read and write scopes, plus `notes.read` and `notes.write` |
| **Read only** with **Design** | The read-only scopes, plus `design.read` |
| **Read & write** with **Design** | The read and write scopes, plus `design.read` and `design.write` |

A Read only key never holds `notes.write` or `design.write`. A key gets the design scopes only when it is created with **Allow access to the help center design** ticked, and an existing key can't gain them. To see what a credential can do, call `GET /v1/account`: `kb.capabilities.read` and `kb.capabilities.write` tell you whether it holds `content.read` and `content.write`. See [API keys](https://developers.helpcenter.io/content/api-keys).

## How OAuth tokens get scopes

An OAuth app asks for scopes in the `scope` parameter of the authorization request, separated by spaces:

```
scope=openid email content.read content.write
```

- **All or nothing.** The consent screen lists every requested scope with its description. The person approves the whole list or denies it; they can't pick some scopes and leave others.
- **A token holds what its request asked for.** The granted scopes are in the access token's `scopes` claim, an array. The token response has no `scope` field.
- **Unknown scopes fail.** A scope that isn't on this page ends the authorization with the error `invalid_scope`.
- **No scope, no content.** An authorization request without `scope` gives a token with no scopes. It can call `GET /v1` and `GET /v1/account`; everything else answers `403` with `insufficient_scope`.
- **Asking for more later.** Send the person through authorization again with the longer list. The consent screen appears whenever you ask for a scope they haven't approved for your app; when every scope was approved before, the browser returns to your app without it. What a person approves adds up: a later approval doesn't take back scopes they approved earlier.
- **Refreshing can narrow, not widen.** A refresh request can pass `scope` with fewer scopes than the original token. Asking for one the original didn't have fails with `invalid_scope`.

The full flow is in [OAuth 2.1 for apps and AI clients](https://developers.helpcenter.io/content/oauth).

## OpenID Connect scopes

These identify the person who approved your app. None of them opens a REST API endpoint.

| Scope | What you get | Consent screen text |
| --- | --- | --- |
| `openid` | An `id_token` in the token response. | Authenticate you and confirm your identity. |
| `profile` | The claims `name`, `given_name`, `family_name` and `picture` in the id_token and from userinfo. | Access your basic profile information (name, picture). |
| `email` | The claims `email` and `email_verified`. | Access your email address. |
| `offline_access` | Nothing extra. Every app gets a refresh token whether or not it asks for this scope; it's accepted for clients that always request it. | Stay connected, so you are not asked to sign in again each time its short-lived access expires. |

## Team notes need scope and access

Holding `notes.read` or `notes.write` isn't enough on its own. On every notes request, the person behind the credential (the creator of the API key, or the person who approved the OAuth app) must be an Owner, Admin, Editor or Translator of the help center. If they aren't, the request fails with `403`, the code `notes_forbidden` and this message: "Editorial notes are visible only to team members who can access team comments in the dashboard. The account this credential authenticates as no longer has that access on this help center."

## The design needs scope and access

Holding `design.read` or `design.write` isn't enough on its own either. On every design request, the person behind the credential must be able to edit the help center's design in the dashboard: an Owner, Admin or Editor. If they can't, the request fails with `403`, the code `design_forbidden` and this message: "Only team members who can edit this help center's design in the dashboard (editors, admins and owners) can read or change it through the API. The account this credential authenticates as no longer has that access on this help center."

## When a scope is missing

A credential without the scope an endpoint needs gets `403 Forbidden` with the body at the top of this page. The `message` always names the missing scope, for API keys and OAuth tokens alike. Retrying won't help:

- With an API key, use a key with the right scope. You can't change the scope of an existing key; create a new one.
- With an OAuth token, send the person through authorization again with the scope added.

Two other errors look similar. `401` means the credential itself isn't valid. `403` with `site_forbidden` means the token doesn't reach the help center you named (see [Choose which help center a request acts on](https://developers.helpcenter.io/content/choosing-a-help-center)). Every code is listed in [Errors](https://developers.helpcenter.io/content/errors).

## AI assistants and their first connection

When an AI assistant connects to the HelpCenter.io MCP server without credentials, the server's sign-in challenge asks for `content.read content.write`, while its metadata lists six API scopes: `content.read`, `content.write`, `analytics.read`, `webhooks.manage`, `notes.read` and `notes.write`. An assistant that asks only for the challenge's scopes gets a token without `analytics.read`, `notes.read` and `notes.write`. Its analytics and team notes tools are then refused with an error that names the missing permission, until the person connects the assistant again and approves it. Which scopes an assistant asks for is up to the assistant. See [Connect an AI assistant to your help center](https://developers.helpcenter.io/content/connect-an-ai-assistant-to-your-help-center).

## Related

- [OAuth 2.1 for apps and AI clients](https://developers.helpcenter.io/content/oauth)
- [API keys](https://developers.helpcenter.io/content/api-keys)
- [Errors](https://developers.helpcenter.io/content/errors)
- [Choose which help center a request acts on](https://developers.helpcenter.io/content/choosing-a-help-center)
