• Blog
  • Agents
  • Compare
  • Documentation
  • Pricing
  • FAQ
  • Book Demo
Sign InStart free

The outbound integration layer for SaaS products: emit once, then let Meshes handle routing, retries, fan-out, and delivery history.

© Copyright 2026 Meshes, Inc. All Rights Reserved.

About
  • Blog
  • Contact
  • FAQ
Product
  • Compare
  • Pricing
  • Status
Developers
  • Documentation
  • Agents
  • API Reference
  • MCP Server
  • llms.txt
Legal
  • Terms of Service
  • Privacy Policy
  • Acceptable Use Policy
  • Cookie Policy

From One Event to Many Integrations: A Practical Guide to Fan-Out Architecture

Your product emits one event, but it needs to fan out to CRMs, webhooks, analytics, and internal systems. This post explains fan-out event routing patterns, per-tenant customization, and how to avoid a tangle of ad-hoc integrations.

Cover Image for From One Event to Many Integrations: A Practical Guide to Fan-Out Architecture

Most modern SaaS products have the same challenge:

"When X happens, we need to notify a lot of things."

For example, when a lead is created:

  • create/update a contact in HubSpot for sales
  • fire a webhook to your backend
  • add the lead to a Mailchimp list
  • add the lead to Intercom for support
  • log an event in your analytics stack
  • notify someone in Slack

That's a classic fan-out problem: one event, many destinations.

The naive approach (and why it hurts)

The straightforward way is to put all the calls in your app:

async function onLeadCreated(lead) {
  await Promise.all([
    // send the lead to all the needed services
    syncToHubSpotForSales(lead),
    sendInternalWebhook(lead),
    addToMailchimpForEmails(lead),
    addToIntercomForSupport(lead),
    trackInAnalytics(lead),
  ]);
}

This works until:

  • one destination is slow (your handler is now slow)
  • one destination is flaky (you need retries with exponential backoff and jitter)
  • each integration wants different payload shapes
  • you need per-tenant configuration ("ACME wants this, Contoso wants that")

You quickly end up with:

  • per-integration branching (if tenant === 'acme' then ...)
  • mixed concerns (business logic + integration plumbing)
  • tangled retries and error handling

Pattern: central event + external fan-out

A more robust pattern:

  1. Your core app emits a single, clean event.
  2. A dedicated integration layer receives the event.
  3. The integration layer:
    • evaluates rules
    • decides which destinations should receive it
    • maps the payload
    • handles retries, backoff, and dead letters

Your app code stays focused on business events, not integration details.

For a deeper walkthrough of how routing rules determine which data goes where, see our post on event routing for SaaS.

A concrete example

Your app just does this:

import fetch from 'node-fetch';

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

async function emitLeadCreated(workspaceId: string, lead: any) {
  const token = await createMeshesMachineToken();

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

Everything else (CRMs, webhooks, Slack, etc.) is configured as rules in the integration layer.

Why event fan-out belongs outside your core app

1. Different destinations fail differently

  • CRMs might rate-limit or return 429s.
  • Webhooks might timeout or be misconfigured.
  • Analytics endpoints might be slow but not critical.

The customer UI needs to return fast, so it can't wait for these things to happen.

You don't want your core app to decide, per destination:

  • what's retryable
  • how long to backoff
  • when to give up
  • how to store dead letters

An integration layer can centralize that logic and treat each destination appropriately. When delivery does fail permanently, you need a dead letter queue to catch and replay events—not a silent data loss.

2. Per-tenant customization explodes combinatorics

Imagine:

  • 1,000 customers
  • each with their own preference of:
    • which CRM to use
    • which fields to map
    • which events they care about

Baking that into your app produces a mess of:

if (tenant === 'acme') { ... }
else if (tenant === 'contoso') { ... }

Versus:

  • one event schema per event type
  • per-workspace rules + mappings in the integration layer

This is the multi-tenant integration pattern: workspaces isolate each customer's credentials, rules, and delivery state so one tenant's configuration never affects another.

3. Observability becomes manageable

Instead of:

  • grepping app logs for "hubspot failed"
  • trying to correlate job IDs with webhook IDs

You get:

  • one place to see an event and all downstream attempts
  • per-destination status and error messages
  • retry and dead-letter counts

The build-vs-buy reality

You can build a fan-out system yourself. An event bus, a rule evaluator, per-destination workers, retry tables, dead letter storage, a dashboard—it's all well-understood infrastructure. Most teams estimate a few weeks.

What they underestimate is the ongoing cost: every new destination introduces new auth patterns, new payload shapes, new rate limit behaviors, and new edge cases in error handling. After the third or fourth integration, you're not building product features anymore—you're maintaining a one-off integration platform.

The question isn't "can we build this?" It's "is this where our engineering hours create the most value?"

Implementing fan-out with Meshes

Meshes is designed to be that integration layer:

  • Events – you POST events into the API.
  • Rules – you configure routing, conditions, and destinations.
  • Connections – you securely store OAuth/API keys per workspace.
  • Delivery engine – you get fan-out, retries, and dead letters out of the box.

Your architecture becomes:

  1. App: emit lead.created.
  2. Meshes:
    • match rules on lead.created
    • fan-out to HubSpot, Salesforce, webhooks, etc.
    • manage retries and failures

You keep one clean pipeline:

Domain events in → integrations out.

When to adopt fan-out architecture

You'll get the most value when:

  • you're integrating with more than a couple of third-party services
  • customers are asking for per-tenant integration behavior
  • you're spending time debugging "where did this webhook go?"

If you're already there, it might be time to centralize event fan-out instead of bolting on another webhook handler in your core app.

That's exactly the job Meshes is trying to do for you.

Ready to centralize your fan-out? Join Meshes and route events to every destination from one API.