HomeAboutFeaturesPricingBlog
Sign inContact Us
All blogs

Building Real-Time Sync Between Slack and Your Task Manager

A technical deep-dive into how we built bidirectional sync between Slack and our ticket system using webhooks, message queues, and event-driven architecture.

Jordan
Jordan4 Feb 2026 · 8 min read

Introduction

One of the most requested features from our users was Slack integration. Not just notifications, but real bidirectional sync. When a ticket updates, Slack knows. When someone replies in Slack, the ticket updates.

Building this turned out to be more interesting than we expected. In this post, we will walk through our architecture, share some code, and explain the tradeoffs we made.

The Architecture Overview

Our sync system has three main components: a webhook receiver for Slack events, a message queue for reliable processing, and a sync engine that handles the bidirectional updates.

Here is the high-level flow:

Slack Event -> Webhook Receiver -> Message Queue -> Sync Engine -> Database
                                                          |
                                                          v
                                                    Ticket Updated
                                                          |
                                                          v
                                              Slack API <- Notification

This architecture ensures we never lose events, even during high load or temporary outages.

Handling Slack Webhooks

Slack sends events via HTTP POST to your webhook endpoint. The tricky part is responding quickly - Slack expects a 200 response within 3 seconds, or it will retry.

Our solution: acknowledge immediately, process asynchronously.

typescript
export async function handleSlackWebhook(req: Request) {
  const payload = await req.json();
  
  if (!verifySlackSignature(req, payload)) {
    return new Response("Invalid signature", { status: 401 });
  }
  
  if (payload.type === "url_verification") {
    return Response.json({ challenge: payload.challenge });
  }
  
  await messageQueue.publish("slack-events", {
    event: payload.event,
    teamId: payload.team_id,
    timestamp: Date.now(),
  });
  
  return new Response("OK", { status: 200 });
}

The key insight: separate receiving from processing. Your webhook handler should do almost nothing except queue the work.

The Sync Engine

The sync engine is where the magic happens. It consumes events from the queue and determines what action to take.

typescript
async function processSyncEvent(event: SlackEvent) {
  const { type, channel, message, thread_ts } = event;
  
  const ticket = await findLinkedTicket(channel, thread_ts);
  if (!ticket) return;
  
  switch (type) {
    case "message":
      await addCommentToTicket(ticket.id, {
        content: message.text,
        author: await resolveSlackUser(message.user),
        source: "slack",
      });
      break;
      
    case "reaction_added":
      if (message.reaction === "white_check_mark") {
        await updateTicketStatus(ticket.id, "done");
      }
      break;
  }
}

We use a simple convention: a checkmark emoji marks a ticket as done. This lets team members update ticket status without leaving Slack.

Preventing Infinite Loops

The biggest gotcha with bidirectional sync: infinite loops. Ticket updates Slack, Slack event triggers ticket update, which updates Slack, forever.

Our solution uses event sourcing with origin tracking:

typescript
await updateTicket(ticketId, {
  status: "done",
  _meta: {
    source: "slack",
    sourceEventId: event.event_ts,
  }
});

async function notifySlack(ticket: Ticket, change: Change) {
  if (change._meta?.source === "slack") {
    return;
  }
  
  await slack.chat.postMessage({
    channel: ticket.slackChannel,
    text: formatTicketUpdate(change),
    thread_ts: ticket.slackThreadTs,
  });
}

Every mutation carries metadata about its origin. Before propagating a change, we check if it came from the destination.

Handling Rate Limits

Slack has strict rate limits. We use exponential backoff and a token bucket algorithm:

typescript
const rateLimiter = new TokenBucket({
  capacity: 50,
  refillRate: 1,
  refillInterval: 1000,
});

async function postToSlack(message: SlackMessage) {
  await rateLimiter.acquire();
  return slack.chat.postMessage(message);
}

Other edge cases we handle: message ordering (using timestamps), deleted messages (soft-delete with indicator), and network failures (retry with backoff).

Monitoring and Debugging

Distributed systems are hard to debug. We built observability in from the start:

typescript
logger.info("sync.completed", {
  ticketId: ticket.id,
  slackChannel: channel,
  eventType: event.type,
  processingTimeMs: Date.now() - startTime,
  queueLatencyMs: startTime - event.timestamp,
});

We track queue depth, processing latency, and error rates. When sync breaks, we know within seconds.

Conclusion

Building real-time sync is challenging but rewarding. The key principles: separate ingestion from processing, track event origins to prevent loops, and build observability from day one.

Our Slack integration has become one of our most-used features. See the full Slack integration guide to get started in minutes.

Want to see what else you can automate? Read our post on how AI handles tickets automatically.

Share this post

Jordan

Jordan

Co-founder

Related posts

The Complete Guide to Smart Email Integrations
Engineering

The Complete Guide to Smart Email Integrations

Stop manually copying emails to your project tool. Learn how smart email integrations with Gmail and Outlook automate your workflow.

Sidney
Sidney28 Mar 2026 · 8 min read
Why We Chose Supabase as the Backbone for Refront
Engineering

Why We Chose Supabase as the Backbone for Refront

An honest account of our decision to choose Supabase over Firebase and other alternatives. With real experiences, costs, and technical trade-offs.

Sidney
Sidney20 Mar 2026 · 8 min read
5 Ways to Monitor Project Profitability with Smart Dashboards
Workflows

5 Ways to Monitor Project Profitability with Smart Dashboards

Projects that go over budget are often discovered too late. Learn how real-time dashboards help you keep grip on margins, hours, and profitability per project.

Jordan
Jordan24 Mar 2026 · 7 min read
From Quote to Invoice: Automate Your Complete Workflow
Product updates

From Quote to Invoice: Automate Your Complete Workflow

Learn how to automate the entire journey from quote to invoice with Refront. Save hours per week and prevent missed revenue from manual errors.

Jordan
Jordan17 Mar 2026 · 6 min read
Slack
GitHub
Stripe
Azure DevOps
Cursor
Gmail
Outlook
Slack
GitHub
Stripe
Azure DevOps
Cursor
Gmail
Outlook

Features like collaboration,
code explainers and more

Whether you're part of a team or an individual, Refront helps you supercharge your code seamlessly.

Get startedStart for free

Refront is a workflow automation platform built to help teams turn work into solved tasks end to end.

© 2026 MG Software B.V. All rights reserved.

IntegrationsSlackGitHubAzure DevOpsStripeCursor
ResourcesKnowledge BaseComparisonsSolutionsTemplatesExamplesDirectoryLocationsTools
HomeFeaturesAbout UsContactPricingBlog