Slack contains years of institutional knowledge buried in channels. Graphlit turns Slack history into a searchable knowledge base—find answers, extract insights, and build RAG chatbots that know what your team knows.
What You'll Learn
- Slack OAuth setup (bot tokens, scopes)
- Syncing channels, messages, threads
- Entity extraction from conversations
- Searching Slack history semantically
- Building Slack-powered RAG
- Production patterns for multi-channel feeds
Part 1: Slack OAuth Setup
Step 1: Create Slack App
- Go to api.slack.com/apps
- Click "Create New App" → "From scratch"
- Name it (e.g., "Graphlit Knowledge Base")
- Select your workspace
Step 2: Add OAuth Scopes
Navigate to "OAuth & Permissions" and add:
Required scopes:
channels:read- List public channelschannels:history- Read public messagesusers:read- Get user info
Optional (for private channels):
groups:read- List private channelsgroups:history- Read private messages
Step 3: Install App
- Click "Install to Workspace"
- Authorize the app
- Copy Bot User OAuth Token (starts with
xoxb-)
// Save token securely
const SLACK_BOT_TOKEN = 'xoxb-your-token-here';
Part 2: Listing Available Channels
Before creating a feed, query available channels:
import { Graphlit } from 'graphlit-client';
const graphlit = new Graphlit();
// List channels
const channels = await graphlit.querySlackChannels({
token: SLACK_BOT_TOKEN
});
console.log('Available channels:');
channels.slackChannels.forEach(channel => {
console.log(` #${channel.name} (${channel.id})`);
});
Example output:
Available channels:
#general (C01234ABCDE)
#engineering (C56789FGHIJ)
#product (C98765KLMNO)
Part 3: Creating Slack Feed
Basic Feed (Single Channel)
import { FeedServiceTypes } from 'graphlit-client/dist/generated/graphql-types';
// Create feed for #engineering channel
const feed = await graphlit.createFeed({
name: 'Engineering Slack',
type: FeedServiceTypes.Slack,
slack: {
token: SLACK_BOT_TOKEN,
channels: ['engineering'], // Channel names
readMessages: true,
readThreads: true,
readLimit: 500 // Last 500 messages
}
});
console.log('Feed created:', feed.createFeed.id);
// Wait for initial sync
let isDone = false;
while (!isDone) {
const status = await graphlit.isFeedDone(feed.createFeed.id);
isDone = status.isFeedDone.result;
if (!isDone) {
console.log('Syncing Slack history...');
await new Promise(r => setTimeout(r, 10000));
}
}
console.log('✓ Slack history synced!');
Multi-Channel Feed
// Sync multiple channels
const multiChannelFeed = await graphlit.createFeed({
name: 'All Engineering Channels',
type: FeedServiceTypes.Slack,
slack: {
token: SLACK_BOT_TOKEN,
channels: ['engineering', 'backend', 'frontend', 'devops'],
readMessages: true,
readThreads: true,
readLimit: 1000
}
});
Feed with Entity Extraction
import {
FilePreparationServiceTypes,
EntityExtractionServiceTypes,
ObservableTypes
} from 'graphlit-client/dist/generated/graphql-types';
// Create workflow for entity extraction
const workflow = await graphlit.createWorkflow({
name: "Slack Entities",
preparation: {
jobs: [{
connector: {
type: FilePreparationServiceTypes.Message
}
}]
},
extraction: {
jobs: [{
connector: {
type: EntityExtractionServiceTypes.ModelText,
extractedTypes: [
ObservableTypes.Person, // @mentions
ObservableTypes.Organization, // Company names
ObservableTypes.Product, // Products discussed
ObservableTypes.Event // Meetings, launches
]
}
}]
}
});
// Create feed with workflow
const feedWithEntities = await graphlit.createFeed({
name: 'Slack with Entities',
type: FeedServiceTypes.Slack,
slack: {
token: SLACK_BOT_TOKEN,
channels: ['engineering'],
readMessages: true,
readThreads: true,
readLimit: 500
},
workflow: { id: workflow.createWorkflow.id }
});
What gets extracted:
- People mentioned (@alice, @bob)
- Companies discussed (Acme Corp, OpenAI)
- Products/projects (Project Phoenix, Dashboard v2)
- Events (Q4 Launch, All-Hands)
Part 4: Searching Slack History
Basic Search
import { ContentTypes } from 'graphlit-client/dist/generated/graphql-types';
// Search Slack messages
const results = await graphlit.queryContents({
search: 'API rate limiting',
filter: {
types: [ContentTypes.Message] // Only Slack messages
}
});
console.log(`Found ${results.contents.results.length} messages`);
results.contents.results.forEach(msg => {
console.log(`\n${msg.message?.author}: ${msg.message?.message}`);
console.log(` Channel: #${msg.message?.channelName}`);
console.log(` Date: ${msg.message?.timestamp}`);
});
Example output:
Found 8 messages
alice: We're hitting rate limits on the GitHub API. Should we add exponential backoff?
Channel: #engineering
Date: 2024-01-15T10:30:00Z
bob: I implemented rate limiting in the API service last week. Check PR #234.
Channel: #backend
Date: 2024-01-16T14:20:00Z
Search with Time Filters
// Messages from last 30 days
const recent = await graphlit.queryContents({
search: 'deployment',
filter: {
types: [ContentTypes.Message],
creationDateRange: {
from: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()
}
}
});
Search by Channel
// Search only in #engineering
const engineeringMsgs = await graphlit.queryContents({
search: 'kubernetes',
filter: {
types: [ContentTypes.Message],
// Note: Channel filtering requires custom metadata filtering
}
});
Part 5: Building Slack RAG
Chat with Slack History
// Create conversation
const conversation = await graphlit.createConversation('Slack Chat');
// Ask questions about Slack history
const response = await graphlit.promptConversation(
'What did the team discuss about API rate limiting?',
conversation.createConversation.id
);
const answer = response.promptConversation?.message?.message;
const citations = response.promptConversation?.message?.citations;
console.log('Answer:', answer);
console.log('\nSources:');
citations?.forEach((citation, i) => {
console.log(`[${i + 1}] ${citation.content?.name} in #${citation.content?.message?.channelName}`);
});
Example answer:
Answer: The team discussed API rate limiting on January 15-16. Alice raised concerns about hitting GitHub API limits and suggested implementing exponential backoff. Bob mentioned he had already implemented rate limiting in the API service (PR #234), and Carol added that they should also consider request batching for efficiency.
Sources:
[1] Slack message in #engineering
[2] Slack message in #backend
[3] Slack message in #engineering
Entity-Filtered Slack Search
// Find Alice's entity
const aliceEntity = await graphlit.queryObservables({
filter: {
searchText: "Alice",
types: [ObservableTypes.Person]
}
});
const aliceId = aliceEntity.observables?.results?.[0]?.observable.id;
// Chat filtered to Alice's messages
const aliceChat = await graphlit.promptConversation(
'What is Alice working on?',
conversationId,
undefined,
undefined,
{
observations: {
observables: [{ id: aliceId }]
}
}
);
Part 6: Production Patterns
Pattern 1: Continuous Sync
Feeds continuously monitor for new messages:
// Feed runs indefinitely
const feed = await graphlit.createFeed({
name: 'Live Slack Feed',
type: FeedServiceTypes.Slack,
slack: {
token: SLACK_BOT_TOKEN,
channels: ['engineering'],
readMessages: true,
readThreads: true,
readLimit: 100 // Only last 100 on initial sync
},
schedulePolicy: {
recurrenceType: 'HOURLY', // Check every hour
interval: 1
}
});
// New messages appear automatically
Pattern 2: Multi-Workspace
// Separate feeds per workspace
const workspaces = [
{ name: 'Engineering', token: ENGINEERING_TOKEN, channels: ['general', 'backend'] },
{ name: 'Product', token: PRODUCT_TOKEN, channels: ['general', 'roadmap'] }
];
for (const workspace of workspaces) {
const feed = await graphlit.createFeed({
name: `${workspace.name} Slack`,
type: FeedServiceTypes.Slack,
slack: {
token: workspace.token,
channels: workspace.channels,
readMessages: true,
readThreads: true
}
});
console.log(`Created feed for ${workspace.name}`);
}
Pattern 3: Slack Bot Integration
import { App } from '@slack/bolt';
const slackApp = new App({
token: SLACK_BOT_TOKEN,
signingSecret: SLACK_SIGNING_SECRET
});
// Answer questions from Slack
slackApp.message(/^@bot (.+)/, async ({ message, say }) => {
const question = message.text.replace(/^@bot /, '');
// Query Graphlit
const response = await graphlit.promptConversation(question, conversationId);
// Reply in Slack
await say({
text: response.promptConversation?.message?.message,
thread_ts: message.ts // Reply in thread
});
});
Common Issues & Solutions
Issue: No Messages Synced
Problem: Feed created but no content appears.
Solutions:
- Check OAuth scopes:
// Verify token has required scopes
const test = await graphlit.querySlackChannels({ token: SLACK_BOT_TOKEN });
console.log('Channels accessible:', test.slackChannels.length);
- Verify bot is in channels:
# Bot must be invited to private channels
/invite @YourBot
Issue: Missing Threads
Problem: Only top-level messages synced, replies missing.
Solution: Enable readThreads:
slack: {
readThreads: true // Must be true
}
Issue: Slow Initial Sync
Problem: 10,000 messages takes 30+ minutes.
Solutions:
- Lower
readLimitfor initial sync:
readLimit: 500 // Not 10000
- Use webhooks instead of polling:
webhookUrl: 'https://yourapp.com/webhooks/graphlit'
What's Next?
You now have a searchable Slack knowledge base. Next steps:
- Build Slack bot that answers questions
- Extract entities to find who's working on what
- Create dashboards showing team activity
Related guides:
- Data Connectors - Other messaging connectors
- Building AI Chat Applications - RAG patterns
- Building Knowledge Graphs - Entity extraction
Happy Slacking! 💬