This is the catalog for the App integrations surface. For an end-to-end walkthrough, see the Tutorial.
Supported providers and scopes
| Provider | Scopes | Picker | Access token TTL |
|---|---|---|---|
Always openid email profile; optional per connection: Gmail (send), Calendar, Sheets, Docs, Contacts, Drive (per-file drive.file) | Yes | ~1 hour | |
| GitHub | Fixed: repo, user, read:org | No | Long-lived (auto-revoked after ~1 year idle) |
| Spotify | Fixed: profile + email, playlist read/modify (public & private), playback read/control, recently-played, top tracks | No | ~1 hour |
Google omits broad Drive scopes (drive, drive.readonly) on purpose — only the per-file drive.file scope is offered, so requesting a broad Drive scope is rejected.
Refresh tokens are stored encrypted at rest (AES-256-GCM). The plaintext is never logged, never returned via the API, and never seen by Agents.
API endpoints
GET /api/apps/providers
Returns the provider registry (env vars, tool/skill install commands, example commands). Used by the on-VM CLI to discover available providers and their scope catalogs. Dual auth (Clerk or HMAC). Strips OAuth URLs, secrets, and dashboard-only fields. Cache-Control: private, max-age=300.
Response 200 (one entry per provider):
[
{
"provider": "google",
"displayName": "Google Workspace",
"description": "Gmail, Calendar, Sheets, Docs, Contacts",
"envVar": "GOG_ACCESS_TOKEN",
"accountEnvVar": "GOG_ACCOUNT",
"tool": { "name": "gog", "installCmd": "…", "checkCmd": "command -v gog" },
"exampleCommand": "gog gmail search 'newer_than:7d' --max 10",
"tokenLifetime": "~1 hour",
"scopeLabels": {
"https://www.googleapis.com/auth/gmail.send": "Gmail (send only)",
"https://www.googleapis.com/auth/calendar": "Google Calendar"
}
}
]
GET /api/apps/authorize
Start the OAuth flow. Clerk-only (all flows originate from the dashboard popup). Query params: provider=google|github|spotify, scopes (comma-separated), accountId (optional, for org context). Generates a one-time state nonce + PKCE challenge and 302-redirects to the provider's consent screen — there is no JSON response body.
GET /api/apps/callback
OAuth callback. No Clerk auth — the one-time state nonce in the query string is the auth (single-use). CrabGlamp exchanges the code using the stored PKCE verifier, reads your email and scopes, saves the connection, and returns a popup-closing page that signals the dashboard to refresh.
GET /api/apps/[id]/token
Fetch a fresh access token for the connection. CrabGlamp returns the cached token if it is still valid, or refreshes it against the provider first (10-second timeout). Concurrent calls for the same connection are serialized so they can't race. Returns:
{
"accessToken": "ya29.…",
"expiresAt": "2026-05-15T13:00:00Z",
"provider": "google",
"providerSubject": "…",
"providerAccountId": "…",
"providerAccountName": "you@example.com",
"scopes": ["openid", "email", "profile"]
}
Status codes:
200— fresh token returned.410 Gone— body{ "error": "connection_error" }. The refresh token is dead; the connection is markederror. Re-authorize from the dashboard.503— body{ "error": "refresh_unavailable" }. A transient provider error or a platform-side OAuth misconfiguration; the connection staysactiveso the next request can retry.
DELETE /api/apps/[id]
Revoke the connection. CrabGlamp best-effort revokes the refresh token at the provider, then removes the connection.
CLI
On-VM CLI for App integrations:
| Command | Purpose |
|---|---|
crabglamp apps providers | List available providers and their scope catalogs |
crabglamp apps <provider> | Configure a connected provider on this Agent — writes the access token to its env var (e.g. $GOG_ACCESS_TOKEN) |
crabglamp apps refresh <provider> | Refresh the access token for a provider and update its env var |
crabglamp apps list | List configured connections on this Agent |
Limits
| Limit | Value |
|---|---|
| Refresh fetch timeout | 10 seconds |
| Authorization window | 10 minutes |
| Encryption | AES-256-GCM at rest |