<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: KallowayThayer</title>
    <description>The latest articles on DEV Community by KallowayThayer (@kallowaythayer).</description>
    <link>https://dev.to/kallowaythayer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4213649%2F82c07283-dccc-4a4b-be10-6c7844bddb5d.jpg</url>
      <title>DEV Community: KallowayThayer</title>
      <link>https://dev.to/kallowaythayer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kallowaythayer"/>
    <language>en</language>
    <item>
      <title>Building Sub-5ms Ingestion Middleware for ServiceTitan and Jobber: Architecture &amp; Deduplication Deep Dive</title>
      <dc:creator>KallowayThayer</dc:creator>
      <pubDate>Wed, 28 Oct 2026 18:14:43 +0000</pubDate>
      <link>https://dev.to/kallowaythayer/building-sub-5ms-ingestion-middleware-for-servicetitan-and-jobber-architecture-deduplication-4neb</link>
      <guid>https://dev.to/kallowaythayer/building-sub-5ms-ingestion-middleware-for-servicetitan-and-jobber-architecture-deduplication-4neb</guid>
      <description>&lt;p&gt;When you're handling inbound leads for high-end home services, you can't afford generic middleware boilerplate. If a lead hits your pipeline with a messed-up phone number or a duplicate webhook retry from a frantic homeowner spamming a contact button, your CRM gets polluted and dispatch gets a bad number.&lt;/p&gt;

&lt;p&gt;I built our ingestion core to run sub-5ms in transient memory, stripping out the fluff before anything touches ServiceTitan or Jobber. Here is how the core loop handles idempotency, schema flattening, and deduplication.&lt;/p&gt;

&lt;p&gt;Dual-Hash Idempotency Without Disk Bottlenecks&lt;/p&gt;

&lt;p&gt;Hitting a database on every inbound webhook to check for duplicates adds unacceptable latency. Instead, we use an in-memory sliding-window ledger combined with a dual-hash fingerprint generator (DJB2 + FNV-1a):&lt;/p&gt;

&lt;p&gt;JavaScript&lt;/p&gt;

&lt;p&gt;const generateFingerprint = (str) =&amp;gt; {&lt;br&gt;
  if (!str || !String(str).replace(/|/g, '').trim()) return null;&lt;br&gt;
  const s = String(str);&lt;br&gt;
  let djb2 = 5381;&lt;br&gt;
  let fnv = 2166136261;&lt;br&gt;
  for (let i = 0; i &amp;lt; s.length; i++) {&lt;br&gt;
    const char = s.charCodeAt(i);&lt;br&gt;
    djb2 = ((djb2 &amp;lt;&amp;lt; 5) + djb2) + char;&lt;br&gt;
    fnv ^= char;&lt;br&gt;
    fnv = Math.imul(fnv, 16777619);&lt;br&gt;
  }&lt;br&gt;
  return (djb2 &amp;gt;&amp;gt;&amp;gt; 0).toString(16).padStart(8, '0') + '-' + (fnv &amp;gt;&amp;gt;&amp;gt; 0).toString(16).padStart(8, '0');&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;This gives us two layers of protection:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;In-Batch Deduplication (seenInBatch): Catches identical payloads arriving simultaneously in the same execution burst.

Sliding TTL Ledger: Tracks recent events with strict time-to-live expiration (86400000ms for events, 3600000ms for contacts) while automatically pruning memory overflow so the heap doesn't bloat.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Flattening Ingress Chaos&lt;/p&gt;

&lt;p&gt;Every form builder and ad network sends data differently. Google Lead Forms use user_column_data, Meta Lead Ads use field_data, and custom webhook forms dump nested objects into body or payload.&lt;/p&gt;

&lt;p&gt;We flatten all of those disparate schemas into a single normalized payload before mapping them directly into clean, CRM-ready structures:&lt;br&gt;
JavaScript&lt;/p&gt;

&lt;p&gt;const allAdapters = {&lt;br&gt;
  servicetitan_v2: {&lt;br&gt;
    name: fullFormattedName,&lt;br&gt;
    type: isCommercialLead ? 'Commercial' : 'Residential',&lt;br&gt;
    address: {&lt;br&gt;
      street: street || rawAddress,&lt;br&gt;
      unit: unit || undefined,&lt;br&gt;
      city: city || undefined,&lt;br&gt;
      state: state || undefined,&lt;br&gt;
      zip: zip || undefined,&lt;br&gt;
      country: resolvedCountryIso3&lt;br&gt;
    },&lt;br&gt;
    contacts: [&lt;br&gt;
      ...(cleanPhone ? [{ type: 'MobilePhone', value: cleanPhone, memo: phoneExtension ? 'ext ' + phoneExtension : undefined }] : []),&lt;br&gt;
      ...(hasValidEmail ? [{ type: 'Email', value: cleanEmail }] : [])&lt;br&gt;
    ]&lt;br&gt;
  },&lt;br&gt;
  jobber_graphql: {&lt;br&gt;
    firstName: firstName,&lt;br&gt;
    lastName: lastName,&lt;br&gt;
    companyName: resolvedCompany || undefined,&lt;br&gt;
    isCompany: Boolean(entityType === 'CORPORATE_OR_SYSTEM' || (isCommercialLead &amp;amp;&amp;amp; resolvedCompany)),&lt;br&gt;
    phones: cleanPhone ? [{ number: cleanPhone, description: 'MAIN', primary: true }] : [],&lt;br&gt;
    emails: hasValidEmail ? [{ address: cleanEmail, description: 'MAIN', primary: true }] : [],&lt;br&gt;
    billingAddress: street ? {&lt;br&gt;
      street1: street,&lt;br&gt;
      street2: unit || undefined,&lt;br&gt;
      city: city || undefined,&lt;br&gt;
      province: state || undefined,&lt;br&gt;
      postalCode: zip || undefined,&lt;br&gt;
      country: resolvedCountryIso2&lt;br&gt;
    } : undefined&lt;br&gt;
  }&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;Edge Validation &amp;amp; DLQ Quarantine&lt;/p&gt;

&lt;p&gt;If a lead has a bogus phone number (like 555-555-5555), profanity in the name field, or invalid email syntax, it never makes it to the CRM.&lt;/p&gt;

&lt;p&gt;Instead, the pipeline flags the payload and routes it straight to a Dead-Letter Queue (DLQ) quarantine envelope, complete with automatic PII redaction (REDACT_DLQ_PII) so logs remain safe and inspectable.&lt;/p&gt;

&lt;p&gt;Check out the full repository and documentation here:&lt;br&gt;
KallowayThayer/KT-pipeline-core on GitHub&lt;/p&gt;

&lt;p&gt;Operated strictly as an asynchronous engineering consultancy. No sales calls. kallowaythayer.com&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>performance</category>
      <category>webhook</category>
    </item>
  </channel>
</rss>
