DEV Community

Cover image for Building Sub-5ms Ingestion Middleware for ServiceTitan and Jobber: Part Three - Multi-Tenant Webhook Segregation and Isolated Tenant Routing
KallowayThayer
KallowayThayer

Posted on

Building Sub-5ms Ingestion Middleware for ServiceTitan and Jobber: Part Three - Multi-Tenant Webhook Segregation and Isolated Tenant Routing

When a private equity firm executes a roll-up acquisition strategy in the home-service sector, they rarely inherit a unified technology stack. Within a single operating group, Branch A might run its dispatch boards on ServiceTitan Max, Branch B on Jobber, and Branch C on legacy regional web forms feeding fragmented CRMs.

The traditional low-code approach is to route all inbound webhooks from Google Local Services Ads (LSAs), Meta Lead Ads, and lead aggregators into a single shared ingestion endpoint. At scale, this architecture fails catastrophically due to schema bleed and noisy-neighbor contention.

When a custom form field change on Branch A accidentally collides with required validation keys for Branch B, shared parsers throw exceptions and silently drop paid leads. Worse, returning HTTP 4xx or 5xx errors to upstream ad platforms during a single branch's CRM outage triggers retry storms—or causes Meta and Google to automatically disable the webhook subscription altogether.

To scale multi-branch operations securely, you need strict tenant isolation at the edge without provisioning an expensive, high-maintenance container cluster for every acquired brand.


1. Edge-Level Tenant Resolution & Raw-Byte Cryptographic Auth

To prevent unauthorized webhook floods and noisy-neighbor spikes from exhausting worker memory, tenant resolution and cryptographic verification must execute before JSON.parse() allocates heap objects.

  • Path-Scoped Ingress Isolation: Upstream platforms like Meta Lead Ads and Google LSAs do not allow arbitrary custom routing headers. Instead, isolate each acquired brand via dedicated path-scoped ingress routes (/v1/ingest/:tenant_slug) paired with O(1) ephemeral memory lookups.
  • Platform-Native Signature Verification: Validate platform HMAC headers (X-Hub-Signature-256 or X-Webhook-Signature) directly against the raw request byte stream (ArrayBuffer) using Web Crypto's constant-time verification (crypto.subtle.verify).
  • Zero Timing or Heap Leaks: Spoofed payloads are dropped in sub-millisecond memory with zero timing side-channels and zero JSON parsing overhead.

2. Isolated Tenant Routing & Stateless Schema Mapping

In a multi-tenant trade architecture, querying a relational database on every webhook to fetch tenant field mappings introduces unacceptable latency drift under ad-traffic spikes.

  • Ephemeral Configuration Maps: Store lightweight, immutable tenant routing profiles in edge worker memory. Each profile encapsulates:
    • Downstream CRM adapter target (ServiceTitan v2 tenant/Business Unit ID vs. Jobber GraphQL account).
    • Deterministic schema mapping contracts to flatten disparate ad-platform JSON keys.
    • Regional NANP/E.164 telephony and extension sanitization rules.
  • Fault Containment: Because tenant execution contexts share zero mutable state, a schema change or API rate limit on Branch A cannot block or corrupt lead ingestion for Branches B through Z.

3. Tenant-Segregated Dead-Letter Queues (DLQ) & Upstream Subscription Safety

When a downstream CRM experiences transient downtime—such as an expired ServiceTitan OAuth credential or a 502 Bad Gateway—global error handlers either drop the lead or return 500 to the ad network, triggering destructive retry storms.

  • 202 Quarantine Pattern: Once a payload passes cryptographic authentication at the edge, transient downstream CRM failures or unexpected schema mutations never return 4xx/5xx to the ad platform. Instead, the raw payload is shunted into an isolated, tenant-scoped Dead-Letter Quarantine and the edge gateway returns 202 Accepted—preserving upstream webhook health while guaranteeing zero lead loss.
  • Granular Operational Telemetry: Emit structured quarantine events tagged with isolated tenant identifiers (e.g., tenant_id: ridgeway_southwest_hvac), allowing automated replay workers to drain and re-dispatch queued leads the moment downstream CRM credentials recover.

Reference Implementation: Stateless Multi-Tenant Edge Router

Below is the TypeScript reference router from our open-source core demonstrating O(1) path resolution, raw-byte constant-time HMAC verification, dependency-injected schema normalization, and 202 tenant-isolated DLQ shunting:

// src/edge/tenant-router.ts
export interface TenantProfile {
  id: string;
  active: boolean;
  webhookSecret: string;
  crmAdapter: "servicetitan_v2" | "jobber_graphql";
  crmEndpoint: string;
  businessUnitId: number;
  schemaRules: Record<string, unknown>;
}

const encoder = new TextEncoder();

async function verifyHmacSha256(
  rawBody: ArrayBuffer,
  secret: string,
  signatureHeader: string | null
): Promise<boolean> {
  if (!signatureHeader) return false;
  const cleanHex = signatureHeader.replace(/^sha256=/i, "").trim();
  if (cleanHex.length !== 64 || !/^[0-9a-f]+$/i.test(cleanHex)) return false;

  const key = await crypto.subtle.importKey(
    "raw",
    encoder.encode(secret),
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["verify"]
  );

  const sigBytes = new Uint8Array(32);
  for (let i = 0; i < 32; i++) {
    sigBytes[i] = parseInt(cleanHex.slice(i * 2, i * 2 + 2), 16);
  }

  // Constant-time verification via Web Crypto API (zero timing leak)
  return crypto.subtle.verify("HMAC", key, sigBytes, rawBody);
}

export async function handleMultiTenantWebhook(
  request: Request,
  tenantLookup: (slug: string) => TenantProfile | undefined,
  normalizeAndSanitize: (payload: unknown, rules: Record<string, unknown>) => unknown,
  forwardToCRM: (payload: unknown, profile: TenantProfile) => Promise<Response>,
  pushToTenantDLQ: (tenantId: string, rawBody: string, reason: string) => Promise<void>
): Promise<Response> {
  const startTime = performance.now();

  // 1. Resolve tenant slug from isolated ingress path (/v1/ingest/:tenant_slug)
  const url = new URL(request.url);
  const pathSegments = url.pathname.split("/").filter(Boolean);
  const tenantSlug = pathSegments[pathSegments.length - 1];

  if (!tenantSlug) {
    return new Response("Missing Tenant Ingress Route", { status: 404 });
  }

  // 2. O(1) ephemeral memory lookup (zero DB I/O)
  const tenant = tenantLookup(tenantSlug);
  if (!tenant || !tenant.active) {
    return new Response("Tenant Quarantined or Unregistered", { status: 403 });
  }

  // 3. Read raw bytes once & verify platform HMAC before JSON allocation
  const rawBuffer = await request.arrayBuffer();
  const sigHeader =
    request.headers.get("X-Hub-Signature-256") ||
    request.headers.get("X-Webhook-Signature");

  const isAuthentic = await verifyHmacSha256(rawBuffer, tenant.webhookSecret, sigHeader);
  if (!isAuthentic) {
    return new Response("Invalid Cryptographic Signature", { status: 401 });
  }

  const rawText = new TextDecoder().decode(rawBuffer);

  try {
    const rawPayload = JSON.parse(rawText);
    const sanitizedEnvelope = normalizeAndSanitize(rawPayload, tenant.schemaRules);
    const crmResponse = await forwardToCRM(sanitizedEnvelope, tenant);

    if (!crmResponse.ok) {
      // Isolate downstream failure to tenant-scoped DLQ; return 202 to prevent upstream retry storms
      await pushToTenantDLQ(tenant.id, rawText, `DOWNSTREAM_${crmResponse.status}`);
      return new Response(
        JSON.stringify({ status: "QUARANTINED_DLQ", tenant: tenant.id }),
        { status: 202, headers: { "Content-Type": "application/json" } }
      );
    }

    const latencyMs = Number((performance.now() - startTime).toFixed(2));
    return new Response(
      JSON.stringify({ status: "DISPATCHED", tenant: tenant.id, latency_ms: latencyMs }),
      { status: 200, headers: { "Content-Type": "application/json" } }
    );
  } catch {
    // Quarantine unexpected schema drift without disabling upstream Meta/LSA subscriptions
    await pushToTenantDLQ(tenant.id, rawText, "SCHEMA_PARSE_EXCEPTION");
    return new Response(
      JSON.stringify({ status: "QUARANTINED_MALFORMED", tenant: tenant.id }),
      { status: 202, headers: { "Content-Type": "application/json" } }
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Architectural Takeaway

For private equity roll-ups and multi-branch trade operators, M&A scalability depends on strict isolation at the ingress boundary. Replacing shared low-code webhooks with stateless, cryptographically verified edge routing eliminates cross-branch schema bleed, protects upstream ad-platform subscriptions, and guarantees sub-5ms speed-to-lead across every acquired brand.

Operated strictly as an asynchronous engineering consultancy,no sales calls.

Explore the open-source reference interfaces at KallowayThayer/KT-pipeline-core or submit an architecture review at kallowaythayer.com.

Top comments (0)