Skip to main content

Personal API key

Create a key under Settings → Access. Default scope is full_access. Send it as a Bearer token:
Keys act as the workspace profile that owns them. Treat them like passwords; revoke compromised keys from Settings. Workspace / metric ingest API keys (metric_data_only) are removed — use a personal key instead.

OAuth user token

Third-party OAuth apps receive sl_oat_... access tokens after the authorization-code flow (user actor). Send them the same way:

OAuth app actor

Install an app as a machine identity (Linear-style) with actor=app on authorize, or mint a token with grant_type=client_credentials when the app has client credentials enabled.
  • Authorize: workspace admin only; POST /api/v1/auth/oauth/authorize with actor=app. Creates an app-scoped token with profileId: null.
  • Client credentials: POST /api/v1/auth/oauth/token with grant_type=client_credentials, client_id, and client_secret. TTL ≈ 30 days. Changing scopes or rotating the secret revokes outstanding machine tokens.
  • App actors run as a synthetic member (writer) profile oauth-app:{appId} — enough for work-graph reads/writes, not Settings/admin powers.
  • An admin OAuth scope is not grantable to app actors.
Use the returned access_token (sl_oat_...) like any other Bearer credential. The TypeScript SDK accepts it via accessToken.

Resource scopes

Coarse scopes remain for back-compat. Resource scopes narrow access per domain. Supersets Missing a required resource scope returns 401 with error.type: INSUFFICIENT_SCOPE.

App session (not for integrations)

The Sayless web app uses cookie session JWTs. That path is for the official client only (including sync). Integrations should use API keys or OAuth — not scraped cookies.

Errors

API key and OAuth failures return a nested envelope:
Branch on error.type (including INSUFFICIENT_SCOPE). See Errors and Rate limits.