Personal API key
Create a key under Settings → Access. Default scope isfull_access. Send it as a Bearer token:
metric_data_only) are removed — use a personal key instead.
OAuth user token
Third-party OAuth apps receivesl_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) withactor=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/authorizewithactor=app. Creates an app-scoped token withprofileId: null. - Client credentials:
POST /api/v1/auth/oauth/tokenwithgrant_type=client_credentials,client_id, andclient_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
adminOAuth scope is not grantable to app actors.
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:error.type (including INSUFFICIENT_SCOPE). See Errors and Rate limits.