Chat with your Datalumo chatbot in Slack
If you've built a chatbot with Datalumo, it lives on your website. That's where most of your visitors meet it. But there's another place your team spends a lot of time asking the same questions: Slack.
The good news is that everything you need to run your chatbot inside Slack already exists. You don't need a new integration type, a separate content pipeline, or a different pricing plan. You just need a small Slack app that forwards messages to the Datalumo API and posts the reply back.
This post walks through how to do that with about fifty lines of code.
What you're building
A Slack bot that, when someone DMs it or mentions it in a channel, sends the question to your Datalumo chatbot integration and replies in the thread. Each Slack thread stays in its own conversation, so follow-up questions keep their context.
The moving parts:
- A chatbot integration in Datalumo, configured with your content
- A Datalumo API token
- A small Slack app with event subscriptions enabled
- A tiny server that sits between them
That's it. Your content stays in Datalumo. Slack just becomes another front door.
The API you'll be calling
Datalumo exposes a chat endpoint for every integration of type chatbot:
POST https://datalumo.app/api/v1/integrations/{integration_id}/chat
Authorization: Bearer <your-api-token>
Content-Type: application/json
{
"message": "How do I reset my password?",
"conversation_id": "optional-string",
"stream": false
}
The response looks like this:
{
"conversation_id": "conv_01h...",
"message": "To reset your password, go to..."
}
Two things to notice. First, conversation_id is optional on the way in and always returned on the way out. If you pass one back on the next request, Datalumo treats it as a follow-up in the same conversation and keeps the context. If you leave it out, you start fresh. Second, stream: false tells the API to return a single JSON response instead of a server-sent event stream. For Slack you want the JSON version, because Slack messages are posted in one shot.
The {integration_id} in the URL is the public ID of your chatbot integration. You can find it in the Datalumo dashboard on the integration's settings page.
Step 1: Get your Datalumo credentials
In your Datalumo dashboard:
- Create a chatbot integration if you don't already have one. Connect it to the collection that holds the content you want the bot to answer from.
- Go to API Tokens and generate a token. Give it a name like "Slack bot" so you can revoke it later if you need to.
- Copy the integration's public ID from its settings page.
Keep both values handy. You'll drop them into environment variables in a minute.
Step 2: Create the Slack app
Head to api.slack.com/apps and click Create New App, then From scratch. Pick a name and a workspace.
Under OAuth & Permissions, add these bot token scopes:
app_mentions:readso the bot can see when it's mentionedchat:writeso the bot can replyim:historyandim:readso the bot can handle DMsim:writeso it can send DMs
Install the app to your workspace and copy the Bot User OAuth Token. It starts with xoxb-.
Under Basic Information, copy the Signing Secret. You'll use it to verify that incoming requests really come from Slack.
Under Event Subscriptions, enable events and subscribe the bot to app_mention and message.im. You'll come back here in a minute to fill in the request URL.
Step 3: The bridge
Here's a minimal Node server using Slack's Bolt SDK. You can run it anywhere that can accept HTTPS requests: a small VM, a Fly.io machine, a Cloudflare Worker with a small adapter, or even your laptop behind ngrok while you're testing.
import bolt from "@slack/bolt";
const app = new bolt.App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
});
const DATALUMO_BASE = "https://datalumo.app/api/v1";
const INTEGRATION_ID = process.env.DATALUMO_INTEGRATION_ID;
const API_TOKEN = process.env.DATALUMO_API_TOKEN;
const conversations = new Map();
async function askDatalumo(threadKey, message) {
const conversationId = conversations.get(threadKey);
const res = await fetch(
`${DATALUMO_BASE}/integrations/${INTEGRATION_ID}/chat`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${API_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
message,
conversation_id: conversationId ?? null,
stream: false,
}),
},
);
if (!res.ok) {
throw new Error(`Datalumo API error: ${res.status}`);
}
const data = await res.json();
conversations.set(threadKey, data.conversation_id);
return data.message;
}
app.event("app_mention", async ({ event, say }) => {
const threadKey = event.thread_ts ?? event.ts;
const question = event.text.replace(/<@[^>]+>/g, "").trim();
const reply = await askDatalumo(threadKey, question);
await say({ text: reply, thread_ts: threadKey });
});
app.message(async ({ message, say }) => {
if (message.channel_type !== "im" || message.subtype) {
return;
}
const threadKey = message.thread_ts ?? message.ts;
const reply = await askDatalumo(threadKey, message.text);
await say({ text: reply, thread_ts: threadKey });
});
await app.start(process.env.PORT ?? 3000);
console.log("Slack bridge running");
That's the whole thing. Four environment variables, one in-memory map that binds Slack thread IDs to Datalumo conversation IDs, and two event handlers.
A few details worth calling out:
Thread IDs as conversation keys. For channel mentions the bot replies in a thread, so thread_ts gives you a stable key for the whole conversation. For the first message in a thread, thread_ts isn't set yet, so we fall back to ts. The same idea works for DMs.
In-memory storage is fine for a prototype. Once you want this to survive restarts or run on multiple instances, swap the Map for Redis or a database table keyed on (workspace_id, thread_key). The value is just the Datalumo conversation ID string.
Strip the mention. Slack sends the message as <@U0123456> what's our return policy?, so we strip the user tag before forwarding the question.
Bolt handles signature verification. Passing signingSecret into the Bolt app is enough. You don't need to implement HMAC checking yourself.
Step 4: Point Slack at your server
Once your server is running and reachable over HTTPS, go back to Event Subscriptions in your Slack app settings and paste in the request URL. Slack will ping it to verify; Bolt handles that automatically. Reinstall the app to your workspace if Slack asks you to.
Open a DM with the bot and say hi. You should get an answer from your Datalumo chatbot in a few seconds.
Going further
The thing I like about this pattern is that it doesn't lock you into Slack. The exact same shape works for Telegram, Discord, Microsoft Teams, or whatever chat surface your team lives in. The chat API doesn't care where the message came from. All you're doing is:
- Receive a message on platform X
- Look up (or create) the Datalumo conversation for that thread
- Call the chat endpoint
- Post the reply back on platform X
If you're thinking about building a Telegram version next, the bridge is even smaller because there's no OAuth step. You create a bot with BotFather, set a webhook URL, and reply via sendMessage.
A few ideas once the basic bridge is working:
- Slash commands. Add
/ask <question>alongside mentions so people don't need to tag the bot. - Source links. The chat endpoint returns matched content; surface those links in the Slack reply so your team can dig deeper.
- Scoping. Run different Datalumo integrations per Slack channel. The engineering channel talks to an engineering chatbot, support talks to a support chatbot.
- Feedback. Add thumbs-up and thumbs-down buttons to each reply and push the signal back to Datalumo's events endpoint to track answer quality.
The point
Datalumo is a chatbot for your content. Where that chatbot shows up is your choice. The website widget is the default path because it's the one most people want, but the API is the real product. If your team lives in Slack, meet them there.
If you build something with this, I'd love to see it. Sign up here if you haven't yet, or tell me about it on X.