Slack Integration
Get ticket notifications in Slack, create tickets from messages, and reply to customers without leaving Slack.
Overview
Dispatch Tickets doesn't have a native Slack app (yet), but you can build powerful Slack integrations using webhooks and the API. Common patterns:
Notifications
Post new tickets to a Slack channel
Create from Slack
Turn Slack messages into tickets
Two-Way Sync
Reply in Slack, update the ticket
Slack Connect
Support customers in shared channels
Slack Notifications
The simplest integration: post a message to Slack when a ticket is created. This keeps your team aware of new tickets without checking the dashboard.
Step 1: Create a Slack Incoming Webhook
- Go to api.slack.com/apps
- Create a new app (or use an existing one)
- Go to Incoming Webhooks and enable them
- Click Add New Webhook to Workspace
- Select the channel (e.g., #support-tickets)
- Copy the webhook URL
Step 2: Create a Dispatch Webhook
Set up a webhook in Dispatch Tickets that triggers your notification endpoint:
const webhook = await dispatch.webhooks.create('br_abc123', {
url: 'https://your-server.com/webhooks/dispatch-to-slack',
events: ['ticket.created'],
});Step 3: Handle the Webhook
import express from 'express';
const app = express();
const SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL;
app.post('/webhooks/dispatch-to-slack', express.json(), async (req, res) => {
const event = req.body;
if (event.type === 'ticket.created') {
const { ticket } = event.data;
// Post to Slack
await fetch(SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*New Ticket:* <https://app.dispatchtickets.com/tickets/${ticket.id}|${ticket.ticketNumber}>\n*From:* ${ticket.customer.email}\n*Subject:* ${ticket.title}`
}
},
{
type: 'context',
elements: [
{
type: 'mrkdwn',
text: `Source: ${ticket.source} | Priority: ${ticket.priority}`
}
]
}
]
})
});
}
res.status(200).send('OK');
});Result
Dispatch Tickets 12:34 PM
New Ticket: TKT-1042
From: [email protected]
Subject: Need help with my order
Source: email | Priority: normal
Create Tickets from Slack
Let your team turn Slack messages into tickets using a message shortcut or emoji reaction.
Using Message Shortcuts
Create a Slack app with a message shortcut that opens a modal to confirm ticket creation:
// Slack sends this when user clicks the shortcut
app.post('/slack/shortcuts', async (req, res) => {
const payload = JSON.parse(req.body.payload);
if (payload.type === 'message_action' && payload.callback_id === 'create_ticket') {
const message = payload.message;
// Create ticket from message
const ticket = await dispatch.tickets.create('br_abc123', {
title: message.text.substring(0, 100),
body: message.text,
source: 'slack',
sourceId: message.ts,
sourceData: {
channelId: payload.channel.id,
userId: payload.user.id,
permalink: payload.message_ts
},
customerEmail: await getEmailFromSlackUser(payload.user.id),
});
// Respond to Slack
await fetch(payload.response_url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `Ticket created: ${ticket.ticketNumber}`,
response_type: 'ephemeral'
})
});
}
res.status(200).send();
});Using Emoji Reactions
Create a ticket when someone adds a specific emoji (e.g., :ticket:) to a message:
// Subscribe to reaction_added events in your Slack app
app.post('/slack/events', async (req, res) => {
const { event } = req.body;
if (event.type === 'reaction_added' && event.reaction === 'ticket') {
// Get the original message
const result = await slackClient.conversations.history({
channel: event.item.channel,
latest: event.item.ts,
inclusive: true,
limit: 1
});
const message = result.messages[0];
// Create ticket
const ticket = await dispatch.tickets.create('br_abc123', {
title: message.text.substring(0, 100),
body: message.text,
source: 'slack',
sourceId: event.item.ts,
});
// Reply in thread
await slackClient.chat.postMessage({
channel: event.item.channel,
thread_ts: event.item.ts,
text: `Ticket created: ${ticket.ticketNumber}`
});
}
res.status(200).send();
});Two-Way Sync
The most powerful pattern: sync ticket conversations with Slack threads. Team members can reply in Slack, and customers get the response.
Architecture
Customer emails → Ticket created → Posted to Slack thread
↓
Agent replies in Slack thread
↓
Your server catches Slack event → Creates comment via API
↓
Customer receives email reply
Store Thread Mapping
You'll need to track which Slack thread corresponds to which ticket:
// Store the mapping between tickets and Slack threads
model TicketSlackThread {
id String @id
ticketId String @unique
channelId String
threadTs String
@@index([channelId, threadTs])
}Post Ticket to Slack
async function onTicketCreated(ticket) {
// Post to Slack
const result = await slackClient.chat.postMessage({
channel: SUPPORT_CHANNEL_ID,
text: `New ticket from ${ticket.customer.email}`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*${ticket.ticketNumber}:* ${ticket.title}\n\n${ticket.body}`
}
},
{
type: 'context',
elements: [
{ type: 'mrkdwn', text: `From: ${ticket.customer.email}` }
]
}
]
});
// Store the mapping
await db.ticketSlackThread.create({
data: {
ticketId: ticket.id,
channelId: SUPPORT_CHANNEL_ID,
threadTs: result.ts
}
});
}Sync Slack Replies to Tickets
app.post('/slack/events', async (req, res) => {
const { event } = req.body;
// Handle replies in ticket threads
if (event.type === 'message' && event.thread_ts) {
// Find the ticket for this thread
const mapping = await db.ticketSlackThread.findFirst({
where: {
channelId: event.channel,
threadTs: event.thread_ts
}
});
if (mapping) {
// Get Slack user info
const userInfo = await slackClient.users.info({ user: event.user });
// Add comment to ticket
await dispatch.tickets.addComment(BRAND_ID, mapping.ticketId, {
body: event.text,
authorType: 'STAFF',
authorName: userInfo.user.real_name,
source: 'slack',
sourceId: event.ts
});
}
}
res.status(200).send();
});Slash Commands
Add a slash command like /ticket to quickly create tickets or look up ticket status.
app.post('/slack/commands', async (req, res) => {
const { command, text, user_id, channel_id } = req.body;
if (command === '/ticket') {
const [action, ...args] = text.split(' ');
switch (action) {
case 'create':
// Open modal to create ticket
await slackClient.views.open({
trigger_id: req.body.trigger_id,
view: createTicketModal()
});
break;
case 'lookup':
// Look up ticket by number
const ticketNumber = args[0];
const ticket = await dispatch.tickets.getByNumber(BRAND_ID, ticketNumber);
if (ticket) {
res.json({
response_type: 'ephemeral',
text: `*${ticket.ticketNumber}:* ${ticket.title}\nStatus: ${ticket.status}\nCustomer: ${ticket.customer.email}`
});
} else {
res.json({ response_type: 'ephemeral', text: 'Ticket not found' });
}
break;
default:
res.json({
response_type: 'ephemeral',
text: 'Usage: /ticket create | /ticket lookup TKT-1234'
});
}
}
});Example Code
We provide a full Slack integration example in our GitHub repository:
slack-integration.ts
A complete Slack app with notifications, ticket creation, and two-way sync.
View on GitHub →The example includes:
- Slack app manifest for easy setup
- Webhook handler for ticket events
- Message shortcut for ticket creation
- Two-way thread sync
- Slash command handler