Vercel Connect
Authenticate Slack, Discord, GitHub, Linear, Notion, and Telegram adapters with Vercel Connect — short-lived runtime tokens for outbound calls and OIDC-verified inbound webhooks where supported.
Vercel Connect lets your bot use a registered connector for adapter authentication instead of storing long-lived provider secrets. You register a connector once, link it to your project and environments, and your code requests scoped, short-lived tokens at runtime.
The @vercel/connect/chat subpath ships a helper per platform that you spread into the matching create*Adapter factory:
connectSlackAdapterconnectDiscordAdapterconnectGitHubAdapterconnectLinearAdapterconnectNotionAdapterconnectTelegramAdapter
Each helper wires outbound credentials; trigger-capable providers also wire inbound traffic:
- Outbound (your bot calls the provider API) — a function-form token field that resolves a fresh, short-lived token per call via
getToken. - Inbound (the provider calls your bot) — trigger-capable helpers include a
webhookVerifierthat validates the Vercel OIDC token Connect attaches to trigger-forwarded webhooks, replacing the provider's native signature check.
Install
pnpm add @vercel/connect@vercel/connect reads the deployment's OIDC token automatically. For local development, run vercel link followed by vercel env pull to download a short-lived token into .env.local.
Adapter support
| Adapter | Helper | Outbound field |
|---|---|---|
| Slack | connectSlackAdapter | botToken |
| Discord | connectDiscordAdapter | botToken, applicationId |
| GitHub | connectGitHubAdapter | installationToken |
| Linear | connectLinearAdapter | accessToken |
| Notion | connectNotionAdapter | token |
| Telegram | connectTelegramAdapter | botToken |
Each helper accepts (connector, params?, options?), where params is the getToken parameters minus subject (pinned to { type: "app" }), letting you pass through installationId, scopes, or validityBufferMs.
Set up a connector
The trigger-forwarding steps below apply to Slack, Discord, GitHub, and Linear. For Notion and Telegram, create and attach the connector without triggers, then use native webhook verification or polling.
- Create a connector for the provider in the Vercel dashboard or with the CLI, enabling trigger forwarding so inbound webhooks reach your project:
vercel connect create slack --name acme-slack --triggers- Attach your project and register your Chat SDK webhook route (
/api/webhooks/{platform}) as the trigger destination:
vercel connect attach slack/acme-slack \
--project my-bot --environment production \
--triggers --trigger-path /api/webhooks/slack- Pull a development token locally (deployments get
VERCEL_OIDC_TOKENautomatically):
vercel link
vercel env pullWire up the adapter
Spread the helper into the adapter factory. The webhook route is unchanged — Connect forwards verified events to the same handler.
import { Chat } from "chat";
import { createSlackAdapter } from "@chat-adapter/slack";
import { createRedisState } from "@chat-adapter/state-redis";
import { connectSlackAdapter } from "@vercel/connect/chat";
export const bot = new Chat({
userName: "mybot",
adapters: {
slack: createSlackAdapter({
...connectSlackAdapter("slack/acme-slack"),
}),
},
state: createRedisState(),
});Replace slack/acme-slack with your connector UID from the Connect dashboard or vercel connect list. Omit signingSecret / SLACK_SIGNING_SECRET when using the helper — the OIDC webhookVerifier is the freshness boundary.
Discord
import { createDiscordAdapter } from "@chat-adapter/discord";
import { connectDiscordAdapter } from "@vercel/connect/chat";
createDiscordAdapter({
...connectDiscordAdapter("discord/acme-discord"),
});The helper resolves botToken and applicationId from one Connect token
response. Omit DISCORD_BOT_TOKEN, DISCORD_PUBLIC_KEY, and
DISCORD_APPLICATION_ID — Connect OIDC verification replaces Discord's
Ed25519 public-key check for trigger-forwarded interactions.
GitHub
import { createGitHubAdapter } from "@chat-adapter/github";
import { connectGitHubAdapter } from "@vercel/connect/chat";
createGitHubAdapter({
...connectGitHubAdapter("github/acme-github"),
userName: "my-bot[bot]",
});installationToken is the installation access token a GitHub App would normally mint via its private-key JWT exchange — the adapter uses it directly and skips that exchange.
Linear
import { createLinearAdapter } from "@chat-adapter/linear";
import { connectLinearAdapter } from "@vercel/connect/chat";
createLinearAdapter({
...connectLinearAdapter("linear/acme-linear"),
mode: "agent-sessions",
});Use mode: "agent-sessions" for app-actor installs. For outbound calls outside webhook handling (cron jobs, workflows), wrap them in withInstallation() so a request-scoped client is bound:
await linear.withInstallation("org-id", async () => {
await linear.postMessage("linear:issue-id", "Hello from a background job");
});Notion
import { createNotionAdapter } from "@chat-adapter/notion";
import { connectNotionAdapter } from "@vercel/connect/chat";
createNotionAdapter({
...connectNotionAdapter("notion/acme-notion"),
verificationToken: process.env.NOTION_VERIFICATION_TOKEN,
});connectNotionAdapter supplies only the outbound token; it does not include a
webhookVerifier. Connect does not forward Notion triggers, so configure the
webhook subscription directly in Notion and retain
NOTION_VERIFICATION_TOKEN for native HMAC verification. Omit
NOTION_TOKEN when using the helper.
Telegram
import { createTelegramAdapter } from "@chat-adapter/telegram";
import { connectTelegramAdapter } from "@vercel/connect/chat";
createTelegramAdapter({
...connectTelegramAdapter("telegram/acme-telegram"),
secretToken: process.env.TELEGRAM_WEBHOOK_SECRET_TOKEN,
});connectTelegramAdapter supplies only the outbound botToken; it does not
include a webhookVerifier. The adapter derives webhook deduplication scope
from Telegram's stable bot identity, not the rotating credential. Retain
TELEGRAM_WEBHOOK_SECRET_TOKEN for native webhook verification or use polling
mode. Omit TELEGRAM_BOT_TOKEN when using the helper.
Custom webhook verification
Each trigger-capable helper attaches a default verifier that matches the deployment's project and environment automatically (projectId defaults to VERCEL_PROJECT_ID, environment to VERCEL_TARGET_ENV then VERCEL_ENV), so production, preview, and development each accept only their own tokens. Verification fails closed — if those values are absent, every request is rejected, and the issuer is pinned to https://oidc.vercel.com.
To add constraints (for example to accept multiple environments), build a verifier with createConnectWebhookVerifier and override the field:
import {
connectSlackAdapter,
createConnectWebhookVerifier,
} from "@vercel/connect/chat";
createSlackAdapter({
...connectSlackAdapter("slack/acme-slack"),
webhookVerifier: createConnectWebhookVerifier({
environment: ["production", "preview"],
}),
});Avoid hardcoding environment: "production" unless you only forward to production — it would reject preview and development deployments.
Notes and limitations
- App-scoped tokens. The helpers act as the application itself (
subject: { type: "app" }). End-user OAuth is a separate concern. - Freshness and replay. OIDC verification replaces each provider's native signature (and timestamp) check, so request freshness relies on the short-lived OIDC token's expiry rather than a signed timestamp, and there is no built-in delivery de-duplication. Keep your webhook handlers idempotent.
- Socket Mode is incompatible. Connect trigger forwarding is HTTP-only; it doesn't apply to the Slack adapter's Socket Mode.
- Testing. Connect forwards to deployed URLs, not
localhost— test against a preview or development deployment.
Related resources
- Vercel Connect Chat SDK documentation
- The Complete Guide to Vercel Connect
- Vercel Connect overview
- Vercel Connect triggers
@vercel/connectSDK reference
Read more
Creating a Chat Instance
Initialize the Chat class with adapters, state, and configuration options.
Error Handling
Handle rate limits, unsupported features, and other errors from adapters.
Testing
Test your bot handlers and custom adapters with @chat-adapter/tests — Vitest factories, custom matchers, and a setup file.
Threads, Messages, and Channels
Work with threads, messages, and channels across platforms.