• Blog
  • Documentation
  • Pricing
  • FAQ
  • Contact
Sign InSign Up

A single, reliable layer for all your product's integrations - rules, routing, retries, and fan-out included.

© Copyright 2025 Meshes. All Rights Reserved.

About
  • Blog
  • Contact
Product
  • Documentation
Legal
  • Terms of Service
  • Privacy Policy
  • Cookie Policy

Stop Hand-Rolling Webhooks: How to Build a Reliable Integration Layer

Webhooks and queues start simple but quickly turn into a tangle of retries, dead letters, and one-off integrations. This post walks through a practical integration architecture and where a universal integration layer fits.

Cover Image for Stop Hand-Rolling Webhooks: How to Build a Reliable Integration Layer

Every SaaS product hits the same wall:

  • “We just need to POST to this webhook.”
  • “We just need to push new customers into our CRM.”
  • “We just need to notify a partner when an event happens.”

So you add a couple of HTTP calls, maybe a job queue, and it works… until it doesn’t.

The slow death by one-off integrations

A familiar progression:

1. Inline HTTP calls

You fire off requests to HubSpot, Salesforce, or a webhook inside your main request cycle.

  • If the call is slow, your API is slow.
  • If the call fails, you have to decide: retry? swallow? error?

2. Background jobs and queues

You add a queue (Bull, Sidekiq, SQS, etc.) and fork work into workers.

  • Better latency, but now you own:
    • retry policies
    • backoff
    • dead-letter queues
    • poison messages

3. Each integration becomes its own mini-platform

Different auth, payloads, rate limits, and error semantics mean:

  • per-integration code paths
  • per-integration retry quirks
  • per-integration dashboards or logs

Over time you’re not just maintaining your product—you’re maintaining a one-off integration platform.

What you really need: a dedicated integration layer

Step back and most integrations follow the same pattern:

1. Something happens in your app

e.g. lead.created, subscription.renewed, invoice.paid.

2. You emit an event with a clean payload.

3. Routing & fan-out decide:

  • which destinations should receive it
  • how the payload should be mapped
  • when conditions should block or alter behavior

4. Delivery engine handles:

  • queues & parallel fan-out
  • retries & backoff
  • rate limiting
  • dead letters & observability

That’s what a universal integration layer (like Meshes) is built to do.

Instead of sprinkling webhook calls and queue logic all over your codebase, you:

  • define event types (e.g. lead.created)
  • configure rules and connections
  • send events to one API, and let the integration layer handle the rest

A simple example: turning one event into many actions

Let’s say your app wants to:

  • send a new lead to HubSpot
  • notify your backend via webhook
  • add the lead to a Mailchimp list

With an integration layer, your app doesn’t know about three different APIs. It only knows how to emit an event:

// backend (Node/TS) – pseudo example
import fetch from 'node-fetch';

import { createMeshesMachineToken } from './meshes-auth';

async function onLeadCreated(lead: {
  id: string;
  email: string;
  firstName?: string;
  lastName?: string;
  source: string;
}) {
  const jwtToken = await createMeshesMachineToken();

  await fetch('https://api.meshes.io/api/v1/events', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${jwtToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      workspace: 'ws_01...',
      event: 'lead.created',
      payload: lead,
    }),
  });
}

Rules + connections decide:

  • which CRMs/webhooks get the event
  • how the payload is transformed per destination
  • when to retry or give up

Why this matters for developers

A dedicated integration layer gives you:

  • One place for retries, backoff, and dead letters No more re-implementing retry logic per service.
  • One schema per event, many projections Map the same lead.created payload into HubSpot, Salesforce, or webhooks without cluttering your app.
  • Multi-tenant isolation Each customer / environment can have its own workspaces, connections, and rules without leaking credentials.
  • Better observability You get event-level and destination-level logs: what failed, why, and what was retried.

When is it time to stop hand-rolling?

A few signals:

  • You’ve built more than 2–3 external integrations.
  • You’re debugging “why didn’t this webhook fire?” more than you’d like.
  • You have multiple queues or worker pools doing similar things.
  • Product wants to offer “integrations as a feature” to customers.

At that point, investing in a universal layer is cheaper than continuing to bolt one-offs onto your core app.

If you’d rather not build that in-house, that’s the whole idea behind Meshes:

“Emit events, define rules, plug in destinations. The platform handles fan-out, retries, and delivery.”

You keep your app simple—and still ship integrations fast.