Use scoped API Keys

API Keys let us integrate Discourse with other systems or automate different actions. However, great power comes with great responsibility. If a malicious actor has access to an API Key, they could have access to sensitive data or make changes to your site. To mitigate this and add an extra layer of security, you can now limit what an API key can do using scopes.

Scopes and allowed parameters

When creating a new API Key, you can select which scopes you want. If you hover over the :grey_question: icon, you’ll see a brief description of what it does. The :link: button will show you the URLs associated with that scope.

Optionally, you could also specify which parameters are accepted. Use commas to separate multiple values.

Migrating existing keys

When we added this feature, existing keys turned into “global” keys. To migrate them to scopes, you’ll have to revoke them and create new ones.

Adding custom scopes

Plugins can add custom scopes using by calling the add_api_key_scope method:

resource is a symbol used to group related scopes, while action is a hash which the following attributes:

  • action: A list containing the allowed controller actions. The format is controller_name#method_name.
  • params: A list containing the name of the allowed parameters.
  • aliases: A hash containing a different name for an allowed parameter.

If you want to know how the base scopes are defined see:


It seems I get 403 response for getting categories with a read only API key

curl -X GET "https://mysite/categories.json" \
-H "Api-Key: mykey" \
-H "Api-Username: system"

where I have read & read_list permissions on topics for the API key. /top.json works with the same key for instance. The endpoint works when I use a ‘Global Key’

I’d like to put the API key in my client for reading the list of categories and topics, so important to have a read only key!

Any pointers?

1 Like

Interestingly it works if no credentials are used at all!

i.e. curl -X GET "https://mysite/categories.json"

1 Like

Unfortunately, we only include a handful of scopes out of the box due to the high number of available endpoints. We may add new ones in the future, but in the meantime, you’ll have to extend them to suit your needs.

If you use a scoped API Key, calling endpoints not included by the scopes you choose won’t work. You can click the :link: button to see which URLs are accepted.

This only works for public sites. Also, you won’t see things like private categories unless you are logged in and have enough permissions.


Are you taking suggestions for further scopes?

One thing I’d be interested in using our site’s API for would be for another site to check if a user exists with an email address and add to a group if so. I trust the site (another site within our parent organisation) but tightening down access options to only what’s needed seems wise if it were an option.

1 Like

I’m super happy that they have opened up an api for plugins. You can start with a plugin and see if it can be incorporated in core.


I believe that #pr-welcome for any generally useful scopes you can come up with. E.g. group_membership.edit ?