<?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: Shannon Mettry</title>
    <description>The latest articles on DEV Community by Shannon Mettry (@shannonianthe).</description>
    <link>https://dev.to/shannonianthe</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3948089%2F5d91a757-51cf-4d59-90fc-827728443e5e.png</url>
      <title>DEV Community: Shannon Mettry</title>
      <link>https://dev.to/shannonianthe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shannonianthe"/>
    <language>en</language>
    <item>
      <title>Why WhatsApp API Integrations Fail: Lessons from Real-Time Systems</title>
      <dc:creator>Shannon Mettry</dc:creator>
      <pubDate>Sat, 23 May 2026 18:32:24 +0000</pubDate>
      <link>https://dev.to/shannonianthe/why-whatsapp-api-integrations-fail-lessons-from-real-time-systems-14f0</link>
      <guid>https://dev.to/shannonianthe/why-whatsapp-api-integrations-fail-lessons-from-real-time-systems-14f0</guid>
      <description>&lt;p&gt;In marketing technology, “sending a message” is never just sending a message.&lt;/p&gt;

&lt;p&gt;In my case, I work on the technical layer that prepares and sends structured campaign data from a delivery creation system into the Meta WhatsApp Template API.&lt;/p&gt;

&lt;p&gt;That means I don’t build the messaging platform itself. I build everything &lt;em&gt;before&lt;/em&gt; it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validating campaign data&lt;/li&gt;
&lt;li&gt;enforcing strict formatting rules&lt;/li&gt;
&lt;li&gt;structuring payloads correctly&lt;/li&gt;
&lt;li&gt;checking whether requests were accepted&lt;/li&gt;
&lt;li&gt;and building workflows that survive very opinionated APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And if there’s one thing I’ve learned from working with strict APIs, it’s this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The hardest part is rarely sending the data.&lt;/p&gt;

&lt;p&gt;The hardest part is convincing the API your data deserves to exist.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The reality of “real-time integrations”
&lt;/h2&gt;

&lt;p&gt;People hear “real-time integration” and imagine something elegant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System A → API → Message sent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In reality, the workflow usually looks more like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Delivery Creation System
        ↓
Data Validation Layer
        ↓
Payload Transformation
        ↓
WhatsApp Template Formatting
        ↓
Meta API Request
        ↓
Acceptance Check
        ↓
Webhook Confirmation
        ↓
Retry / Logging / Monitoring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message itself is actually the easiest part.&lt;/p&gt;

&lt;p&gt;The difficult part is making sure the payload satisfies every rule enforced by the receiving API.&lt;/p&gt;

&lt;p&gt;And the Meta WhatsApp Template API has &lt;em&gt;a lot&lt;/em&gt; of rules.&lt;/p&gt;




&lt;h2&gt;
  
  
  Strict APIs do not negotiate
&lt;/h2&gt;

&lt;p&gt;One of the first things I learned is that WhatsApp templates are extremely rigid.&lt;/p&gt;

&lt;p&gt;You are not sending “messages”.&lt;/p&gt;

&lt;p&gt;You are sending:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;approved templates&lt;/li&gt;
&lt;li&gt;predefined structures&lt;/li&gt;
&lt;li&gt;validated variables&lt;/li&gt;
&lt;li&gt;controlled CTA configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything has to fit the exact schema expected by Meta.&lt;/p&gt;

&lt;p&gt;Not “close enough”.&lt;/p&gt;

&lt;p&gt;Not “technically valid JSON”.&lt;/p&gt;

&lt;p&gt;Exactly correct.&lt;/p&gt;




&lt;h2&gt;
  
  
  The formatting constraints are where things get interesting
&lt;/h2&gt;

&lt;p&gt;Most integration issues were not caused by networking problems or authentication failures.&lt;/p&gt;

&lt;p&gt;They were caused by formatting.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;h2&gt;
  
  
  Invalid newline characters
&lt;/h2&gt;

&lt;p&gt;Something as small as an unexpected newline character can create problems depending on template validation rules.&lt;/p&gt;

&lt;p&gt;This means data often has to be sanitized before it is sent.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sanitizeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple?&lt;/p&gt;

&lt;p&gt;Yes.&lt;/p&gt;

&lt;p&gt;Important?&lt;/p&gt;

&lt;p&gt;Extremely.&lt;/p&gt;

&lt;p&gt;Because formatting is not cosmetic in systems like this.&lt;/p&gt;

&lt;p&gt;Formatting &lt;em&gt;is logic&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  CTA rules: where flexibility disappears
&lt;/h2&gt;

&lt;p&gt;Call-to-actions (CTAs) were another area with strict constraints.&lt;/p&gt;

&lt;p&gt;Templates only allow certain CTA structures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;limited number of buttons&lt;/li&gt;
&lt;li&gt;predefined action types&lt;/li&gt;
&lt;li&gt;strict ordering requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which means you cannot dynamically invent whatever UX the business wants.&lt;/p&gt;

&lt;p&gt;At some point, every integration engineer eventually says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“No, we cannot add a fourth button because Meta said no.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And surprisingly often, that becomes a real architectural discussion.&lt;/p&gt;




&lt;h2&gt;
  
  
  The integration layer became a validation system
&lt;/h2&gt;

&lt;p&gt;One of the biggest lessons from this workflow was realizing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The system is not really a messaging system.&lt;/p&gt;

&lt;p&gt;It is a validation system that occasionally sends messages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before anything gets sent, the workflow has to verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CTA counts are valid&lt;/li&gt;
&lt;li&gt;variables match template expectations&lt;/li&gt;
&lt;li&gt;required fields exist&lt;/li&gt;
&lt;li&gt;formatting rules are respected&lt;/li&gt;
&lt;li&gt;payload structure matches the approved template&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because once the payload reaches Meta:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it may be accepted&lt;/li&gt;
&lt;li&gt;it may be rejected&lt;/li&gt;
&lt;li&gt;or in some cases, it may fail in ways that are not immediately obvious&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which is why defensive validation matters so much.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the payload
&lt;/h2&gt;

&lt;p&gt;Here’s a simplified example of the kind of transformation logic involved in the integration layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildTemplatePayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowedCtas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CTA limit exceeded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sanitizedVariables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;variable&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;messaging_product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;whatsapp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;template&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en_US&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sanitizedVariables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
          &lt;span class="p"&gt;}))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is not the JavaScript itself.&lt;/p&gt;

&lt;p&gt;It’s the design philosophy behind it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Data cannot be trusted to arrive API-ready.&lt;/p&gt;

&lt;p&gt;It has to be transformed into something the receiving system is willing to accept.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  “Message sent” is not success
&lt;/h2&gt;

&lt;p&gt;Another important lesson:&lt;/p&gt;

&lt;p&gt;Sending the request is only half the workflow.&lt;/p&gt;

&lt;p&gt;After the payload is sent, we still need to determine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;was the request accepted?&lt;/li&gt;
&lt;li&gt;was the message processed correctly?&lt;/li&gt;
&lt;li&gt;did the delivery succeed?&lt;/li&gt;
&lt;li&gt;did downstream systems confirm the event?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So after sending the payload, the workflow performs additional checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://graph.facebook.com/v19.0/messages&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;API rejection:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Message failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But even a successful API response is not the final truth.&lt;/p&gt;

&lt;p&gt;The workflow still relies on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;response verification&lt;/li&gt;
&lt;li&gt;logging&lt;/li&gt;
&lt;li&gt;webhook confirmation&lt;/li&gt;
&lt;li&gt;retry handling&lt;/li&gt;
&lt;li&gt;reconciliation checks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because in real integrations:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Accepted by API” does not automatically mean “delivered successfully”.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Real-time systems are actually reliability systems
&lt;/h2&gt;

&lt;p&gt;One thing I underestimated early on was how much of “real-time integration” is actually about resilience.&lt;/p&gt;

&lt;p&gt;You quickly realize the real engineering work is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;handling retries&lt;/li&gt;
&lt;li&gt;tracking failures&lt;/li&gt;
&lt;li&gt;validating edge cases&lt;/li&gt;
&lt;li&gt;preventing malformed payloads&lt;/li&gt;
&lt;li&gt;monitoring delivery status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not just sending requests quickly.&lt;/p&gt;

&lt;p&gt;The API call itself is usually the shortest part of the process.&lt;/p&gt;

&lt;p&gt;The surrounding system is where the complexity lives.&lt;/p&gt;




&lt;h2&gt;
  
  
  The most dangerous failures are silent ones
&lt;/h2&gt;

&lt;p&gt;Hard failures are annoying.&lt;/p&gt;

&lt;p&gt;Silent failures are terrifying.&lt;/p&gt;

&lt;p&gt;Because if a payload fails quietly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;messages disappear&lt;/li&gt;
&lt;li&gt;workflows become inconsistent&lt;/li&gt;
&lt;li&gt;debugging becomes archaeology&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s why observability became extremely important in our workflow.&lt;/p&gt;

&lt;p&gt;Every step needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;logging&lt;/li&gt;
&lt;li&gt;validation&lt;/li&gt;
&lt;li&gt;tracking&lt;/li&gt;
&lt;li&gt;explicit error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because eventually every integration engineer learns:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If a system can fail silently, it eventually will.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Usually on Friday afternoon.&lt;/p&gt;




&lt;h2&gt;
  
  
  Constraints actually improve engineering
&lt;/h2&gt;

&lt;p&gt;At first, strict APIs feel frustrating.&lt;/p&gt;

&lt;p&gt;But over time, I realized they force better engineering discipline.&lt;/p&gt;

&lt;p&gt;You stop making assumptions.&lt;/p&gt;

&lt;p&gt;You validate everything.&lt;/p&gt;

&lt;p&gt;You think more carefully about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;structure&lt;/li&gt;
&lt;li&gt;consistency&lt;/li&gt;
&lt;li&gt;formatting&lt;/li&gt;
&lt;li&gt;system reliability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And ironically, those constraints often produce cleaner systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Working with the Meta WhatsApp Template API taught me something important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Most integration work is not about moving data.&lt;/p&gt;

&lt;p&gt;It’s about shaping data into something another system is willing to trust.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And in marketing technology, the real challenge is not sending messages in real time.&lt;/p&gt;

&lt;p&gt;It’s building workflows reliable enough to survive strict APIs, validation rules, and the reality that every external system has opinions about your payload.&lt;/p&gt;

&lt;p&gt;Very strong opinions.&lt;/p&gt;

</description>
      <category>api</category>
      <category>backend</category>
      <category>marketing</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
