Skip to main content

userConnectRoutes()

Returns API routes that let your application's authenticated users connect their own provider accounts as end-user connections. Spread the result into the server's apiRoutes:

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { userConnectRoutes } from '@mastra/connect'

export const mastra = new Mastra({
server: {
auth: {
authenticateToken: async token => verifyToken(token),
mapUserToResourceId: user => user.id,
},
apiRoutes: [...userConnectRoutes({ providers: ['linear', 'slack'] })],
},
})

Returns: ApiRoute[] for the server's apiRoutes option.

The routes do no authentication of their own: the server's auth pipeline authenticates each request, and mapUserToResourceId establishes the resource ID the routes are gated on. A request without a resolvable resource ID is rejected with a 401 on every route. All platform calls happen server-side with your platform token. The browser only ever talks to these routes.

Parameters
Direct link to Parameters

providers:

string[]
Required allowlist of provider IDs exposed to end users. The end-user surface never defaults open: providers outside this set return 404 on every route.

resourceId?:

(ctx: { requestContext?, user? }) => string | undefined | Promise<string | undefined>
Derives the caller's resource ID per request, overriding the default. Defaults to the server-derived resource ID that mapUserToResourceId sets after authentication. A request where the callback returns a falsy value or throws is rejected with 401.

basePath?:

string
Mount prefix for every route. Default '/connect'.

projectId?:

string
Platform project the routes act on. Falls back to MASTRA_PROJECT_ID.

client?:

ConnectClientOptions
Platform client overrides. See ConnectClientOptions in tools(). client.resourceId is rejected: the routes scope each request to the calling user.

returnTo?:

{ allowedOrigins?: string[] }
Origins (for example https://app.example.com) allowed as absolute returnTo redirect targets on the hosted page. Relative paths are always allowed; all other values are rejected.

Routes
Direct link to Routes

All routes are gated on the caller's resource ID and respond with Cache-Control: no-store. Paths below use the default /connect base path.

RouteDescription
GET /connect/providersThe platform catalog filtered to the allowlist: { providers: [{ id, displayName, logoUrl, authType, authFields }] }.
GET /connect/connectionsGlobal connections plus the caller's own, filtered to the allowlist: { connections: [{ id, integrationId, status, accountLabel, scope }] } where scope is 'global' or 'user'.
POST /connect/:provider/sessionsCreates a connect session owned by the caller: { connectionId, integrationId, connectUrl, sessionToken, expiresAt }. Returns 404 for providers outside the allowlist.
GET /connect/connections/:id/statusPoll target for a visible (global or own) connection: { id, status, scope }. An invisible connection returns the same 404 as an unknown ID.
DELETE /connect/connections/:idDisconnects the caller's own connection (204). Global connections return 403.
GET /connect/:provider?returnTo=...Hosted connect page: a Connect button opens the provider's authorization flow in a popup, polls the status route, and redirects to the validated returnTo with connectionId and status query parameters. Loaded by top-level navigation, so it requires cookie-based auth; with token-only auth it returns 401 and the JSON routes are the alternative.

Error responses
Direct link to Error responses

Errors are JSON: { error: { code, message } }. Platform failure details never reach the end user.

StatusCodeWhen
401unauthorizedNo resolvable resource ID for the request
400invalid_return_toreturnTo isn't a relative path or an allowlisted origin
403forbiddenDeleting a global connection
404provider_not_found, connection_not_foundProvider outside the allowlist or an invisible connection
500misconfiguredMissing platform token or other configuration problem
502platform_errorThe platform request failed

Configuration problems detectable up front (an empty providers allowlist, a missing project ID, an invalid basePath, client.resourceId) throw a MastraConnectError from userConnectRoutes() itself.

On this page