SDK Reference
@vercel/connect is the TypeScript SDK for calling Vercel Connect from your app. It authenticates with your Vercel deployment's OIDC token and exchanges it for provider tokens through the Vercel API. SDK methods that make API requests are subject to rate limits.
pnpm add @vercel/connectThe SDK has one runtime dependency, @vercel/oidc, which reads VERCEL_OIDC_TOKEN from the environment.
In a Vercel deployment, the SDK reads VERCEL_OIDC_TOKEN automatically. For local development, run vercel link followed by vercel env pull to download a development token into .env.local. The token is short-lived; re-run vercel env pull if you see authentication errors.
To override the token explicitly, pass options.vercelToken:
import { getToken } from '@vercel/connect';
const token = await getToken(
'slack/acme-slack',
{ subject: { type: 'app' } },
{ vercelToken: process.env.MY_VERCEL_TOKEN },
);See Authentication for the full caller-to-Vercel-Connect story.
Returns the access token string. Use when you just need the token to put in an Authorization header.
function getToken(
connector: string,
params: ConnectTokenParams,
options?: ConnectOptions,
): Promise<string>;Minimal example:
import { getToken } from '@vercel/connect';
const token = await getToken('slack/acme-slack', {
subject: { type: 'app' },
scopes: ['chat:write'],
});
await fetch('https://slack.com/api/chat.postMessage', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ channel: 'C123', text: 'hello' }),
});This omits installationId and falls back to the connector's default installation, which is the predominant case (a private app installed in a single workspace). Pass installationId explicitly only for multi-tenant connectors where you need to address a specific installation. See Installations.
Returns the full response: the token, its expiry, the connector identity, and provider metadata. Use when your app needs more than the raw string, for example to display the connected workspace name or to decide whether to surface a re-authorize button.
function getTokenResponse(
connector: string,
params: ConnectTokenParams,
options?: ConnectOptions,
): Promise<ConnectTokenResponse>;Minimal example:
import { getTokenResponse } from '@vercel/connect';
const response = await getTokenResponse('slack/acme-slack', {
subject: { type: 'app' },
});
console.log(response.connector.uid);
console.log(response.tenantId);
console.log(new Date(response.expiresAt).toISOString());Returns the connector's stable public metadata and provider-specific configuration. Use it when your app needs connector setup values in addition to a runtime token, such as a Snowflake account identifier.
function getConnectorMetadata(
connector: string,
options?: ConnectOptions,
): Promise<ConnectorMetadata>;import { getConnectorMetadata } from '@vercel/connect';
const connector = await getConnectorMetadata('snowflake/analytics');
const accountIdentifier = connector.vendor.accountIdentifier as string;
console.log(connector.name);
console.log(accountIdentifier);The calling project and environment must be linked to the connector, just as they must be for getToken.
| Field | Type | Required | Description |
|---|---|---|---|
subject | ConnectTokenSubject (see below) | yes | Who the token represents. See Tokens. |
installationId | string | no | Which tenant the token is for. Pass '*' for a cross-installation token where the connector supports it. See Installations. |
audience | string[] | no | Provider audience claim. Used when the provider requires a specific audience in the issued token. |
scopes | string[] | no | Provider scope strings (chat:write, repo:read, etc.). Pass ['*'] to request the connector's default scopes for the selected subject type. |
resources | string[] | no | Resource indicators that narrow the token to a specific provider resource (channel, repo, record set). SDK-only. |
authorizationDetails | Array<{ type: string } & Record<string, unknown>> | no | Rich authorization requests, when the provider supports them. SDK-only. |
validityBufferMs | number | no | Refresh the token if it expires within this many milliseconds. Defaults to 30000 (30 seconds). |
A discriminated union with three variants:
type ConnectTokenSubject =
| { type: 'app' }
| { type: 'user'; id: string; issuer?: string }
| {
type: 'jwt-bearer';
sub: string;
iss?: string;
aud?: string;
additionalClaims?: Record<string, unknown>;
};| Variant | Required fields | Optional fields |
|---|---|---|
app | none | none |
user | id | issuer (the OIDC issuer of the user id, when not the default) |
jwt-bearer | sub | iss (defaults to the connector's OAuth client id), aud (defaults to the connector's OAuth token endpoint), additionalClaims |
| Field | Type | Description |
|---|---|---|
token | string | The access token to send to the provider. |
tokenId | string | undefined | Per-issuance identifier (stk_…) for correlating the token with Connect observability and usage data. |
expiresAt | number | Token expiration as a Unix timestamp in milliseconds. |
connector.id | string | Opaque internal identifier for the connector. |
connector.uid | string | Human-readable connector identifier (the same string you passed as the first arg). |
connector.type | string | The connector type (slack, github, linear, microsoft-entra, oauth, snowflake, salesforce, api-key, custom). |
name | string | undefined | Human-readable connector or installation name, when known. |
installationId | string | undefined | The installation this token was issued against, when applicable. |
tenantId | string | undefined | Provider's own tenant identifier (Slack team ID, GitHub org ID, Microsoft tenant GUID). |
externalSubject | string | undefined | The subject identifier at the provider (for example, the Slack user ID for a user-subject token). |
metadata | Record<string, unknown> | undefined | Driver-specific metadata stored during OAuth (varies by provider). |
claims | Record<string, unknown> | undefined | Allow-listed claims propagated from the upstream provider token. |
| Field | Type | Description |
|---|---|---|
id | string | Opaque internal connector identifier. |
uid | string | Human-readable connector identifier. |
name | string | Connector display name. |
type | string | Connector type, such as snowflake or oauth. |
service | string | Service the connector integrates with. |
clientUrl | string | undefined | Fully qualified service URL, when Vercel Connect can derive it. |
createdAt | number | Creation time as a Unix timestamp in milliseconds. |
updatedAt | number | Last update time as a Unix timestamp in milliseconds. |
vendor | Record<string, unknown> | Provider-specific public configuration stored on the connector. |
| Field | Type | Description |
|---|---|---|
vercelToken | string | Override the OIDC token the SDK reads from the environment. Useful for tests and non-Vercel runtimes. |
forceRefresh | boolean | For getToken and getTokenResponse, bypass the in-process cache and revalidate the grant with Vercel Connect. |
Revokes the provider grant for a connector subject and clears the SDK's in-process token cache. Provider support for revocation varies; see Revocation.
import { revokeToken } from '@vercel/connect';
await revokeToken('oauth/linear', {
subject: { type: 'user', id: 'user_123' },
});Removes one cached token without revoking its provider grant. Pass the same connector and complete request parameters that you used for getToken or getTokenResponse. The next request for that cache entry fetches a fresh token.
import { deleteTokenCacheEntry, getToken } from '@vercel/connect';
const params = {
subject: { type: 'app' as const },
scopes: ['chat:write'],
};
let token = await getToken('slack/acme-slack', params);
// If Slack rejects this token with a 401:
deleteTokenCacheEntry('slack/acme-slack', params);
token = await getToken('slack/acme-slack', params);The SDK maintains an in-process LRU cache with a maximum of 100 entries, keyed by the connector and the full request params. Cached tokens are reused on subsequent calls until they fall inside the validityBufferMs window, at which point the next call fetches a fresh one.
Pass { forceRefresh: true } in ConnectOptions to bypass the cache for a request. Use deleteTokenCacheEntry when you only need to discard one rejected token while preserving normal caching for future calls.
The SDK throws typed error classes you can match on with instanceof:
| Error | Cause |
|---|---|
NoValidTokenError | The connector exists but cannot produce a token for this request. The grant may have been revoked at the provider. |
UserAuthorizationRequiredError | The request used a { type: 'user' } subject and that user has not authorized the connector. Surface a "connect your account" UI. |
ConnectorInstallationRequiredError | The connector type requires an installation and none matches the request. Surface an install link. |
ConnectorNotFoundError | The team has no connector registered under the given uid. |
ClientNotLinkedToProjectError | The calling project is not linked to this connector. See Project links. |
ClientNotEnabledForEnvironmentError | The link exists but does not include the environment the OIDC token was issued for. |
Most SDK calls have a vercel connect CLI equivalent. The CLI supports --subject, --installation-id, and --scopes. The SDK additionally supports resources and authorizationDetails, which are not exposed through the CLI. The CLI supports --triggers on vercel connect attach, which has no SDK equivalent.
- Quickstart: Wire
getTokeninto a working integration in four steps. - Tokens: How a token request is shaped and what each field does.
- Authentication: What the OIDC token carries and how the API checks it.
Was this helpful?