Skip to content
Docs

Chat SDK with Vercel Connect

Vercel Connect is available on all plans and is subject to the Vercel Connect terms

The @vercel/connect/chat entry point connects Vercel Connect credentials to the Chat SDK adapter for each supported platform.

The helpers configure outbound and inbound requests separately:

  • Outbound provider API requests: Each credential field is an async resolver. The adapter calls it when it needs a provider credential. @vercel/connect handles caching and refreshes short-lived tokens.
  • Inbound provider webhooks: Slack, Discord, Microsoft Teams, GitHub, and Linear can use Connect triggers. Connect verifies the provider's request, forwards it, and adds a Vercel OpenID Connect (OIDC) bearer token. The helper's webhookVerifier verifies that OIDC token instead of performing the provider's native verification.

The inbound verifier does not verify provider signatures or Microsoft's native JWT itself. Only use it for requests forwarded through a Connect trigger.

PlatformHelperOutbound fieldsInbound verification
SlackconnectSlackAdapterbotTokenConnect trigger and Vercel OIDC
DiscordconnectDiscordAdapterbotToken, applicationIdConnect trigger and Vercel OIDC
Microsoft TeamsconnectTeamsAdapterappId, tokenConnect trigger and Vercel OIDC
GitHubconnectGitHubAdapterinstallationTokenConnect trigger and Vercel OIDC
LinearconnectLinearAdapteraccessTokenConnect trigger and Vercel OIDC
NotionconnectNotionAdaptertokenDirect Notion webhook and NOTION_VERIFICATION_TOKEN
TelegramconnectTelegramAdapterbotTokenDirect Telegram webhook secret or polling

All helpers request app-subject credentials with subject: { type: 'app' }. They do not request credentials on behalf of an end user.

The following example connects a Slack app. Replace slack with discord, github, linear, or microsoft-teams for another trigger-capable platform. For Microsoft Teams, use a connector UID such as microsoft-teams/acme-teams and the webhook route /api/webhooks/teams for bot.webhooks.teams.

  1. Create the connector and enable triggers:

    terminal
    vercel connect create slack --name acme-slack --triggers
  2. Attach it to your project. Set the trigger path to the matching Chat SDK webhook route:

    terminal
    vercel connect attach slack/acme-slack \
      --project my-bot \
      --environment production \
      --triggers \
      --trigger-path /api/webhooks/slack

    The path must match the route where you expose bot.webhooks.<platform>. See the vercel connect CLI reference for Preview branches and Custom Environments.

  3. Link your local directory and pull a development OIDC token:

    terminal
    vercel link
    vercel env pull

    Vercel deployments receive VERCEL_OIDC_TOKEN automatically. Connect forwards webhooks to deployed URLs, not localhost.

  4. Spread the Connect config into the adapter:

    app/chat.ts
    import { createSlackAdapter } from '@chat-adapter/slack';
    import { connectSlackAdapter } from '@vercel/connect/chat';
    import { Chat } from 'chat';
     
    export const bot = new Chat({
      userName: 'my-bot',
      adapters: {
        slack: createSlackAdapter({
          ...connectSlackAdapter('slack/acme-slack'),
        }),
      },
    });

Keep your existing Chat SDK webhook route. Connect sends the forwarded request to the path you registered during vercel connect attach.

Pass requested provider scopes as the helper's second argument:

app/chat.ts
import { createSlackAdapter } from '@chat-adapter/slack';
import { connectSlackAdapter } from '@vercel/connect/chat';
 
const slack = createSlackAdapter({
  ...connectSlackAdapter('slack/acme-slack', {
    scopes: ['chat:write'],
  }),
});

Do not set signingSecret or SLACK_SIGNING_SECRET for a Connect-triggered route. The adapter verifies the forwarded request's Vercel OIDC token.

The helper resolves the bot token and application ID from one Connect token response:

app/chat.ts
import { createDiscordAdapter } from '@chat-adapter/discord';
import { connectDiscordAdapter } from '@vercel/connect/chat';
 
const discord = createDiscordAdapter({
  ...connectDiscordAdapter('discord/acme-discord'),
});

Do not set DISCORD_BOT_TOKEN, DISCORD_APPLICATION_ID, or DISCORD_PUBLIC_KEY for the Connect-backed fields and trigger route.

connectTeamsAdapter resolves the bot's appId during initialization and supplies a scope-aware token callback for Bot Framework and Microsoft Graph:

app/chat.ts
import { createTeamsAdapter } from '@chat-adapter/teams';
import { connectTeamsAdapter } from '@vercel/connect/chat';
 
const teams = createTeamsAdapter({
  ...connectTeamsAdapter('microsoft-teams/acme-teams'),
});
app/chat.js
import { createTeamsAdapter } from '@chat-adapter/teams';
import { connectTeamsAdapter } from '@vercel/connect/chat';
 
const teams = createTeamsAdapter({
  ...connectTeamsAdapter('microsoft-teams/acme-teams'),
});

Omit TEAMS_APP_ID and TEAMS_APP_PASSWORD when using the helper. Enable trigger forwarding to /api/webhooks/teams; the helper verifies forwarded requests with Vercel OIDC instead of Microsoft's native JWT.

The token callback requests separate tokens for https://api.botframework.com/.default and https://graph.microsoft.com/.default. Graph reads still require the appropriate installation and consent permissions. Connect uses the bot's home tenant for managed tokens.

Chat initializes the adapter automatically before handling webhooks. Call await bot.initialize() before using adapter methods directly, because the appId resolver runs during initialization.

The adapter receives an installation access token directly, so it does not perform a private-key JSON Web Token exchange:

app/chat.ts
import { createGitHubAdapter } from '@chat-adapter/github';
import { connectGitHubAdapter } from '@vercel/connect/chat';
 
const github = createGitHubAdapter({
  ...connectGitHubAdapter('github/acme-github'),
  userName: 'my-bot[bot]',
});

Use the agent sessions mode for an app-actor installation:

app/chat.ts
import { createLinearAdapter } from '@chat-adapter/linear';
import { connectLinearAdapter } from '@vercel/connect/chat';
 
const linear = createLinearAdapter({
  ...connectLinearAdapter('linear/acme-linear'),
  mode: 'agent-sessions',
});

Notion support is outbound-only. Create and attach the connector without --triggers:

terminal
vercel connect create notion --name acme-notion
vercel connect attach notion/acme-notion --project my-bot

Configure the webhook directly in Notion. Keep Notion's native verification token for inbound HMAC verification:

app/chat.ts
import { createNotionAdapter } from '@chat-adapter/notion';
import { connectNotionAdapter } from '@vercel/connect/chat';
 
const notion = createNotionAdapter({
  ...connectNotionAdapter('notion/acme-notion'),
  verificationToken: process.env.NOTION_VERIFICATION_TOKEN,
});

The helper supplies token, so you can omit NOTION_TOKEN. It does not supply a webhookVerifier.

Telegram support is outbound-only. Create and attach the connector without --triggers:

terminal
vercel connect create telegram --name acme-telegram
vercel connect attach telegram/acme-telegram --project my-bot

Keep Telegram's native webhook secret, or use polling mode without an inbound webhook:

app/chat.ts
import { createTelegramAdapter } from '@chat-adapter/telegram';
import { connectTelegramAdapter } from '@vercel/connect/chat';
 
const telegram = createTelegramAdapter({
  ...connectTelegramAdapter('telegram/acme-telegram'),
  secretToken: process.env.TELEGRAM_WEBHOOK_SECRET_TOKEN,
});

The helper supplies botToken, so you can omit TELEGRAM_BOT_TOKEN. It does not supply a webhookVerifier.

Each helper accepts Connect token parameters as its second argument. You can pass scopes, installationId, and validityBufferMs. You cannot override the app subject.

Use installationId when one connector has several provider installations:

app/chat.ts
const slack = createSlackAdapter({
  ...connectSlackAdapter('slack/acme-slack', {
    installationId: 'sci_123',
    scopes: ['chat:write'],
  }),
});

If you omit installationId, Connect uses the connector's default installation. The helpers do not automatically select an installation from an inbound workspace or organization. See Installations and Tokens for selection and scope behavior.

The default verifier accepts a token only when its project and environment match the current deployment. It reads the project from VERCEL_PROJECT_ID and the environment from VERCEL_TARGET_ENV, then VERCEL_ENV.

Use createConnectWebhookVerifier to accept additional environments or pin an explicit project:

app/chat.ts
import { createSlackAdapter } from '@chat-adapter/slack';
import {
  connectSlackAdapter,
  createConnectWebhookVerifier,
} from '@vercel/connect/chat';
 
const slack = createSlackAdapter({
  ...connectSlackAdapter('slack/acme-slack'),
  webhookVerifier: createConnectWebhookVerifier({
    environment: ['production', 'preview'],
  }),
});

The verifier fails closed when it cannot determine a project or environment. Do not pin it to production if a Preview deployment must receive triggers.

  • Preview deployments: Register a Preview branch as the trigger destination and allow preview in a custom verifier. Connect cannot forward to localhost.
  • Freshness and idempotency: OIDC token expiry provides request freshness. The verifier does not provide nonce or delivery deduplication. Providers and Connect can retry requests, so make every handler idempotent.
  • Slack Socket Mode: Connect trigger forwarding uses HTTP. It does not work with the Slack adapter's Socket Mode.
  • Installation routing: The helpers use one configured or default installation. They do not derive a tenant-specific installation from each inbound event.

Deploy a Chat SDK template to start building AI agents that use Vercel Connect to securely access third-party services and APIs:

Last updated September 18, 2026

Was this helpful?

supported.