Skip to content

How to Connect a Discord Bot to Usable


How to Connect a Discord Bot to Usable

Category: Integrations | Difficulty: Intermediate | Last Updated: 2025-10-16

Overview

Learn how to build a Discord bot that automatically creates Usable memory fragments from Discord forum posts. This integration enables seamless knowledge management, turning Discord discussions into searchable, organized documentation.

What You’ll Learn

  • Set up a Discord bot application with proper permissions
  • Integrate Usable’s API for fragment creation
  • Listen for Discord forum thread events
  • Format Discord messages for Usable storage
  • Deploy the bot to production

Prerequisites

  • Basic TypeScript/JavaScript knowledge
  • Discord account with server admin access
  • Usable account with API access
  • Node.js or Bun installed
  • Understanding of REST APIs

Estimated Time

⏱️ 30-45 minutes to complete


Concepts

Discord Bot Architecture

A Discord bot is a program that:

  1. Connects to Discord’s Gateway (WebSocket connection)
  2. Listens for events (messages, threads, reactions)
  3. Responds to events by performing actions
  4. Runs continuously (24/7)

Usable Memory Fragments

Memory fragments are the core knowledge unit in Usable:

  • Title: Brief description
  • Content: Full text with markdown formatting
  • Tags: For organization and search
  • Type: Classification (issue, knowledge, recipe, etc.)
  • Workspace: Where the fragment is stored

Integration Flow

sequenceDiagram
participant User
participant Discord
participant Bot
participant Usable
User->>Discord: Create forum post
Discord->>Bot: threadCreate event
Bot->>Bot: Format message
Bot->>Usable: POST /memory-fragments
Usable-->>Bot: Fragment ID
Bot->>Discord: Reply with confirmation
Discord-->>User: See bot reply

Key Terminology

TermDefinition
GuildDiscord server (API terminology)
ThreadDiscussion within a forum channel
GatewayWebSocket connection to Discord
FragmentKnowledge unit in Usable
OAuth2Discord’s authorization system
IntentPermission to receive specific events

Tutorial / How-To

Step 1: Create Discord Bot Application

  1. Go to Discord Developer Portal
  2. Click “New Application”
  3. Name it “Usable Bot” (or your preference)
  4. Click “Create”

What this does: Creates your bot application container that holds credentials and settings.

Step 2: Configure Bot Settings

  1. Navigate to the “Bot” section in left sidebar
  2. Click “Add Bot” → “Yes, do it!”
  3. Under “Token”, click “Reset Token” and copy it (save for later)
  4. Enable Privileged Gateway Intents:
    • ☑️ Message Content Intent (required)
    • ☑️ Server Members Intent (optional)
  5. Click “Save Changes”

Why this matters: Message Content Intent allows your bot to read message text, which is essential for creating fragments.

Step 3: Generate Bot Invite URL

  1. Go to “OAuth2” → “URL Generator”
  2. Select Scopes:
    • ☑️ bot
  3. Select Bot Permissions:
    • ☑️ View Channels
    • ☑️ Send Messages
    • ☑️ Send Messages in Threads
    • ☑️ Read Message History
  4. Copy the Generated URL at the bottom
  5. Open URL in browser and add bot to your test server

Expected result: Bot appears in your server’s member list (offline until you run the code).

Step 4: Get Usable API Credentials

  1. Log in to Usable
  2. Navigate to Settings → API Keys
  3. Click “Generate New Key”
  4. Copy the API key (starts with usa_key_...)
  5. Copy your Workspace ID from workspace settings

Security note: Never commit API keys to git. Use environment variables.

Step 5: Set Up Project

Create project directory:

Terminal window
mkdir usable-discord-bot
cd usable-discord-bot

Initialize with Bun (recommended):

Terminal window
bun init -y
bun add discord.js axios zod
bun add -d @types/node typescript

Or with npm:

Terminal window
npm init -y
npm install discord.js axios zod
npm install -D @types/node typescript

Step 6: Create Environment Configuration

Create .env file:

Terminal window
# Discord Configuration
DISCORD_BOT_TOKEN=your_bot_token_here
DISCORD_CLIENT_ID=your_client_id_here
# Usable API Configuration
USABLE_API_URL=https://api.usable.dev/api
USABLE_API_KEY=your_usable_api_key_here
USABLE_WORKSPACE_ID=your_workspace_id_here
USABLE_ISSUE_FRAGMENT_TYPE_ID=your_fragment_type_id_here
# Bot Configuration
NODE_ENV=development
LOG_LEVEL=info

Get Fragment Type ID:

  1. In Usable, go to workspace settings
  2. Navigate to Fragment Types
  3. Copy the UUID for “Issue” type

Step 7: Implement Usable API Client

Create src/services/usable-api.service.ts:

import axios, { AxiosInstance } from 'axios';
export class UsableApiService {
private client: AxiosInstance;
constructor() {
this.client = axios.create({
baseURL: process.env.USABLE_API_URL,
headers: {
Authorization: `Bearer ${process.env.USABLE_API_KEY}`,
'Content-Type': 'application/json',
},
});
}
async createFragment(data: {
title: string;
content: string;
workspaceId: string;
fragmentTypeId: string;
tags?: string[];
}) {
const response = await this.client.post('/memory-fragments', data);
return response.data;
}
}
export const usableApi = new UsableApiService();

Explanation:

  • Line 6-12: Initialize axios client with Usable API URL and auth header
  • Line 14-22: Create fragment method that POSTs to Usable API
  • Line 25: Export singleton instance for easy imports

Step 8: Create Discord Bot

Create src/bot.ts:

import { Client, GatewayIntentBits, Events } from 'discord.js';
import { usableApi } from './services/usable-api.service';
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
// Bot ready event
client.once(Events.ClientReady, (c) => {
console.log(`✅ Bot is ready! Logged in as ${c.user.tag}`);
});
// Listen for new threads in forum channels
client.on(Events.ThreadCreate, async (thread) => {
// Only process forum threads
if (!thread.parent || thread.parent.type !== 15) return;
try {
// Fetch starter message
const starterMessage = await thread.fetchStarterMessage();
// Create Usable fragment
const fragment = await usableApi.createFragment({
title: thread.name,
content: `## Discord Forum Post\n\n**Author:** ${starterMessage.author.username}\n**Server:** ${thread.guild.name}\n**Channel:** ${thread.parent.name}\n\n---\n\n${starterMessage.content}`,
workspaceId: process.env.USABLE_WORKSPACE_ID!,
fragmentTypeId: process.env.USABLE_ISSUE_FRAGMENT_TYPE_ID!,
tags: ['discord', 'forum-post', `server:${thread.guild.name}`],
});
// Reply to thread
await thread.send(
`✅ **Issue registered in Usable!**\n📝 Fragment ID: \`${fragment.fragmentId}\`\n📌 Title: ${fragment.title}`
);
} catch (error) {
console.error('Error:', error);
await thread.send('❌ Failed to register issue in Usable.');
}
});
// Login to Discord
client.login(process.env.DISCORD_BOT_TOKEN);

Explanation:

  • Line 4-10: Initialize Discord client with required intents
  • Line 17-43: Listen for forum thread creation events
  • Line 19: Filter to only forum channels (type 15)
  • Line 23: Fetch the first message in the thread
  • Line 26-32: Create Usable fragment with formatted content
  • Line 35-37: Reply to thread with confirmation

Step 9: Run the Bot

Development:

Terminal window
bun run src/bot.ts

Expected output:

✅ Bot is ready! Logged in as Usable Bot#1234

Step 10: Test the Integration

  1. Go to your Discord server
  2. Create a Forum Channel if you don’t have one
  3. Create a new Forum Post:
    • Title: “Test: Integration Works”
    • Message: “Testing Usable integration!”
  4. Wait 2-3 seconds
  5. Bot should reply with fragment ID

Expected bot reply:

✅ Issue registered in Usable!
📝 Fragment ID: `abc123-def456-...`
📌 Title: Test: Integration Works
  1. Go to Usable and search for the fragment
  2. Verify it contains the Discord context

Code Examples

Basic Example

Scenario: Minimal bot that creates fragments from forum posts

import { Client, GatewayIntentBits } from 'discord.js';
import axios from 'axios';
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.MessageContent,
],
});
client.on('threadCreate', async (thread) => {
if (thread.parent?.type === 15) { // Forum channel
const msg = await thread.fetchStarterMessage();
await axios.post(
'https://api.usable.dev/api/memory-fragments',
{
title: thread.name,
content: msg.content,
workspaceId: 'your-workspace-id',
fragmentTypeId: 'your-fragment-type-id',
},
{
headers: {
Authorization: `Bearer ${process.env.USABLE_API_KEY}`,
},
}
);
}
});
client.login(process.env.DISCORD_BOT_TOKEN);

Advanced Example with Error Handling

Scenario: Production-ready implementation with retry logic

import { Client, ThreadChannel } from 'discord.js';
import axios, { AxiosError } from 'axios';
async function createFragmentWithRetry(
data: FragmentData,
retries = 3
): Promise<Fragment> {
for (let i = 0; i < retries; i++) {
try {
const response = await axios.post(
'https://api.usable.dev/api/memory-fragments',
data,
{
headers: {
Authorization: `Bearer ${process.env.USABLE_API_KEY}`,
},
}
);
return response.data;
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
client.on('threadCreate', async (thread: ThreadChannel) => {
try {
const fragment = await createFragmentWithRetry({
title: thread.name,
content: await formatThreadContent(thread),
workspaceId: process.env.USABLE_WORKSPACE_ID!,
fragmentTypeId: process.env.USABLE_ISSUE_FRAGMENT_TYPE_ID!,
});
await thread.send(`✅ Created: ${fragment.fragmentId}`);
} catch (error) {
console.error('Failed after retries:', error);
await thread.send('❌ Error creating fragment');
}
});

Common Patterns

Pattern 1: Rich Metadata Formatting

Use when: You want to preserve Discord context in fragments

function formatThreadContent(thread: ThreadChannel, message: Message): string {
return `## Discord Thread Message
**Server:** ${thread.guild.name}
**Channel:** ${thread.parent?.name}
**Thread:** ${thread.name}
**Author:** ${message.author.username}
**Posted:** ${message.createdAt.toISOString()}
---
${message.content}`;
}

Benefits:

  • Full context preserved
  • Easy to search in Usable
  • Clear formatting

Pattern 2: Tag Generation

Use when: You want automatic, consistent tagging

function generateTags(thread: ThreadChannel): string[] {
const tags = ['discord', 'forum-post'];
if (thread.guild.name) {
tags.push(`server:${thread.guild.name.toLowerCase().replace(/\s+/g, '-')}`);
}
if (thread.parent?.name) {
tags.push(`channel:${thread.parent.name.toLowerCase().replace(/\s+/g, '-')}`);
}
return tags;
}

Configuration

Required Environment Variables

VariableTypeDescription
DISCORD_BOT_TOKENstringBot authentication token from Discord
USABLE_API_KEYstringAPI key from Usable settings
USABLE_WORKSPACE_IDstringTarget workspace UUID
USABLE_ISSUE_FRAGMENT_TYPE_IDstringFragment type for issues

Optional Variables

VariableTypeDefaultDescription
USABLE_API_URLstringhttps://api.usable.dev/apiUsable API endpoint
LOG_LEVELstringinfoLogging verbosity
NODE_ENVstringdevelopmentEnvironment mode

Diagrams

Architecture Diagram

graph TD
A[Discord User] -->|Create Post| B[Discord Forum]
B -->|threadCreate Event| C[Discord Gateway]
C -->|WebSocket| D[Discord Bot]
D -->|Format Data| E[Usable API Client]
E -->|POST /memory-fragments| F[Usable API]
F -->|Fragment Created| E
E -->|Fragment ID| D
D -->|Send Message| B
B -->|Bot Reply| A

Event Flow

sequenceDiagram
participant User
participant Discord
participant Bot
participant Usable
User->>Discord: Create forum thread
Discord->>Bot: ThreadCreate event
Bot->>Discord: Fetch starter message
Discord-->>Bot: Message content
Bot->>Bot: Format for Usable
Bot->>Usable: Create fragment
Usable-->>Bot: Fragment ID
Bot->>Discord: Reply to thread
Discord-->>User: See confirmation

Best Practices

✅ Do’s

  • Use environment variables for all secrets
  • Validate data before sending to Usable
  • Include error handling for network failures
  • Log events for debugging and monitoring
  • Format content with Discord context
  • Test locally before deploying

❌ Don’ts

  • Don’t commit .env files or secrets
  • Don’t skip Message Content Intent
  • Avoid blocking the main thread with sync operations
  • Don’t ignore rate limits
  • Never hardcode API keys in source code

💡 Tips

  • Use TypeScript for type safety
  • Implement retry logic for API calls
  • Add health checks for monitoring
  • Store thread-to-fragment mapping in a database for updates
  • Use structured logging for production

Troubleshooting

Issue: Bot doesn’t respond to forum posts

Symptoms:

  • Bot shows as online
  • No reply when creating forum threads
  • No errors in console

Cause: Missing Message Content Intent or wrong channel type

Solution:

  1. Check Discord Developer Portal → Bot → Privileged Gateway Intents
  2. Ensure “Message Content Intent” is enabled
  3. Verify forum channel type in code:
if (thread.parent?.type !== 15) {
console.log('Not a forum channel, skipping');
return;
}

Prevention: Always enable required intents before launching bot

Issue: Usable API returns 401 Unauthorized

Symptoms:

  • Error: “Request failed with status code 401”
  • Fragment not created
  • Bot sends error message to Discord

Cause: Invalid or expired API key

Solution:

Terminal window
# Regenerate API key in Usable
# Update .env file
USABLE_API_KEY=usa_key_new_key_here
# Restart bot

Prevention: Use environment validation on startup

Issue: Fragment created but missing content

Symptoms:

  • Fragment exists in Usable
  • Content field is empty or null
  • Title is correct

Cause: Starter message fetch failed or returned null

Solution:

const starterMessage = await thread.fetchStarterMessage();
if (!starterMessage) {
console.error('Could not fetch starter message');
return;
}

Prevention: Always validate fetched data before using it


Performance Considerations

Optimization Tips

  • Batch operations if processing multiple threads
  • Cache fragment type IDs to reduce API calls
  • Use connection pooling for HTTP requests
  • Implement rate limiting to avoid Discord/Usable API limits
  • Queue long-running tasks instead of blocking

Rate Limits

ServiceLimitNotes
Discord Gateway120 events/minPer bot connection
Discord API50 requests/secGlobal rate limit
Usable API100 requests/minPer API key

Real-World Examples

Example 1: Support Forum Integration

Scenario: Customer support team uses Discord forum for issues

Implementation:

client.on('threadCreate', async (thread) => {
if (thread.parent?.name === 'customer-support') {
const fragment = await usableApi.createFragment({
title: `[Support] ${thread.name}`,
content: formatSupportTicket(thread),
workspaceId: process.env.USABLE_WORKSPACE_ID!,
fragmentTypeId: process.env.USABLE_ISSUE_FRAGMENT_TYPE_ID!,
tags: ['support', 'customer', thread.guild.name],
});
await thread.send(
`🎫 Support ticket created!\nTicket ID: ${fragment.fragmentId}`
);
}
});

Outcome: All support requests automatically tracked in Usable, searchable by team

Example 2: Feature Request Collection

Scenario: Community submits feature requests via Discord

Implementation:

client.on('threadCreate', async (thread) => {
if (thread.appliedTags?.includes('feature-request')) {
await usableApi.createFragment({
title: thread.name,
content: formatFeatureRequest(thread),
fragmentTypeId: process.env.USABLE_FEATURE_FRAGMENT_TYPE_ID!,
tags: ['feature-request', 'community', 'discord'],
});
}
});

Outcome: Product team has organized list of community feature requests


Documentation

Tutorials

External Resources


FAQs

Q: Can I update fragments when threads receive replies?

A: Yes! Store thread-to-fragment mapping in a database, then listen for messageCreate events in threads and update the corresponding fragment.

Q: What fragment types should I use?

A:

  • Support tickets → “Issue” type
  • Feature requests → “Feature Request” type
  • Documentation → “Knowledge” type
  • Tutorials → “Recipe” type

Q: How do I handle rate limits?

A: Implement a queue system with exponential backoff. Libraries like bottleneck or p-queue can help manage rate limits automatically.

Q: Can I use this with Slack instead?

A: Yes! The Usable API integration part is the same. You’d just replace the Discord bot code with Slack’s Bolt framework.


Changelog

Version 1.0.0 (2025-10-16)

  • Initial documentation
  • Complete tutorial from setup to deployment
  • Code examples for common patterns
  • Troubleshooting guide
  • Production best practices

Contributing

Found an issue or want to improve this documentation?

  • Report issues in the Usable Public workspace
  • Suggest improvements via Discord community
  • Submit examples of your integration

Footer Note: This documentation is part of the Usable Public workspace and is automatically synced to the docs site.