<?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>Forem: Mateusz</title>
    <description>The latest articles on Forem by Mateusz (@flowsystems).</description>
    <link>https://forem.com/flowsystems</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%2F3816811%2F23d077d1-46a3-49b3-b2cd-c94e52b47234.png</url>
      <title>Forem: Mateusz</title>
      <link>https://forem.com/flowsystems</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/flowsystems"/>
    <language>en</language>
    <item>
      <title>Why Most WordPress Webhooks Fail in Production</title>
      <dc:creator>Mateusz</dc:creator>
      <pubDate>Tue, 10 Mar 2026 13:34:47 +0000</pubDate>
      <link>https://forem.com/flowsystems/why-most-wordpress-webhooks-fail-in-production-1pmh</link>
      <guid>https://forem.com/flowsystems/why-most-wordpress-webhooks-fail-in-production-1pmh</guid>
      <description>&lt;p&gt;Webhooks are one of the easiest ways to integrate WordPress with other systems.&lt;/p&gt;

&lt;p&gt;Expose an endpoint.&lt;br&gt;
Send a POST request.&lt;br&gt;
Process the payload.&lt;/p&gt;

&lt;p&gt;Done.&lt;/p&gt;

&lt;p&gt;At least… until the first production failure.&lt;/p&gt;

&lt;p&gt;Once webhooks start powering &lt;strong&gt;real integrations&lt;/strong&gt;, a different set of problems appears:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIs fail&lt;/li&gt;
&lt;li&gt;endpoints time out&lt;/li&gt;
&lt;li&gt;payloads get lost&lt;/li&gt;
&lt;li&gt;debugging becomes nearly impossible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suddenly the biggest challenge is no longer &lt;strong&gt;triggering a webhook&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The real challenge becomes &lt;strong&gt;delivering it reliably&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This article focuses on the production side of WordPress webhooks — the things that matter once integrations start running in the real world.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Problem With Most WordPress Webhook Implementations
&lt;/h2&gt;

&lt;p&gt;A typical webhook implementation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;wp_remote_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'headers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'Content-Type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'body'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payload&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;If the request succeeds — great.&lt;/p&gt;

&lt;p&gt;If it fails?&lt;/p&gt;

&lt;p&gt;Most systems simply move on.&lt;/p&gt;

&lt;p&gt;No retry.&lt;br&gt;
No visibility.&lt;br&gt;
No way to replay the event.&lt;/p&gt;

&lt;p&gt;This approach works fine for hobby projects or simple automations.&lt;/p&gt;

&lt;p&gt;But once webhooks power &lt;strong&gt;payment flows, CRM syncing, or external automations&lt;/strong&gt;, reliability becomes critical.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpeg45183mj9wvvattuoc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpeg45183mj9wvvattuoc.jpg" alt="Typical webhook delivery: event → send request → hope it succeeds." width="800" height="852"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  When Should You Use Webhooks vs Polling?
&lt;/h2&gt;

&lt;p&gt;Google loves this question.&lt;/p&gt;

&lt;p&gt;Short answer:&lt;br&gt;
Use webhooks when events are important and should trigger actions immediately.&lt;/p&gt;

&lt;p&gt;Use polling when the external system does not support webhooks or when you need periodic synchronization.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Actually Breaks in Production
&lt;/h2&gt;

&lt;p&gt;In production environments, webhook delivery fails more often than you might expect.&lt;/p&gt;

&lt;p&gt;Some common failure scenarios:&lt;/p&gt;
&lt;h3&gt;
  
  
  External API downtime
&lt;/h3&gt;

&lt;p&gt;The receiving service may be temporarily unavailable.&lt;/p&gt;

&lt;p&gt;Your webhook fires once and disappears forever.&lt;/p&gt;
&lt;h3&gt;
  
  
  Network timeouts
&lt;/h3&gt;

&lt;p&gt;Slow APIs can easily exceed WordPress HTTP timeouts.&lt;/p&gt;
&lt;h3&gt;
  
  
  Temporary bugs in the receiving integration
&lt;/h3&gt;

&lt;p&gt;Developers fix integrations after deployment — but by then the webhook payload is already gone.&lt;/p&gt;
&lt;h3&gt;
  
  
  Rate limits
&lt;/h3&gt;

&lt;p&gt;Some APIs reject bursts of webhook requests.&lt;/p&gt;

&lt;p&gt;Without retries, events are permanently lost.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Pattern Used by Most Reliable Webhook Systems
&lt;/h2&gt;

&lt;p&gt;Systems like Stripe, GitHub, and Shopify treat webhooks very differently.&lt;/p&gt;

&lt;p&gt;Instead of &lt;strong&gt;fire-and-forget&lt;/strong&gt;, they follow this pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Store the webhook payload&lt;/li&gt;
&lt;li&gt;Attempt delivery&lt;/li&gt;
&lt;li&gt;Retry on failure&lt;/li&gt;
&lt;li&gt;Allow replay of past events&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This simple change dramatically improves reliability.&lt;/p&gt;

&lt;p&gt;It also makes debugging integrations far easier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6hnstfzr1q8bd4666sy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6hnstfzr1q8bd4666sy.png" alt="Reliable webhook systems store events, retry failures, and allow replay." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  A Simple Architecture for Reliable WordPress Webhooks
&lt;/h2&gt;

&lt;p&gt;A production-friendly webhook system usually contains three components:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Event storage
&lt;/h3&gt;

&lt;p&gt;Before sending the webhook, store the payload.&lt;/p&gt;

&lt;p&gt;A custom table works well for this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;wp_webhook_events&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;event_type&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="nb"&gt;LONGTEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;attempts&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every webhook event is persisted.&lt;/p&gt;

&lt;p&gt;Nothing disappears.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Delivery worker
&lt;/h3&gt;

&lt;p&gt;Instead of sending webhooks immediately, you queue them.&lt;/p&gt;

&lt;p&gt;WordPress cron can process the queue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;processWebhookQueue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nv"&gt;$events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPendingWebhookEvents&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$events&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;sendWebhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$event&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;If delivery fails, increment the attempt counter and retry later.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Replay capability
&lt;/h3&gt;

&lt;p&gt;One of the most powerful debugging tools is &lt;strong&gt;replaying a webhook&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If an integration breaks, developers can simply resend the stored event.&lt;/p&gt;

&lt;p&gt;This is how many large platforms support developers.&lt;/p&gt;

&lt;p&gt;A simple replay function might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;replayWebhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$eventId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nv"&gt;$event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getWebhookEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$eventId&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="nv"&gt;$event&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="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;sendWebhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$event&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;Now integrations become much easier to debug.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0d72593w4lv8ltoiig3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0d72593w4lv8ltoiig3.png" alt="Replay capability makes debugging integrations dramatically easier." width="800" height="1235"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters for WordPress Integrations
&lt;/h2&gt;

&lt;p&gt;WordPress often acts as the &lt;strong&gt;integration hub&lt;/strong&gt; between different services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WooCommerce → CRM&lt;/li&gt;
&lt;li&gt;Membership plugins → email platforms&lt;/li&gt;
&lt;li&gt;forms → automation tools&lt;/li&gt;
&lt;li&gt;content publishing → external APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When webhooks silently fail, those integrations break.&lt;/p&gt;

&lt;p&gt;And the worst part?&lt;/p&gt;

&lt;p&gt;You often don't even know it happened.&lt;/p&gt;

&lt;p&gt;Persisting webhook events changes that completely.&lt;/p&gt;

&lt;p&gt;Now you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inspect payloads&lt;/li&gt;
&lt;li&gt;retry failed deliveries&lt;/li&gt;
&lt;li&gt;replay events after fixing integrations&lt;/li&gt;
&lt;li&gt;debug issues faster&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Observability: The Missing Piece
&lt;/h2&gt;

&lt;p&gt;Another common problem with webhooks is visibility.&lt;/p&gt;

&lt;p&gt;Without logs, developers are essentially blind.&lt;/p&gt;

&lt;p&gt;A good webhook system should expose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;delivery status&lt;/li&gt;
&lt;li&gt;number of attempts&lt;/li&gt;
&lt;li&gt;response codes&lt;/li&gt;
&lt;li&gt;response body&lt;/li&gt;
&lt;li&gt;timestamps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even a simple admin table showing webhook history can save hours of debugging.&lt;/p&gt;




&lt;h2&gt;
  
  
  WordPress Is Actually a Great Webhook Platform
&lt;/h2&gt;

&lt;p&gt;Despite these challenges, WordPress is surprisingly well suited for webhook-based integrations.&lt;/p&gt;

&lt;p&gt;You already have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an HTTP client (&lt;code&gt;wp_remote_post&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;REST API endpoints&lt;/li&gt;
&lt;li&gt;cron scheduling&lt;/li&gt;
&lt;li&gt;database storage&lt;/li&gt;
&lt;li&gt;plugin architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a few architectural decisions, WordPress can power surprisingly robust automation systems.&lt;/p&gt;

&lt;p&gt;Example: Sending a WordPress Webhook&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;wp_remote_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'headers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'Content-Type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'timeout'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'body'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;wp_json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payload&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;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Creating a webhook endpoint in WordPress is easy.&lt;/p&gt;

&lt;p&gt;Building a &lt;strong&gt;reliable webhook system&lt;/strong&gt; requires a bit more thought.&lt;/p&gt;

&lt;p&gt;Once integrations become important to your workflow, it's worth considering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;storing webhook payloads&lt;/li&gt;
&lt;li&gt;retrying failed deliveries&lt;/li&gt;
&lt;li&gt;allowing event replay&lt;/li&gt;
&lt;li&gt;logging delivery results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These patterns are used by nearly every major platform that relies on webhooks.&lt;/p&gt;

&lt;p&gt;Bringing them into WordPress can make integrations far more reliable — and far easier to debug.&lt;/p&gt;

&lt;p&gt;How are you handling webhook reliability in your projects?&lt;/p&gt;

&lt;p&gt;Do you store payloads, retry failures, or treat them as fire-and-forget?&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>php</category>
      <category>api</category>
    </item>
    <item>
      <title>Inside my webhook retry + replay system for WordPress</title>
      <dc:creator>Mateusz</dc:creator>
      <pubDate>Tue, 10 Mar 2026 11:57:00 +0000</pubDate>
      <link>https://forem.com/flowsystems/inside-my-webhook-retry-replay-system-for-wordpress-20hf</link>
      <guid>https://forem.com/flowsystems/inside-my-webhook-retry-replay-system-for-wordpress-20hf</guid>
      <description>&lt;p&gt;WordPress webhooks fire once.&lt;/p&gt;

&lt;p&gt;If the receiving API fails, the event is gone.&lt;/p&gt;

&lt;p&gt;So I built a retry + replay system.&lt;/p&gt;

&lt;p&gt;Architecture inside 👇&lt;br&gt;
&lt;a href="https://flowsystems.pl/blog/wordpress-webhook-retry-replay-system/" rel="noopener noreferrer"&gt;https://flowsystems.pl/blog/wordpress-webhook-retry-replay-system/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>backend</category>
      <category>wordpress</category>
      <category>api</category>
    </item>
  </channel>
</rss>
