/api/public/v1 must be authenticated with an API key in the Authorization: Bearer header. This page explains how to mint a key, which scopes to request, how to store the secret, and how to rotate when needed.
How keys work
- A key is a long random string (opaque to callers).
- It is associated with a scope list that controls which endpoints it can reach.
- The server checks the scope on every request. A key without the right scope gets HTTP 403.
- Keys can optionally have an expiry (1–365 days). Keys without an expiry never expire until revoked.
The platform:adapter scope
Integration keys use the scope
platform:adapter. This scope is distinct from user-grantable scopes. Only community owners with platform_admin status can mint platform:adapter keys. The platform.opennotes.ai dashboard handles this automatically for registered communities.platform:adapter unlocks:
POST /api/public/v1/requests— submit content for reviewGET /api/public/v1/requests/GET /api/public/v1/requests/{id}— retrieve request statusPATCH /api/public/v1/requests/{id}— update request metadataPOST /api/public/v1/moderation-actions— record integration-side moderation actionsGET /api/public/v1/notesand note sub-resources — read community notesPOST /api/public/v1/ratings— submit ratingsGET /api/public/v1/community-servers/lookup— resolve instance IDsGET /api/public/v1/user-profiles/lookup— look up profiles
platform:adapter, the dashboard’s Discourse Plugin scope template automatically selects: requests:read, requests:write, notes:read, notes:write, ratings:write, profiles:read, community-servers:read, moderation-actions:read.
Minting a key
Go to platform.opennotes.ai and sign in. The API Keys dashboard opens automatically.
A form appears. Give the key a descriptive name, e.g.
Production Discourse or Staging Discord. Good names make it easier to audit and rotate later.Click Discourse Plugin to pre-select the recommended scope set for a Discourse integration. You can also click individual scopes to customize.
Leave blank for a non-expiring key, or enter a number of days (max 365). For service accounts we recommend 365-day expiry with a rotation reminder.
After clicking Create, the dashboard shows the full key value once. Copy it now into your secret manager.
Storing the secret
Store your key in a secrets manager, not in source control or config files:| Environment | Recommended storage |
|---|---|
| GCP Cloud Run | GCP Secret Manager |
| Docker Compose / local | .env file outside the repo (git-ignored) |
| Discourse | Site settings (stored in encrypted PostgreSQL column) |
| GitHub Actions CI | Repository or organization secret |
Using the key in requests
Pass the key in theAuthorization header:
Rotation procedure
Update the secret in every environment that uses the old key. For Discourse, update the
opennotes_api_key site setting. Restart any services that load the key at startup.Error responses
| HTTP status | Cause |
|---|---|
| 401 | Missing Authorization header or invalid key |
| 403 | Key exists but lacks the required scope |