Developer
Authentication
Atlas accepts two kinds of credential on every /v1 request and on the hosted MCP endpoint: a Personal Access Token for first-party and CI use, or an OAuth 2.0 access token for third-party apps that act on a user's behalf. This guide covers both end to end - mint, use, scope, refresh, introspect, rotate, and revoke - with copyable examples grounded in the real endpoints.
Two kinds of credential
Every authenticated call carries a bearer token in the Authorization header. The header shape is identical for both credential types; only how you obtain and manage the token differs.
Personal Access Token
A long-lived token that carries your own permissions, narrowed to the scopes you pick. Best for scripts, CI, backend jobs, and first-party integrations. Prefixed atlas_pat_.
OAuth 2.0 access token
A short-lived token a user grants to a third-party app through a consent screen. Best when someone else's app needs to act on a user's behalf without ever seeing a password or a PAT. Expires in one hour; refreshed automatically.
Authorization: Bearer atlas_pat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxThe same /v1 API and the same hosted MCP endpoint accept either credential. Scopes, tenant isolation, and rate limits are enforced identically regardless of which one you send.
PAT vs OAuth - when to use which
Both are first-class. The right choice is about who owns the credential and who the caller is acting as.
| Personal Access Token | OAuth 2.0 | |
|---|---|---|
| Acts as | You (the token owner). | The user who granted consent. |
| Best for | CI, cron, backend jobs, your own scripts, single-tenant tools. | Third-party apps, marketplace integrations, anything you ship to other people. |
| Lifetime | Long-lived until you revoke or it hits its optional expiry. | Access token 1 hour; refresh token 30 days, rotated on every use. |
| Obtained via | Settings, then API access. | A registered app + the authorization-code (or device) flow. |
| Secret handling | One token string. Treat it like a password. | A client_id (+ client_secret for confidential apps) plus per-user tokens. |
| Revocation | Revoke the token in Settings. | Revoke a single token, or revoke the whole app to cut off every user at once. |
Rule of thumb
Personal Access Tokens
A PAT is the shortest path from nothing to an authenticated request. It carries your permissions, narrowed to the scopes you grant, and is revocable independently of your login session.
Mint a token
Open Settings, then API access, and click New token. Give it a name, pick the narrowest scopes the job needs (start withtasks:read), and optionally set an expiry. The token is shown once and starts withatlas_pat_- copy it now and store it in a secret manager.Send it as a bearer header
Pass the token verbatim on every request. No exchange, no refresh - a PAT is ready to use the moment it is minted.curl -H "Authorization: Bearer atlas_pat_REPLACE_ME" \ https://api-atlas.wrxstack.com/v1/tasks?limit=50Rotate or revoke any time
The same page lists every token with its last-used time. Rotate issues a fresh secret and invalidates the old one; Revoke kills it immediately. See Rotate and revoke below.
Scopes are enforced per request
403 (insufficient_scope) if the token is missing the scope a route requires. The problem body lists requiredScopes and grantedScopes so you know exactly what to add. See the 403 example in the REST guide.Scopes
Scopes are a coarse resource:action split - read, write, sometimes delete - per resource. The same vocabulary applies to PATs and to OAuth apps, so a token or an app only ever gets what you check. Grant the least you need.
ai
| Scope | Grants |
|---|---|
ai:read | Use Ask Atlas, semantic search, and other read-only AI helpers. |
ai:write | Natural-language capture creating tasks, AI rescheduling, etc. |
appointments
| Scope | Grants |
|---|---|
appointments:read | List confirmed bookings, view appointment detail, subscribe to the appointments ICS feed. |
appointments:write | Reschedule, cancel, mark no-show, send reminders, and edit private host notes. |
automations
| Scope | Grants |
|---|---|
automations:read | View automation rules, run history, versions, and the workflow-template catalog. |
automations:write | Create, update, delete, enable/disable, run, and revert automations; install templates. |
billing
| Scope | Grants |
|---|---|
billing:read | View subscription, invoices, and plan/entitlement status. |
billing:write | Start checkout, change plan, add seats, and cancel - for workspace admins. |
calendar
| Scope | Grants |
|---|---|
calendar:read | View calendar events, availability, booking metadata, and conflicts. |
calendar:write | Create, reschedule, cancel, and update calendar-backed bookings. |
chat
| Scope | Grants |
|---|---|
chat:read | View chat channels, members, and message history. |
chat:write | Create channels/DMs, post/edit/delete messages, and manage members and reactions. |
comments
| Scope | Grants |
|---|---|
comments:read | View comments and discussion threads on work items. |
comments:write | Create and update comments on work items. |
developer
| Scope | Grants |
|---|---|
developer:manage | Register OAuth apps, manage API keys, and view API usage and rate limits. |
diagrams
| Scope | Grants |
|---|---|
diagrams:read | List, view, and export diagrams, templates, and shape libraries. |
diagrams:write | Create/update diagrams, templates, comments, bindings, and share links. |
diagrams:delete | Archive or delete diagrams, comments, bindings, and share links. |
forms
| Scope | Grants |
|---|---|
forms:read | List forms and export their submissions. |
forms:write | Create, update, and delete forms and manage task approval requests. |
goals
| Scope | Grants |
|---|---|
goals:read | View Objectives and Key Results, including linked projects. |
goals:write | Create and update Objectives and Key Results. |
goals:delete | Permanently remove Objectives and Key Results. |
growth
| Scope | Grants |
|---|---|
growth:read | View your referral summary and NPS survey eligibility. |
habits
| Scope | Grants |
|---|---|
habits:read | View your habits, streak statistics, and at-risk habits. |
habits:write | Create, update, archive, and check in/out of habits. |
huddles
| Scope | Grants |
|---|---|
huddles:read | List and view lightweight audio/video huddles and their rosters. |
huddles:write | Start, join, leave, update state for, and end huddles. |
inbox
| Scope | Grants |
|---|---|
inbox:read | List your notifications and read the unread count. |
inbox:write | Mark notifications read/unread and mark all as read. |
intelligence
| Scope | Grants |
|---|---|
intelligence:read | Semantic search, recommendations, and summaries via the local intelligence layer. |
intelligence:write | Natural-language capture and generation via the local intelligence layer. |
pats
| Scope | Grants |
|---|---|
pats:manage | List, mint, and revoke other access tokens. Sensitive - grant sparingly. |
| Scope | Grants |
|---|---|
pdf:read | List and download PDF documents and jobs, read annotations, and run read-only analysis such as comparison, signature verification, and text extraction. |
pdf:write | Convert, split, extract pages, stamp, sign, encrypt, optimise, and fill PDF forms, and create or change documents, versions, jobs, and annotations. |
profile
| Scope | Grants |
|---|---|
profile:read | Read your name, email, timezone, locale, and notification config. |
profile:write | Update your profile details, preferences, and public profile fields. |
projects
| Scope | Grants |
|---|---|
projects:read | List and view your projects (no task data on its own). |
projects:write | Add new projects, rename, or reorganise existing ones. |
reports
| Scope | Grants |
|---|---|
reports:read | List, run, and export report and dashboard definitions (JSON/CSV). |
search
| Scope | Grants |
|---|---|
search:read | Run global, unified, and cross-tool searches across content you can access. |
tasks
| Scope | Grants |
|---|---|
tasks:read | List, view, and search your tasks across all projects. |
tasks:write | Add new tasks, change status, priority, schedule, or assignee. |
tasks:delete | Move tasks to the archive or permanently delete them. |
time
| Scope | Grants |
|---|---|
time:read | List your time entries and view the currently running timer. |
time:write | Create, start, stop, and update your time entries. |
time:delete | Permanently remove your time entries. |
webhooks
| Scope | Grants |
|---|---|
webhooks:manage | Create, update, replay, and delete outbound webhook subscriptions. |
whiteboards
| Scope | Grants |
|---|---|
whiteboards:read | List and view whiteboards and their contents. |
whiteboards:write | Create whiteboards and edit what is on them. |
whiteboards:delete | Permanently remove whiteboards. |
workload
| Scope | Grants |
|---|---|
workload:read | View workload allocation, overallocations, leveling proposals, and capacity profiles. |
workload:write | Create or replace per-user capacity profiles used by workload math. |
OAuth 2.0
Atlas is a full OAuth 2.1 authorization server. Third-party apps register once, then run the authorization-code flow (with PKCE) so a user can grant scoped access without sharing a password or a PAT. Access tokens are opaque bearer tokens that expire in one hour and refresh silently.
Everything is discoverable
/.well-known/oauth-authorization-server and let your OAuth library configure itself. The endpoints below are shown for clarity; the discovery document is the source of truth.1. Register an app
Open Settings, then Developer and click Register OAuth app. Provide a name, one or more redirect URIs (exact-match HTTPS callbacks), and the scopes the app may request. Choose the client type:
| Client type | Credential | Use when |
|---|---|---|
| Confidential | client_id + client_secret (cs_...) | Your app has a backend that can keep a secret (server-side web app, API). |
| Public (PKCE only) | client_id (cli_...), no secret | The code runs where a secret cannot hide (SPA, mobile, CLI, desktop). PKCE is required. |
The secret is shown once
client_secret is displayed only at registration and after each rotation. Store it in a secret manager. If it leaks, rotate it from the same Developer page - see Rotate and revoke.2. Discover the endpoints
Atlas publishes two RFC discovery documents. The first (RFC 8414) tells a client where the authorization server lives; the second (RFC 9728) tells an MCP client which authorization server protects the hosted /mcp resource.
curl https://api-atlas.wrxstack.com/.well-known/oauth-authorization-server{
"issuer": "https://api-atlas.wrxstack.com",
"authorization_endpoint": "https://atlas.wrxstack.com/oauth/authorize",
"token_endpoint": "https://api-atlas.wrxstack.com/v2/oauth/token",
"userinfo_endpoint": "https://api-atlas.wrxstack.com/v2/oauth/userinfo",
"jwks_uri": "https://api-atlas.wrxstack.com/.well-known/jwks.json",
"registration_endpoint": "https://api-atlas.wrxstack.com/v2/oauth/register",
"revocation_endpoint": "https://api-atlas.wrxstack.com/v2/oauth/revoke",
"introspection_endpoint": "https://api-atlas.wrxstack.com/v2/oauth/introspect",
"device_authorization_endpoint": "https://api-atlas.wrxstack.com/v2/oauth/device_authorization",
"scopes_supported": ["tasks:read", "tasks:write", "projects:read", "..."],
"response_types_supported": ["code"],
"grant_types_supported": [
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:device_code"
],
"token_endpoint_auth_methods_supported": ["none", "client_secret_post"],
"code_challenge_methods_supported": ["S256"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"]
}The protected-resource document points MCP clients at the authorization server that guards the hosted /mcp endpoint:
curl https://api-atlas.wrxstack.com/.well-known/oauth-protected-resource{
"resource": "https://api-atlas.wrxstack.com/mcp",
"authorization_servers": ["https://api-atlas.wrxstack.com"],
"scopes_supported": ["tasks:read", "tasks:write", "..."],
"bearer_methods_supported": ["header"],
"resource_name": "Atlas MCP",
"resource_documentation": "https://atlas.wrxstack.com/docs/mcp"
}Endpoint reference
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /.well-known/oauth-authorization-server | Authorization-server metadata (RFC 8414). |
| GET | /.well-known/oauth-protected-resource | Protected-resource metadata for /mcp (RFC 9728). |
| GET | https://atlas.wrxstack.com/oauth/authorize | Browser consent page (on the web origin). Users log in and approve scopes here. |
| POST | /v2/oauth/token | Exchange a code, refresh, or device_code for tokens. |
| POST | /v2/oauth/introspect | Check whether a token is active (RFC 7662). |
| POST | /v2/oauth/revoke | Revoke an access or refresh token (RFC 7009). |
| POST | /v2/oauth/device_authorization | Start the device-code flow (RFC 8628). |
| GET | /v2/oauth/userinfo | Read the subject + granted scope for a bearer access token. |
| GET | /.well-known/jwks.json | Signing keys for id_token verification (RS256). |
MCP clients discover the same thing
/mcp endpoint without a token, Atlas answers 401 with a WWW-Authenticate header that points at /.well-known/oauth-protected-resource. The client reads it, finds this authorization server, and runs the flow below automatically. See the MCP connection guide.3. Authorization code flow (with PKCE)
This is the flow for apps a user signs in to. PKCE (Proof Key for Code Exchange, S256) is required for public clients and recommended for everyone - it stops a stolen authorization code from being redeemed by anyone but you.
Create a PKCE verifier and challenge (public clients)
Generate a high-entropycode_verifier, hash it with SHA-256, and base64url-encode the result into acode_challenge. Keep the verifier; send only the challenge.# 1. code_verifier: 43-128 URL-safe chars kept secret in your app code_verifier=$(openssl rand -base64 96 | tr -d '\n=+/' | cut -c1-96) # 2. code_challenge: base64url( SHA-256( code_verifier ) ) code_challenge=$(printf '%s' "$code_verifier" \ | openssl dgst -binary -sha256 \ | openssl base64 | tr '+/' '-_' | tr -d '=\n') echo "verifier: $code_verifier" echo "challenge: $code_challenge"Send the user to the authorization page
Redirect the user's browser to the authorization endpoint on the web origin. They sign in to Atlas (if not already), see exactly which scopes you asked for, and approve or deny.stateis an opaque anti-CSRF value you generate and verify on the way back.texthttps://atlas.wrxstack.com/oauth/authorize ?client_id=cli_YOUR_CLIENT_ID &redirect_uri=https://yourapp.example/callback &scope=tasks:read%20tasks:write &state=RANDOM_ANTI_CSRF_VALUE &code_challenge=THE_CODE_CHALLENGE &code_challenge_method=S256Receive the code on your redirect URI
On approval, Atlas redirects the browser back to your registeredredirect_uriwith a single-usecodeand yourstate. Reject the response ifstatedoes not match what you sent. A denial returns?error=access_deniedinstead.texthttps://yourapp.example/callback ?code=ac_1a2b3c4d5e6f... &state=RANDOM_ANTI_CSRF_VALUEExchange the code for tokens
POST the code to the token endpoint. Public clients send thecode_verifier; confidential clients send theclient_secret. The code is single-use and expires in five minutes.curl -X POST https://api-atlas.wrxstack.com/v2/oauth/token \ -H "Content-Type: application/json" \ -d '{ "grant_type": "authorization_code", "code": "ac_1a2b3c4d5e6f...", "redirect_uri": "https://yourapp.example/callback", "client_id": "cli_YOUR_CLIENT_ID", "code_verifier": "THE_ORIGINAL_CODE_VERIFIER" }'The token endpoint returns an access token, its lifetime in seconds, a refresh token, and the granted scope:
httpHTTP/1.1 200 OK Content-Type: application/json { "access_token": "at_9f8e7d6c5b4a...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "rt_0a1b2c3d4e5f...", "scope": "tasks:read tasks:write" }
4. Call the API
The access token is an opaque bearer token. Send it exactly like a PAT - the /v1 API does not care which credential type it is.
curl -H "Authorization: Bearer at_9f8e7d6c5b4a..." \
https://api-atlas.wrxstack.com/v1/tasks?limit=505. Refresh before it expires
Access tokens live one hour. When yours nears expiry (or a call returns 401), exchange the refresh token for a new pair. Refresh tokens rotate: each refresh returns a brand-new refresh token and invalidates the one you sent. Store the new one.
curl -X POST https://api-atlas.wrxstack.com/v2/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "refresh_token",
"refresh_token": "rt_0a1b2c3d4e5f...",
"client_id": "cli_YOUR_CLIENT_ID",
"client_secret": "cs_YOUR_CLIENT_SECRET"
}'Reuse detection
Introspect a token
A resource server can ask Atlas whether a token is still active and what it carries (RFC 7662). An inactive token returns simply { "active": false } and nothing else.
curl -X POST https://api-atlas.wrxstack.com/v2/oauth/introspect \
-H "Content-Type: application/json" \
-d '{
"token": "at_9f8e7d6c5b4a...",
"client_id": "cli_YOUR_CLIENT_ID",
"client_secret": "cs_YOUR_CLIENT_SECRET"
}'{
"active": true,
"scope": "tasks:read tasks:write",
"client_id": "cli_YOUR_CLIENT_ID",
"token_type": "Bearer",
"sub": "usr_01HW3RA2B3C4D5E6F7G8H9J0K1",
"aud": "https://api-atlas.wrxstack.com/mcp",
"exp": 1746123456,
"iat": 1746119856,
"jti": "jti_7c9e2f14..."
}Revoke a token
Revoke an access or refresh token (RFC 7009) at logout or when a user disconnects your app. Revoking a refresh token also revokes the access tokens minted from it.
curl -X POST https://api-atlas.wrxstack.com/v2/oauth/revoke \
-H "Content-Type: application/json" \
-d '{
"token": "rt_0a1b2c3d4e5f...",
"token_type_hint": "refresh_token",
"client_id": "cli_YOUR_CLIENT_ID",
"client_secret": "cs_YOUR_CLIENT_SECRET"
}'Device code flow
For inputs-constrained devices (a CLI, a TV, an IoT box) that cannot host a browser redirect, use the device-code grant (RFC 8628). Start it, show the user the user_code and verification_uri, then poll the token endpoint until they approve.
curl -X POST https://api-atlas.wrxstack.com/v2/oauth/device_authorization \
-H "Content-Type: application/json" \
-d '{ "client_id": "cli_YOUR_CLIENT_ID", "scope": "tasks:read" }'{
"device_code": "dc_1a2b3c...",
"user_code": "WDJB-MJHT",
"verification_uri": "https://atlas.wrxstack.com/device",
"verification_uri_complete": "https://atlas.wrxstack.com/device?user_code=WDJB-MJHT",
"expires_in": 600,
"interval": 5
}Poll every interval seconds. Until the user approves you get authorization_pending; once they do, the same call returns the token pair.
curl -X POST https://api-atlas.wrxstack.com/v2/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"device_code": "dc_1a2b3c...",
"client_id": "cli_YOUR_CLIENT_ID"
}'Rotate and revoke credentials
Treat every credential as rotatable. Rotate on a schedule, rotate immediately on any suspected leak, and always prefer the least-privilege scope set. Here is every lifecycle path Atlas gives you.
Rotate or revoke a Personal Access Token
- Open Settings, then API access. Each token shows its name, scopes, and last-used time.
- Click Rotate to mint a replacement secret for the same token. The new value is shown once; update your secret store, then the old value stops working.
- Click Revoke (the trash action) to kill a token immediately. Every caller using it - including any MCP agent - loses access at once.
Rotate an OAuth app's client secret
- Open Settings, then Developer and find the app in the OAuth applications table.
- Click Rotate secret. Atlas issues a new
cs_...secret and invalidates the previous one. Theclient_idis unchanged, so already-issued user tokens keep working - only new token exchanges need the new secret. - Deploy the new secret to your backend, then confirm sign-ins still succeed.
Revoke an entire OAuth app
On the same Developer page, Revoke an app to deactivate its client_id and invalidate every access and refresh token issued to every user for that app in one action. Use this when an app is decommissioned or a client secret is known to be compromised and you want a hard cutoff.
Revoke a single OAuth token
To disconnect one user (a logout, an account unlink) without touching anyone else, call POST /v2/oauth/revoke with that user's refresh token - see Revoke a token. This is the RFC 7009 path and is the correct programmatic revocation for a per-user session.
If a credential leaks
Best practices
The short list that keeps an integration secure and boring.
- Least privilege. Grant only the scopes a token or app actually uses. Start read-only and widen when a call needs it.
- Rotate on a schedule. Rotate PATs and client secrets periodically, not just after an incident. Automate it where you can.
- Never embed a secret in a public client. A SPA, mobile, or CLI app is a public client - use PKCE, no
client_secret. - Prefer OAuth for third parties, PAT for first-party. If someone else installs your app, they should sign in with OAuth, never paste your PAT.
- Store the latest refresh token. Refresh tokens rotate; persisting the newest one avoids a reuse-detection lockout.
- Verify
stateand validate redirect URIs. Always check the returnedstate, and register exact-match HTTPS redirect URIs only.