Novu

The fastest way to put your Chat SDK agent in front of customers — on Slack, Teams, WhatsApp, Telegram, and email. Novu manages the credentials, identity, and delivery for every channel.

Vendor-official adapter maintained by Novu, not Vercel or Chat SDK contributors. For feature requests, bug reports, and support, file an issue on the adapter's repo.

Install

pnpm add @novu/chat-sdk-adapter chat @chat-adapter/state-memory

Quick start

  1. Install the adapter (see above).
  2. Wire the adapter in your Chat SDK app.
  3. Setup a channel — run npx novu connect --runtime chat-sdk and pick Slack, Email, Telegram, WhatsApp, or Microsoft Teams.
import { Chat } from "chat";
import { createMemoryState } from "@chat-adapter/state-memory";
import { createNovuAdapter } from "@novu/chat-sdk-adapter";

const novu = createNovuAdapter();

const chat = new Chat({
  userName: "support",
  adapters: { novu },
  state: createMemoryState(),
});

chat.onNewMention(async (thread, message) => {
  await thread.post(`Hi! You said: ${message.text}`);
});

chat.onSubscribedMessage(async (thread, message) => {
  await thread.post(`echo: ${message.text}`);
});

await chat.initialize();

Then connect and setup your agent to a real channel:

npx novu connect --runtime chat-sdk

The CLI authenticates your Novu account, creates your bridge agent, writes NOVU_SECRET_KEY and NOVU_AGENT_IDENTIFIER to your env. It provisions the provider integration, assists in setting up the bot entity and stores credentials multi-tenant user oauth credentials.

Why Novu

Everything below is what you'd otherwise build and maintain against each platform's raw APIs:

  • Managed credentials & channel setup — OAuth, token storage and rotation, and Slack Connect are handled by Novu. No platform secrets live in your app.
  • Unified identity — every channel resolves to a single Novu subscriber mapped to your own user, so your agent always knows who it's talking to.
  • Built-in observability — delivery status and full conversation history for every message, out of the box.
  • Notify and reply in one loop — your agent sends a proactive notification and handles the reply on the same channel, across every channel, from one handler set.

Configuration

Set via the environment (the CLI writes these for you) or pass them to createNovuAdapter({ ... }):

Prop

Type

Environment variables

VariableDescription
NOVU_SECRET_KEYNovu API key — authorizes replies and verifies the inbound HMAC. Set automatically by npx novu connect.
NOVU_AGENT_IDENTIFIERYour bridge agent ID — set automatically by npx novu connect.
NOVU_API_BASE_URLAPI base URL. Defaults to https://api.novu.co.

Novu context

Inside any handler, getNovuContext(thread) unlocks Novu-native data:

import { getNovuContext } from "@novu/chat-sdk-adapter";

chat.onSubscribedMessage(async (thread, message) => {
  const ctx = getNovuContext(thread);

  const subscriber = await ctx.getSubscriber(); // email, phone, locale, custom data
  const history = await ctx.getHistory(); // canonical transcript — ideal for LLM context
  const ticketId = await ctx.getMetadata("ticketId");

  if (subscriber?.data?.plan === "enterprise") {
    await thread.post("Priority support enabled.");
  }
});

Proactive multi-channel notifications

Your agent can push notifications outside an active conversation by triggering a Novu workflow. Define the workflow once in Novu with the channels you want (Slack, email, WhatsApp, etc.); a single trigger call delivers to every step in that workflow.

By default, the trigger targets the subscriber on the current conversation. Pass explicit recipients to notify someone else or a topic:

import { getNovuContext } from "@novu/chat-sdk-adapter";

chat.onSubscribedMessage(async (thread, message) => {
  const ctx = getNovuContext(thread);

  // Notify the same user on every channel in the "order-shipped" workflow.
  await ctx.trigger("order-shipped", {
    payload: {
      orderId: "1234",
      trackingUrl: "https://example.com/track/1234",
    },
  });

  // Escalate to a different subscriber on a separate workflow.
  await ctx.trigger("manager-alert", {
    to: { subscriberId: "manager-42" },
    payload: { reason: message.text },
  });

  // Fan out to a Novu topic.
  await ctx.trigger("incident-broadcast", {
    to: { type: "Topic", topicKey: "on-call" },
    payload: { severity: "high" },
  });
});

When the user replies on any channel Novu delivered to, the message routes back through the same bridge and your existing handlers — one agent loop for both proactive notifications and conversational replies.

Create workflows in the Novu dashboard or via the API, then reference them by workflow ID from your agent.

Connect channels

After your adapter is wired, npx novu connect --runtime chat-sdk is the fastest way to put your agent on a real channel. The CLI runs an interactive flow:

  1. Authenticate — sign in to Novu (or pass --secret-key for an existing account).
  2. Create or reuse a bridge agent — one agent per Chat SDK app.
  3. Pick a channel — Slack, Email, Telegram, WhatsApp, Microsoft Teams, or skip for now.
  4. Finish the handoff — the CLI creates the provider integration and guides you through the last step (Slack OAuth, BotFather token, send a test email, etc.).

What Novu handles for you:

ChannelWhat the CLI sets up
SlackCreates the Slack app (manifest quick-setup), stores OAuth credentials, opens the install flow
EmailProvisions a unique agent email inbox (see below)
TelegramLinks your @BotFather bot token
WhatsAppOpens the Novu dashboard to finish WhatsApp Business provider setup
Microsoft TeamsOpens the Novu dashboard to configure Azure Bot credentials and the customer admin-consent flow

Re-run npx novu connect --runtime chat-sdk to add another channel or refresh credentials. Each run creates a new agent unless you pick an existing one.

Email

Pick Email in the connect channel picker, or re-run npx novu connect --runtime chat-sdk and choose it from the menu.

Novu provisions a unique inbound address for your agent — for example my-agent-abc@agentconnect.sh. Anyone can email that address; Novu normalizes the thread and forwards it to your Chat SDK bridge. Your agent replies from the same inbox, and the conversation continues over email like any other channel.

Unlike Slack or Telegram, email starts with the user sending the first message. The CLI opens a pre-filled draft so you can send a test email and confirm the connection.

Custom domains — the shared @agentconnect.sh address works out of the box. For production, configure your own inbound domain in the Novu dashboard (Domains → add domain → verify DNS). Route mail to your agent on @yourcompany.com while keeping the same bridge handlers.

Use getNovuContext(thread).getEmailContext() inside handlers for email-specific metadata (routing domain, thread headers).

Channels

Slack · Microsoft Teams · WhatsApp · Telegram · Email — one handler set serves them all, with no per-channel code.

Examples

A complete Next.js boilerplate — live bridge route, setup UI, bridge-status panel, and a handler set covering every capability: novu-chat-sdk-example.

It ships one bot that exercises each handler. Message it on any connected channel:

MessageDemonstrates
(any text)Echo reply tagged with the originating platform
cardPosting an interactive Chat SDK card
whoamigetNovuContext() — subscriber profile + unified user identity
resolveResolving the Novu conversation from the agent

Handler coverage: onNewMention, onSubscribedMessage, onAction (button clicks), and onReaction.

Resources

Feature support

Messaging

FeatureSupported
Post message
Edit message
Delete message
File uploadsOutbound
Streaming
Scheduled messages

Rich content

FeatureSupported
Card format
Buttons
Link buttons
Select menus
Tables
Fields
Images in cards
Modals

Conversations

FeatureSupported
Slash commands
Mentions
Add reactions
Remove reactions
Typing indicator
DMsInbound only (no openDM in v1)
Ephemeral messages
User lookupgetUser, getSubscriber
Parent subject
Native clientgetNovuContext()
Custom API endpointNOVU_API_BASE_URL

Message history

FeatureSupported
Fetch messagesgetHistory
Fetch single message
Fetch thread infogetMetadata
Fetch channel messages
List threads
Fetch channel info
Post channel message