support-agent.tsx until the bot greets users, routes by topic, answers with an LLM, and resolves the thread when the user is done.
This page keeps each setup step to the minimum you need to get going. For the full reference on any step, follow the linked pages under Set up your agent.
What you’re building
In this tutorial, you build a Pipeliner support bot that:- Greets the user and asks whether their issue is a Billing question, a Technical issue, or Something else
- Stores the user’s choice and answers follow-up questions with an LLM
- Closes the conversation when the user confirms the issue is resolved
onMessage, onAction, metadata, LLM replies, and ctx.resolve().
Before you start
You need:- A Novu account.
- Node.js 18+ installed.
- A chat provider you can connect. This tutorial uses Slack.
Set up the project
These three steps get you from an empty folder to a running agent. Each one links to a more detailed page if you want the full walkthrough.Create the agent and connect a provider
In the Novu dashboard, open Agents and click Create agent. Set the Identifier tosupport-agent, since that value must match the agent id you use in code.On the guided setup page, open Select provider, choose Slack, and follow the prompts to create and install the Slack app. When it finishes, you get a welcome message from the agent in Slack.For the full provider flow (Slack tokens, install, permissions), see the Quickstart and Create an agent.Scaffold the bridge app
The bridge app is the project that receives events from Novu and runs your handler code. Copy the pre-filled command from the agent setup page, or run:Run it locally
On the agent detail page, set the bridge to Local. Then, from the project directory, start the app:Where the code goes
The scaffold creates a Next.js bridge app. All tutorial code goes inapp/novu/agents/support-agent.tsx:
app/api/novu/route.ts, which exposes your agents over HTTP. You do not need to change that file for this tutorial. Make sure index.ts re-exports your agent so the route picks it up; the scaffold wires this up for the starter agent already.
Everything below happens inside support-agent.tsx.
Build the agent
Follow the steps below to add handlers, cards, metadata, an LLM, and conversation resolution tosupport-agent.tsx. For the API reference behind each step, see Handle events, Reply, and Signals.
Define the agent shell
Start with the bare minimum: anagent() call with an id and an onMessage handler. The agent id (support-agent) must match the identifier you set in the Novu dashboard.Replace the contents of support-agent.tsx with the following echo handler:app/novu/agents/support-agent.tsx
/** @jsxImportSource @novu/framework */ pragma at the top of the file so you can return JSX cards in later steps. If you only return strings, you can omit it.At this point the agent echoes messages back. In the next step, replace that behavior with a welcome card.Handle the first message
Replace the echo handler with a welcome card. On the first message, the bot introduces itself and asks the user to pick a topic.Usectx.conversation.messageCount to detect the first turn. When the count is 1, return a welcome card with three topic buttons:ctx.subscribercarries user profile data for personalized greetings.- Returning JSX is shorthand for
await ctx.reply(...). - Each
Buttonhas anidandvalueused inonAction.
Use metadata for context
When the user clicks a button,onAction fires instead of onMessage. Add an onAction handler that stores the user’s topic choice in ctx.metadata so the next turn can read it.ctx.metadata.get('topic') on the next message. To alert on-call for technical issues, use ctx.trigger. For details, see Trigger a workflow.Answer follow-ups with an LLM
After the welcome card, plug in a model. This example uses the Vercel AI SDK with OpenAI.Install the SDK and set your API key:onMessage, after the welcome-card branch, add LLM generation:ctx.historymaps directly to SDK message format.- For files in replies, use
ctx.replywith thefilesoption. For details, see Sending attachments.
Resolve the conversation
When the user confirms the issue is fixed, callctx.resolve(). Add this check inside onMessage before the LLM branch:Try it out
Withnpm run dev:novu still running, message your agent in Slack to walk the full flow:
- Send any message. The bot replies with the welcome card and three topic buttons.
- Click Billing question. The bot confirms the topic and asks for details.
- Ask a follow-up question. The LLM answers using the stored topic and conversation history.
- Reply with thanks. The bot resolves the conversation and closes the thread.
Complete agent
The following file combines all five steps:app/novu/agents/support-agent.tsx
How the pieces fit together
onMessage: every user text message; branch on turn and content.onAction: button clicks and dropdown selections from cards.ctx.metadata: conversation scratchpad across turns.ctx.history: transcript for LLM context.ctx.reply(or return value) - strings, markdown, cards, or files.ctx.trigger: fire Novu workflows (email, escalation, CSAT).ctx.resolve: end the conversation.
Next steps
Give the model real context
Add RAG or tool calls so the model can query your API or docs.
Capture reactions
Add
onReaction for thumbs-up/down feedback.Send a CSAT email
Use
ctx.trigger after resolution for a follow-up survey workflow.Build richer cards
Dropdowns, links, text inputs, and multi-action cards.
Going to production
Run locally, deploy to development, and publish to production.