Authentication

Scopes

Export
Download Markdown Use with AI

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.

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

Articles: GET /v1/articles, GET /v1/articles/{articleId}, GET /v1/articles/{articleId}/staged, GET /v1/articles/{articleId}/versions

Categories: GET /v1/categories

Images: GET /v1/images, GET /v1/images/{imageId}

Comments: GET /v1/comments, GET /v1/articles/{articleId}/comments

Change sets (API keys only): GET /v1/change-sets, GET /v1/change-sets/{changeSetId}, GET /v1/change-sets/{changeSetId}/status

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

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

Categories: POST /v1/categories, PATCH /v1/categories/{categoryId}, DELETE /v1/categories/{categoryId}, PUT /v1/categories/order, PUT /v1/categories/{categoryId}/articles/order

Images: POST /v1/images, POST /v1/images/bulk, POST /v1/images/uploads, DELETE /v1/images/{imageId}

Comments: POST /v1/articles/{articleId}/comments, PATCH /v1/comments/{commentId}, DELETE /v1/comments/{commentId}

Interface translations: POST /v1/translations, POST /v1/translations/{translationKey}

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.

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.

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.

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). Every code is listed in 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.

Was this article helpful?