Chat SDK Bridge
Unified chat interaction plugin bridging Slack, Teams, Discord, Telegram, Linear, WhatsApp, and more into Atlas via Chat SDK adapters.
The Chat SDK bridge plugin (@useatlas/chat) is the unified interaction layer for Atlas chat platforms. It bridges Chat SDK (vercel/chat) adapters into the Atlas plugin system, replacing the standalone @useatlas/slack and @useatlas/teams plugins with a single, multi-platform plugin.
Instead of maintaining separate plugins per platform, @useatlas/chat wraps Chat SDK adapters and provides built-in state management, error scrubbing, and conversation threading — all configured from a single chatPlugin() call.
Quick Navigation
Jump to the platform or topic you need:
| Platform | Setup Guide |
|---|---|
| Slack | Slack adapter options / Dedicated guide |
| Microsoft Teams | Teams adapter options / Dedicated guide |
| Discord | Discord adapter options |
| Google Chat | Google Chat adapter options / Dedicated guide |
| Telegram | Telegram adapter options / Dedicated guide |
| GitHub | GitHub adapter options / Dedicated guide |
| Linear | Linear adapter options / Dedicated guide |
| WhatsApp adapter options / Dedicated guide |
| Topic | Section |
|---|---|
| State persistence | State Adapters |
| Streaming responses | Streaming |
| Rich cards & formatting | Rich Cards |
| Interactive buttons | Quick-Action Buttons |
| CSV file export | File Upload (CSV Export) |
| Status reactions | Status Reactions |
| Error scrubbing | Error Scrubbing |
| Ephemeral messages | Ephemeral Messages |
| Proactive DMs | Proactive Direct Messages |
| Migration from deprecated plugins | Migrating from @useatlas/slack / Migrating from @useatlas/teams |
| Mounted routes reference | Mounted Routes |
| Environment variables | Environment Variables |
Installation
bun add @useatlas/chatPeer Dependencies
hono(^4.0.0)zod(^4.0.0)@useatlas/plugin-sdk(>=0.0.1)
Available Adapters
| Platform | Chat SDK Adapter | Status |
|---|---|---|
| Slack | @chat-adapter/slack | Stable |
| Microsoft Teams | @chat-adapter/teams | Stable |
| Discord | @chat-adapter/discord | Stable |
| Google Chat | @chat-adapter/gchat | Stable |
| Telegram | @chat-adapter/telegram | Stable |
| GitHub | @chat-adapter/github | Stable |
| Linear | @chat-adapter/linear | Stable |
@chat-adapter/whatsapp | Stable |
Stable adapters are bundled as dependencies of @useatlas/chat — no separate install needed.
Configuration
import { defineConfig } from "@atlas/api/lib/config";
import { chatPlugin } from "@useatlas/chat";
export default defineConfig({
plugins: [
chatPlugin({
adapters: {
slack: {
botToken: process.env.SLACK_BOT_TOKEN!,
signingSecret: process.env.SLACK_SIGNING_SECRET!,
},
},
executeQuery: myQueryFunction,
}),
],
});Multi-Platform with State Persistence
export default defineConfig({
plugins: [
chatPlugin({
adapters: {
slack: {
botToken: process.env.SLACK_BOT_TOKEN!,
signingSecret: process.env.SLACK_SIGNING_SECRET!,
clientId: process.env.SLACK_CLIENT_ID,
clientSecret: process.env.SLACK_CLIENT_SECRET,
},
teams: {
appId: process.env.TEAMS_APP_ID!,
appPassword: process.env.TEAMS_APP_PASSWORD!,
tenantId: process.env.TEAMS_TENANT_ID,
},
},
state: { backend: "pg" },
executeQuery: myQueryFunction,
actions: myActionCallbacks,
conversations: myConversationCallbacks,
}),
],
});Options
| Option | Type | Required | Description |
|---|---|---|---|
adapters | object | Yes | Platform adapter credentials (at least one required). |
adapters.slack.botToken | string | Yes* | Slack bot token (xoxb-...). |
adapters.slack.signingSecret | string | Yes* | Slack signing secret for request verification. |
adapters.slack.clientId | string | No | Client ID for multi-workspace OAuth. |
adapters.slack.clientSecret | string | No | Client secret for multi-workspace OAuth. |
adapters.teams.appId | string | Yes* | Microsoft App ID from Azure Bot registration. |
adapters.teams.appPassword | string | Yes* | Microsoft App Password from Azure Bot registration. |
adapters.teams.tenantId | string | No | Restrict to a specific Microsoft Entra ID tenant. |
adapters.discord.botToken | string | Yes* | Discord bot token. |
adapters.discord.applicationId | string | Yes* | Discord application ID. |
adapters.discord.publicKey | string | Yes* | 64-character hex string (Ed25519 public key from Discord Developer Portal). |
adapters.discord.mentionRoleIds | string[] | No | Role IDs that trigger mention handlers (in addition to direct @mentions). |
adapters.gchat.credentials | object | No* | Service account credentials (client_email, private_key, optional project_id). |
adapters.gchat.useApplicationDefaultCredentials | true | No* | Use Application Default Credentials. |
adapters.gchat.endpointUrl | string | No | HTTP endpoint URL for card button click actions. |
adapters.gchat.pubsubTopic | string | No | Pub/Sub topic for Workspace Events (projects/{project}/topics/{topic}). |
adapters.gchat.impersonateUser | string | No | User email for domain-wide delegation. |
adapters.telegram.botToken | string | Yes* | Telegram bot token from BotFather. |
adapters.telegram.secretToken | string | No | Webhook secret token for request verification. |
adapters.github.appId | string | Yes* | GitHub App ID. Required for App-based auth. |
adapters.github.privateKey | string | Yes* | GitHub App private key (PEM format). |
adapters.github.installationId | number | No | Installation ID for single-tenant mode. |
adapters.github.token | string | Yes* | Personal Access Token (alternative to App auth). |
adapters.github.webhookSecret | string | No | Webhook secret for HMAC-SHA256 verification. |
adapters.github.userName | string | No | Bot username for @-mention detection. |
adapters.linear.apiKey | string | Yes* | Personal API key from Linear Settings. |
adapters.linear.accessToken | string | Yes* | Pre-obtained OAuth access token. |
adapters.linear.clientId | string | Yes* | OAuth application client ID. |
adapters.linear.clientSecret | string | Yes* | OAuth application client secret. Required with clientId. |
adapters.linear.webhookSecret | string | No | Webhook signing secret for HMAC-SHA256 verification. |
adapters.linear.userName | string | No | Bot display name for @-mention detection. |
adapters.whatsapp.phoneNumberId | string | Yes* | WhatsApp Business phone number ID. |
adapters.whatsapp.accessToken | string | Yes* | System User access token for Cloud API. |
adapters.whatsapp.verifyToken | string | Yes* | Verify token for webhook challenge-response. |
adapters.whatsapp.appSecret | string | Yes* | Meta App Secret for HMAC-SHA256 signature verification. |
adapters.whatsapp.userName | string | No | Bot display name. |
adapters.whatsapp.apiVersion | string | No | Meta Graph API version (default: "v21.0"). |
state | object | No | State backend config (default: { backend: "memory" }). |
state.backend | "memory" | "pg" | "redis" | No | State backend to use. Default: "memory". |
state.tablePrefix | string | No | Table name prefix for PG backend. Must be a valid SQL identifier. Default: "chat_". |
state.redisUrl | string | No | Redis connection URL (future — not yet implemented). |
slashCommandName | string | No | Slash command name (e.g. "/data-query"). Must start with / followed by lowercase alphanumeric + hyphens. Default: "/atlas". |
executeQuery | function | Yes | Callback to run the Atlas agent on a question. |
executeQueryStream | function | No | Streaming query callback — returns { stream, result }. See Streaming. |
streaming | object | No | Streaming configuration. |
streaming.enabled | boolean | No | Enable streaming responses. Default: true. |
streaming.chunkIntervalMs | number | No | Minimum interval (ms) between edits on non-streaming platforms. Range: 200–10000. Default: 500. |
checkRateLimit | function | No | Optional rate limiting callback. |
scrubError | function | No | Optional error scrubbing callback. |
actions | ActionCallbacks | No | Optional action framework callbacks (approve, deny, get). |
conversations | ConversationCallbacks | No | Optional conversation persistence callbacks (create, addMessage, get, generateTitle). |
fileUpload | object | No | CSV file upload configuration. |
fileUpload.autoAttachThreshold | number | No | Row count above which CSV files are auto-attached. Set to 0 to disable. Default: 20. |
fileUpload.webBaseUrl | string | No | Atlas web UI base URL for fallback links on platforms without file upload (GitHub, Linear). |
reactions | object | No | Status reaction configuration. See Status Reactions. |
reactions.enabled | boolean | No | Enable emoji reactions on user messages. Default: true. |
reactions.customEmoji | object | No | Override default emoji for each lifecycle stage (received, processing, complete, error). |
* Required when that adapter is configured. At least one adapter must be present.
For Slack OAuth, clientId and clientSecret must both be provided together.
State Adapters
The state backend controls how thread subscriptions, conversation history, and distributed locks are persisted.
| Backend | Description | Persistence | Production Ready |
|---|---|---|---|
memory | In-memory store. State lost on restart. | None | No |
pg | PostgreSQL via Atlas internal DB (DATABASE_URL). | Full | Yes |
redis | Redis (stub — not yet implemented). | — | No |
chatPlugin({
adapters: { slack: { /* ... */ } },
state: {
backend: "pg",
tablePrefix: "chat_", // default — customize to avoid conflicts
},
executeQuery: myQueryFunction,
})The pg backend requires DATABASE_URL to be configured. It auto-creates three tables (chat_subscriptions, chat_locks, chat_cache) using CREATE TABLE IF NOT EXISTS on first connection.
For production deployments, use the pg backend. The memory backend is suitable for development and testing only — state is lost when the process restarts.
How It Works
The plugin bridges Chat SDK events to the Atlas agent:
- Slash commands (configurable, default
/atlas <question>) — Posts a "Thinking about: <question>..." indicator, subscribes the thread, runsexecuteQuery, edits the response with a JSX card (or markdown fallback) - @mentions — Subscribes to the thread, runs
executeQuery, posts the result as a native JSX card (Block Kit on Slack, Adaptive Cards on Teams, Discord Embeds) with markdown fallback - Thread follow-ups — Messages in subscribed threads are routed through
executeQuerywith conversation history, posted as a JSX card with markdown fallback - Actions — Approval button clicks call
actions.approve()oractions.deny(), then update the original message. Quick-action buttons (Run Again, Export CSV) appear on query result cards - Modals (Slack only) — Clarification dialogs collect structured input and feed responses back into the conversation
Rich Cards
Query results render as platform-native cards using the Chat SDK JSX runtime. Cards include the answer, SQL code block, data table preview, and metadata (steps, tokens). Telegram, GitHub, Linear, and WhatsApp receive formatted text with the markdown fallback.
Card components are exported for host customization:
import { buildQueryResultCard, buildErrorCard, buildApprovalCardJSX, buildDataTableCard } from "@useatlas/chat";
// Or from the dedicated cards entrypoint:
import { buildQueryResultCard } from "@useatlas/chat/cards";Configurable Slash Command
By default, the bridge registers /atlas as the slash command. To use a custom name (e.g., if you want /data or /query), set slashCommandName:
chatPlugin({
adapters: { slack: { /* ... */ } },
slashCommandName: "/data-query",
executeQuery: myQueryFunction,
})The command name must start with / followed by lowercase alphanumeric characters or hyphens. Remember to register the matching slash command in your platform's app configuration (e.g., Slack App Manifest, Discord Application Commands).
Quick-Action Buttons
Query result cards include interactive buttons when SQL was executed:
- Run Again — Re-executes the same SQL query and posts updated results in the thread
- Export CSV — Re-executes the query and uploads results as a CSV file attachment
Buttons are rendered as platform-native interactive components (Block Kit buttons on Slack, Adaptive Card actions on Teams, Discord buttons). On platforms with limited interactivity, the card includes a text fallback with instructions:
To run again, re-send the same question. To export, ask "export the last result as CSV".
File Upload (CSV Export)
Query results can be exported as CSV file attachments. This happens in three ways:
- Export CSV button — Clicking the "Export CSV" button on a query result card re-executes the SQL and uploads the full result as a CSV file
- Auto-attach — When query results exceed the configured row threshold (default: 20 rows), a CSV file is automatically attached alongside the card response
- Keyword detection — When the user's question contains export keywords ("export", "download", "send as CSV", "as csv", "csv file", "to csv"), the CSV is always attached regardless of row count
Platform support
| Platform | File Upload | Fallback |
|---|---|---|
| Slack | Yes | — |
| Teams | Yes | — |
| Discord | Yes | — |
| Google Chat | Yes | — |
| Telegram | Yes | — |
| Yes | — | |
| GitHub | No | Link to web UI |
| Linear | No | Link to web UI |
On platforms without file upload support, a "View full results" link to the Atlas web UI is posted instead. Configure fileUpload.webBaseUrl to enable this:
chatPlugin({
adapters: { /* ... */ },
executeQuery: myQueryFunction,
fileUpload: {
autoAttachThreshold: 50, // attach CSV when > 50 rows (default: 20)
webBaseUrl: "https://app.example.com", // fallback link for GitHub/Linear
},
})CSV files include a UTF-8 BOM for Excel compatibility and follow RFC 4180 formatting (proper quoting and escaping of commas, newlines, and double quotes).
Interactive Capabilities by Platform
| Feature | Slack | Teams | Discord | Google Chat | Telegram | GitHub | Linear | |
|---|---|---|---|---|---|---|---|---|
| Slash commands | Yes | Yes | Yes | Yes | Yes | No | No | No |
| Action buttons | Yes | Yes | Yes | Yes | Partial (inline keyboard) | No | No | No |
| File uploads | Yes | Yes | Yes | Yes | Yes | No | No | Yes |
| Status reactions | Yes | Limited | Yes | Yes | Yes | No | No | Limited |
| Modals | Yes | No | No | No | No | No | No | No |
Slack Modals
On Slack, the bridge supports modals for parameter collection. When the agent needs clarification (e.g., date range, segment selection), a modal dialog can be opened with input fields.
The bridge exposes an openClarificationModal method for host integrations that need to collect structured input:
// From an action handler or slash command context
await bridge.openClarificationModal(event, "What date range would you like to analyze?");The modal collects user input and feeds the response back into the conversation thread. Modal submissions are validated server-side — empty responses are rejected with an error message displayed in the modal.
Modals are currently supported only on Slack. On other platforms, clarifying questions are handled through regular thread messages.
Status Reactions
The bridge adds emoji reactions to user messages as visual feedback during query processing:
| Stage | Emoji | Meaning |
|---|---|---|
| Received | eyes (👀) | "I see your message" |
| Processing | hourglass (⏳) | "Running your query" |
| Complete | check (✅) | "Query finished successfully" |
| Error | warning (⚠️) | "Something went wrong" |
Reactions are managed via Chat SDK's type-safe emoji API — no raw Unicode strings. Each transition removes the previous reaction and adds the new one.
Platform support
| Platform | Reactions | Notes |
|---|---|---|
| Slack | Full | Native emoji reactions |
| Discord | Full | Unicode emoji reactions |
| Google Chat | Full | Unicode emoji reactions |
| Telegram | Full | Message reactions |
| Teams | Limited | Read-only — reactions may silently no-op |
| Limited | Reactions may silently no-op | |
| GitHub | N/A | Graceful no-op |
| Linear | N/A | Graceful no-op |
Reactions are always best-effort — failures are logged at debug level and never fail a query. On platforms where reactions aren't supported, the lifecycle silently does nothing.
Configuration
Reactions are enabled by default. To disable or customize:
import { emoji } from "chat";
chatPlugin({
adapters: { slack: { /* ... */ } },
executeQuery: myQueryFunction,
reactions: {
enabled: true, // default — set to false to disable
customEmoji: {
received: emoji.sparkles, // override 👀 with ✨
complete: emoji.rocket, // override ✅ with 🚀
},
},
})For workspaces with custom emoji installed (Slack, Discord), use emoji.custom():
reactions: {
customEmoji: {
received: emoji.custom("atlas_thinking"),
complete: emoji.custom("atlas_done"),
},
}Disabling reactions
Set reactions.enabled to false to skip all reaction lifecycle calls:
chatPlugin({
adapters: { /* ... */ },
executeQuery: myQueryFunction,
reactions: { enabled: false },
})Streaming
When executeQueryStream is provided and streaming.enabled is not false, the bridge streams agent responses incrementally to chat platforms instead of waiting for the full result.
How it works
- Slack — Uses Chat SDK's native streaming API for real-time token-by-token updates
- Teams, Discord, Google Chat — Posts an initial message then edits it with new content at the configured
chunkIntervalMsinterval (post + edit pattern) - Telegram — Posts an initial message then progressively edits it (same post + edit pattern)
- GitHub — Accumulates the full stream and posts as a single comment (no progressive edits due to API constraints)
- Linear — Accumulates the full stream and posts as a single comment (same as GitHub)
- WhatsApp — Buffers the full stream and sends as a single message (WhatsApp does not support message editing)
The bridge passes the async iterable from executeQueryStream directly to Chat SDK's thread.post(), which automatically selects the best transport per platform.
Configuration
import { chatPlugin } from "@useatlas/chat";
import type { StreamChunk } from "@useatlas/chat";
chatPlugin({
adapters: {
slack: { botToken: "xoxb-...", signingSecret: "..." },
teams: { appId: "...", appPassword: "..." },
},
executeQuery: myQueryFunction, // always required — used when streaming is disabled
streaming: {
enabled: true, // default: true
chunkIntervalMs: 1000, // override Chat SDK default of 500ms — edit throttle for Teams/Discord/GChat
},
executeQueryStream: (question, options) => {
// Return a stream and a promise for the final structured result
const agentResult = runAgent(question, options);
return {
stream: agentResult.textStream, // AsyncIterable<string | StreamChunk>
result: agentResult.finalResult, // Promise<ChatQueryResult>
};
},
})Status indicators
The stream can yield structured StreamChunk objects for rich status updates during agent reasoning:
executeQueryStream: (question) => {
const stream = async function* () {
yield { type: "task_update", id: "think", title: "Analyzing question...", status: "in_progress" } as StreamChunk;
yield { type: "task_update", id: "sql", title: "Running SQL...", status: "in_progress" } as StreamChunk;
yield { type: "task_update", id: "sql", title: "Running SQL", status: "complete" } as StreamChunk;
yield "Here are the results..."; // plain text chunks also work
};
return { stream: stream(), result: computeResult() };
},Error handling
If the stream fails mid-way, partial content may already be visible on the platform. The bridge catches the error, scrubs sensitive information, and delivers an error card — either as a follow-up message (mentions and follow-ups) or by editing the thinking indicator (slash commands). The executeQuery callback is not used as a fallback — if streaming fails, the error is reported directly.
Disabling streaming
Set streaming.enabled to false, or omit executeQueryStream entirely. The bridge falls back to the non-streaming executeQuery path, posting the complete result as a single message.
The chunkIntervalMs setting controls how frequently messages are edited on platforms that use the post + edit pattern. Values below 200ms are rejected to avoid platform rate limits. Slack uses native streaming and is not affected by this setting.
Mounted Routes
| Method | Path | Description |
|---|---|---|
POST | /webhooks/slack | Slack webhook (slash commands, events, interactions) |
POST | /webhooks/teams | Teams webhook (Bot Framework activities) |
POST | /webhooks/discord | Discord webhook (interactions, gateway events) |
POST | /webhooks/gchat | Google Chat webhook (@mentions, DMs, card clicks, Pub/Sub) |
POST | /webhooks/telegram | Telegram webhook (messages, @mentions, commands, callback queries) |
POST | /webhooks/github | GitHub webhook (issue comments, PR comments, review comments) |
POST | /webhooks/linear | Linear webhook (issue comment events) |
GET | /webhooks/whatsapp | WhatsApp webhook verification (Meta challenge-response) |
POST | /webhooks/whatsapp | WhatsApp webhook (incoming messages) |
GET | /oauth/slack/install | Slack OAuth install redirect (only when clientId configured) |
GET | /oauth/slack/callback | Slack OAuth callback (only when clientId configured) |
Routes are mounted under the plugin's base path (e.g., /api/plugins/chat-interaction/webhooks/slack).
Environment Variables
| Variable | Description |
|---|---|
SLACK_BOT_TOKEN | Slack bot token (xoxb-...) |
SLACK_SIGNING_SECRET | Slack signing secret |
SLACK_CLIENT_ID | Slack OAuth client ID (multi-workspace) |
SLACK_CLIENT_SECRET | Slack OAuth client secret (multi-workspace) |
TEAMS_APP_ID | Microsoft App ID |
TEAMS_APP_PASSWORD | Microsoft App Password |
TEAMS_TENANT_ID | Microsoft Entra ID tenant ID (optional) |
DISCORD_BOT_TOKEN | Discord bot token |
DISCORD_APPLICATION_ID | Discord application ID |
DISCORD_PUBLIC_KEY | Discord application public key (64-char hex, Ed25519) |
GOOGLE_CHAT_CREDENTIALS | Google Chat service account key JSON (stringified) |
GOOGLE_CHAT_USE_ADC | Set to "true" for Application Default Credentials |
GOOGLE_CHAT_PUBSUB_TOPIC | Pub/Sub topic for Workspace Events |
GOOGLE_CHAT_IMPERSONATE_USER | User email for domain-wide delegation |
TELEGRAM_BOT_TOKEN | Telegram bot token from BotFather |
TELEGRAM_WEBHOOK_SECRET_TOKEN | Optional webhook secret token for request verification |
GITHUB_APP_ID | GitHub App ID (for App-based auth) |
GITHUB_PRIVATE_KEY | GitHub App private key in PEM format (for App-based auth) |
GITHUB_INSTALLATION_ID | Installation ID for single-tenant mode (optional) |
GITHUB_TOKEN | Personal Access Token (for PAT auth) |
GITHUB_WEBHOOK_SECRET | Webhook secret for HMAC-SHA256 verification |
LINEAR_API_KEY | Linear personal API key (for API key auth) |
LINEAR_ACCESS_TOKEN | Linear OAuth access token (for OAuth token auth) |
LINEAR_CLIENT_ID | Linear OAuth application client ID (for OAuth App auth) |
LINEAR_CLIENT_SECRET | Linear OAuth application client secret (for OAuth App auth) |
LINEAR_WEBHOOK_SECRET | Linear webhook signing secret for HMAC-SHA256 verification |
WHATSAPP_PHONE_NUMBER_ID | WhatsApp Business phone number ID |
WHATSAPP_ACCESS_TOKEN | System User access token for Cloud API |
WHATSAPP_VERIFY_TOKEN | Verify token for webhook challenge-response |
WHATSAPP_APP_SECRET | Meta App Secret for HMAC-SHA256 signature verification |
DATABASE_URL | Required for pg state backend |
Error Scrubbing
All error messages are scrubbed before sending to chat platforms. Built-in patterns redact:
- Connection strings —
postgres://,postgresql://,mysql://,mongodb://,redis://,clickhouse:// - Stack traces —
at Module.foo (/path:line:col)patterns - File paths —
/home/...,/usr/...,/app/...,/src/... - API keys and tokens —
sk-*,xoxb-*,xoxp-*(Slack),ghp_*,gho_*(GitHub),Bearer *
Provide a custom scrubError callback for additional scrubbing.
Ephemeral Messages
Error messages and rate-limit notices are posted as ephemeral messages by default — visible only to the requesting user, not the entire channel.
| Platform | Behavior |
|---|---|
| Slack | Native ephemeral (disappears on reload) |
| Google Chat | Native private message (persists, only target user sees it) |
| Discord | DM fallback (bot sends a DM to the user) |
| Teams | DM fallback |
| Telegram | DM fallback |
| DM fallback | |
| GitHub | DM fallback (opens issue/comment thread with user) |
| Linear | DM fallback |
Configure via the ephemeral option:
chatPlugin({
adapters: { slack: { /* ... */ } },
executeQuery,
ephemeral: {
// Default: true — errors shown only to requesting user
errorsAsEphemeral: true,
},
})Set errorsAsEphemeral: false to post errors publicly in the thread.
Proactive Direct Messages
The bridge exposes a sendDirectMessage() method for programmatic DMs — useful for digest delivery, alert notifications, and anomaly detection.
The sendDirectMessage() method is available on the ChatBridge instance returned by the bridge's internal initialization. It accepts a platform name, user ID, and message:
// Send a proactive DM to a Slack user
const result = await bridge.sendDirectMessage(
"slack", // platform name
"U0123456789", // platform-specific user ID
"Your saved query detected an anomaly in revenue data.",
);
if (result) {
console.log(`DM sent: ${result.messageId}`);
}sendDirectMessage() works across all configured adapters that support DMs. Returns null if the adapter is not configured, does not support DMs, or if delivery fails.
Migrating from @useatlas/slack
// Before:
import { slackPlugin } from "@useatlas/slack";
slackPlugin({ signingSecret: "...", botToken: "xoxb-...", executeQuery })
// After:
import { chatPlugin } from "@useatlas/chat";
chatPlugin({
adapters: {
slack: { botToken: "xoxb-...", signingSecret: "..." },
},
executeQuery,
})Update your Slack app webhook URLs:
| Route | @useatlas/slack | @useatlas/chat |
|---|---|---|
| Slash commands | /commands | /webhooks/slack |
| Events | /events | /webhooks/slack |
| Interactions | /interactions | /webhooks/slack |
| OAuth install | /install | /oauth/slack/install |
| OAuth callback | /callback | /oauth/slack/callback |
Migrating from @useatlas/teams
// Before:
import { teamsPlugin } from "@useatlas/teams";
teamsPlugin({ appId: "...", appPassword: "...", executeQuery })
// After:
import { chatPlugin } from "@useatlas/chat";
chatPlugin({
adapters: {
teams: { appId: "...", appPassword: "..." },
},
executeQuery,
})Update the messaging endpoint in your Azure Bot Configuration:
https://your-atlas-api.example.com/api/plugins/chat-interaction/webhooks/teams| Feature | @useatlas/teams | @useatlas/chat |
|---|---|---|
| Webhook | /messages | /webhooks/teams |
| Adaptive Cards | Hand-rolled builders | Automatic via Chat SDK |
| State | None | State adapter (memory/PG/Redis) |
| Follow-ups | Single query per activity | Chat SDK subscription model |
| Multi-platform | Teams only | Slack, Teams, Discord, etc. |
Troubleshooting
Slack signature verification failures
Ensure SLACK_SIGNING_SECRET matches the signing secret from your Slack app's Basic Information page. Clock skew between your server and Slack can also cause verification failures.
Teams authorization failures
Ensure TEAMS_APP_ID and TEAMS_APP_PASSWORD match the Azure Bot registration credentials. If using tenant restriction, verify the tenant ID matches the Microsoft Entra ID tenant where Teams is configured.
State adapter connection errors
For the pg backend, verify DATABASE_URL is set and the database is accessible. The adapter will fail at plugin initialization if it cannot connect. The redis backend is not yet implemented and will throw at startup — use pg or memory instead.
Plugin returns 503
Routes return 503 if the plugin hasn't finished initializing. This is normal during startup — wait for the Chat interaction plugin initialized log message before sending webhooks.