Back to Docs

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

  1. Go to api.slack.com/apps
  2. Create a new app (or use an existing one)
  3. Go to Incoming Webhooks and enable them
  4. Click Add New Webhook to Workspace
  5. Select the channel (e.g., #support-tickets)
  6. Copy the webhook URL

Step 2: Create a Dispatch Webhook

Set up a webhook in Dispatch Tickets that triggers your notification endpoint:

Create Dispatch webhook
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

Post to Slack on new ticket
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

DT

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:

Handle message shortcut
// 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:

Handle emoji reaction
// 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:

Database schema
// 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

Create Slack thread for new ticket
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

Handle Slack thread replies
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.

Handle /ticket command
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