Connections API
@claxedo/connections lets a host link external accounts (Notion, Atlassian,
GitHub, Google) once and lets features consume them by capability (docs,
work-source, channel, code-host) instead of building their own auth. The
host mounts the routes under /api/claxedo/integrations with
createIntegrationsRoutes(service, gates).
Mount gates
Section titled “Mount gates”The Claxedo host mounts these routes behind authRouteOptions(services) plus a
loopback check (isLoopbackLocalRequest → 403 in unsigned-local mode). The
token and auth-failure endpoints are additionally behind tokenGate, which
enforces loopback unconditionally — even in signed mode — plus the
x-claxedo-connections: 1 header. The header makes browser reads
non-simple/preflighted so the CORS allowlist actually gates them.
Routes
Section titled “Routes”All routes are under /api/claxedo/integrations.
| Method + path | Gates beyond the mount | Response |
|---|---|---|
GET / | — | { integrations: IntegrationDeclaration[], connections: ConnectionSummary[] } |
POST /:id/connect | body { fields, secret, confirmReplace? }, or { method: "oauth", confirmReplace? } with no secret to start an OAuth flow | 200 { ok: true } · 404 { code: "unknown_integration" } · 409 { code: "connection_exists" } · 422 { code: "connection_verify_failed", reason } |
GET /callback | none — deliberately ungated (the provider redirect lands here from the user’s browser; single-use attempt state + TTL are the guards) | HTML landing page, 200 on success / 400 on failure |
GET /attempts/:state | — | 200 attempt status · 404 { code: "attempt_not_found" } |
DELETE /connections/:id | — | 200 / 404 |
POST /connections/:id/reverify | — | 200 { ok, reason? } |
POST /connections/:id/auth-failure | tokenGate: loopback + header x-claxedo-connections: 1 (same gate as the token endpoint) | 204 |
GET /connections/:id/token?capability=<x> | tokenGate: loopback + header x-claxedo-connections: 1 (else 403); capability granted (else 403) | 200 frozen shape · 404 · 409 { code: "connection_not_available", status } · 503 { code: "connections_unavailable" } or { code: "connection_refresh_transient" } |
Token endpoint
Section titled “Token endpoint”GET /connections/:id/token?capability=<x> is how a feature obtains a live
token to call a provider API. Consumers request a token per operation and never
cache it.
capabilitystringrequiredThe capability the caller needs (docs, work-source, channel,
code-host). The connection must have that capability granted, or the route
returns 403.
x-claxedo-connectionsstringrequiredMust be 1. Missing header returns 403 — this is the tokenGate, which also
gates POST /connections/:id/auth-failure and enforces loopback
unconditionally (even in signed mode), on top of the auth mount gates.
The success response is the frozen wire shape { token, tokenType, fields? }:
tokenstringrequiredThe bearer or basic credential material for one call. For OAuth connections this is the access token only — refresh tokens never cross HTTP.
tokenType"bearer" | "basic"requiredHow to present the token. Atlassian, for example, returns
tokenType: "basic".
fieldsobjectOptional extra fields the provider integration needs alongside the token (present only when the integration declares them).
{ "token": "…", "tokenType": "bearer", "fields": { "…": "…" }}Error responses:
404— unknown connection409{ code: "connection_not_available", status }— credential not in an available state503{ code: "connections_unavailable" }— no credential-secret resolver wired503{ code: "connection_refresh_transient" }— transient OAuth refresh failure; nothing is marked errored, retry the call
- A connection is an authenticated link to an external account. One connection per integration per host (an explicit replace flow handles re-linking).
- Capabilities are granted at connect time, derived from what the credential actually covers. Changing grants means reconnecting with broader consent — there is no toggle API.
- Failure semantics: a definitive provider rejection
(
invalid_grant-class) marks the credentialerrorand the connection stops serving tokens until the user reconnects or re-verifies; transient failures change nothing and the next call retries. Refresh is single-flight per credential, per-process.
The package ships reference implementations only — Notion, Atlassian, GitHub
(key-paste + verify), and Google (OAuth). Additional providers belong in host
code, registered via registry.register(decl, impl).
For the storage ports, OAuth attempt machine, and install instructions, see the connections package page.