General
The Capacities API uses Bearer authentication (token-based, OAuth 2.0 RFC 6750). Which method to use depends on who you are building for.
Always use HTTPS. Never expose your access token in client-side code or public repositories.
Use a personal token when you are building something for yourself: a local script, a personal automation, a CLI tool, or a private integration that will only ever access your own Capacities account.
Generate a personal token inside the Capacities desktop app:
A personal token grants access to data in the selected space. Do not share it with anyone you do not trust. If you believe your token has been compromised, revoke it immediately and generate a new one.
Personal tokens are prefixed with cap-api-. Pass the full string in the Authorization header:
Authorization: Bearer cap-api-…
Each personal token is tied to one space — there is no spaceId field in request bodies for those routes.
Open Settings > Capacities API and delete the token. Requests using that token immediately receive 401 Unauthorized (cap_not_authenticated).
Use OAuth when you are building an integration, app, or service that other users will connect to their Capacities account. OAuth lets users authorise your integration without sharing their personal credentials with you.
If you are building something other people will install or connect, do not ask users to paste their personal token into your app. Use OAuth instead. For community integrations, we require OAuth authentication (Learn more).
client_idOAuth access is available to verified clients only.
The Capacities OAuth registry is curated. Submit via email to register and include the following:
| Field | Description |
|---|---|
| Integration name | Displayed to users on the consent screen |
| Short description | What your integration does |
| Icon URL | Square image used on the consent screen |
| Homepage URL | Public page for your integration or product |
| Redirect URI(s) | Exact URI(s) your OAuth flow will redirect to |
| Scopes needed | Which of api:read, api:write, offline_access you require |
| About the integration | What you're building and how it uses Capacities data |
| Testing instructions | A link to a staging environment or steps we can follow to verify the integration works |
We will reply with a stable client_id. No client secret is issued.
Fetch server metadata to get all endpoint URLs before hard-coding any of them:
GET https://api.capacities.io/.well-known/oauth-authorization-server
This returns a standard RFC 8414 document listing authorization_endpoint, token_endpoint, scopes_supported, and more.
Request only the permissions your integration actually needs.
| Scope | What it allows |
|---|---|
api:read | Read the user's content (objects, pages, properties, space info) |
api:write | Create and modify content in the user's space |
offline_access | Obtain a refresh token to stay connected without requiring re-authorisation |
code_verifier = 43–128 random URL-safe characters
code_challenge = BASE64URL(SHA-256(code_verifier))
GET https://api.capacities.io/oauth/authorize
?response_type=code
&client_id=<your_client_id>
&redirect_uri=<your_redirect_uri>
&scope=api:read%20api:write%20offline_access
&resource=https://api.capacities.io
&code_challenge=<S256_code_challenge>
&code_challenge_method=S256
&state=<random_state>
The resource parameter is required and must be https://api.capacities.io.
The user logs in (if not already) and selects a Capacities space to authorise. After consent, the browser redirects to your redirect_uri with ?code=…&state=….
POST https://api.capacities.io/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=<authorization_code>
&redirect_uri=<your_redirect_uri>
&client_id=<your_client_id>
&code_verifier=<your_code_verifier>
Do not send an Authorization header. These are public clients with no client secret. Send client_id in the request body only.
Response
{
"access_token": "eyJ…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "…", // present when offline_access was granted
"scope": "api:read api:write offline_access",
}
When an access token expires, use the refresh token to obtain a new one without user interaction:
POST https://api.capacities.io/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=<refresh_token>
&client_id=<your_client_id>
Refresh tokens rotate on every use. Store the new refresh_token from the response immediately; the previous one is revoked.
Refresh tokens expire after 6 months of inactivity or when the user removes the connection from Settings > Connected Apps. You then need to prompt the user to re-authorise.
Pass the access token as a Bearer token on every API request:
Authorization: Bearer eyJ…
Access tokens are short-lived JWTs that expire after 1 hour.
Users can remove a connection at any time from Settings > Connected Apps. Your application should handle a 401 response gracefully and prompt the user to re-authorise.
You can revoke a token programmatically using the POST /oauth/revoke endpoint.
POST /oauth/revoke — RFC 7009
Immediately invalidates a refresh token or access token and removes the connection from the user's Connected Apps. Use this when the user disconnects your integration from within your own UI.
Request
POST https://api.capacities.io/oauth/revoke
Content-Type: application/x-www-form-urlencoded
token=<refresh_token_or_access_token>&client_id=<your_client_id>
| Parameter | Required | Description |
|---|---|---|
token | Yes | The refresh token or access token to revoke |
token_type_hint | No | refresh_token or access_token — helps resolve the token faster |
client_id | Yes | Your OAuth client ID |
Response
Always 200 OK with an empty body, even if the token was already expired or not found (RFC 7009 §2.2 — prevents token enumeration).
Example — revoke a refresh token
curl -X POST https://api.capacities.io/oauth/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=<refresh_token>&token_type_hint=refresh_token&client_id=<your_client_id>"
Revoking a refresh token revokes the entire connection (all associated access tokens become invalid immediately). Revoking only an access token has the same effect — the connection row is removed and future requests with that access token will return 401. The endpoint is also discoverable via the AS metadata at /.well-known/oauth-authorization-server under revocation_endpoint.
Errors follow RFC 6749 §5.2.
error | Likely cause |
|---|---|
invalid_request | Missing or malformed parameter (for example, client_secret sent when not expected) |
invalid_client | client_id unknown or not registered |
invalid_grant | Code already used, expired, or code_verifier mismatch |
invalid_scope | Scope not permitted for your client_id |
invalid_target | resource parameter missing or not https://api.capacities.io |
unauthorized_client | Redirect URI not in your registered allow-list |
Create a ticket on our feedback board. - Let us know if you have an idea for a feature, improvement or think there is something missing.
Request additions to the documentation. - If your questions are not getting answered, let us know and we will extend the documentation.