Laravel Webhooks: Complete Side Effects Before Firing

📖 3 minutes read

The Problem: Webhooks Firing with Incomplete Data

You’ve built a bulk operation tool that processes multiple records—replacing files, updating statuses, generating documents. At the end of the process, you fire a webhook to notify downstream systems. The webhook delivers successfully… but the data is incomplete.

The tool thinks the job is done. Downstream systems think they have everything. But some operations didn’t actually finish before the webhook fired.

What Happened

This pattern emerges when your bulk operation tool handles mixed workflows:

  • Some items are simple file attachments (just attach and done)
  • Other items require generation steps (QR codes, PDFs, consolidated documents)

The tool was originally built to handle only file attachments. It attaches all the files, then fires the webhook—”from its point of view the flow is complete.”

But when you start using it for mixed workflows, the generation step gets skipped. The webhook fires too early, and downstream systems receive incomplete records.

Example: Mixed Document Attachments

Let’s say you’re building a bulk order processor that handles digital fulfillment:

// BulkOrderProcessorController.php
public function process(Request $request)
{
    $orders = Order::whereIn('uuid', $request->order_ids)->get();
    
    foreach ($orders as $order) {
        // Attach pre-existing PDF files
        foreach ($order->getPreExistingDocuments() as $doc) {
            $order->attachments()->attach($doc->id);
        }
    }
    
    // Send webhook to downstream system
    $this->dispatchWebhook($orders);
    
    return response()->json(['status' => 'complete']);
}

This works great when all documents are pre-existing files. But what if some orders need generated documents (e.g., a consolidated invoice PDF or QR codes for ticket verification)?

The webhook fires after attaching files, but before generating the missing documents. Downstream systems see “Order complete” but some documents are missing.

The Solution: Complete All Side Effects Before Firing Webhooks

The fix is simple: don’t send webhooks until every side effect is complete.

// BulkOrderProcessorController.php
public function process(Request $request)
{
    $orders = Order::whereIn('uuid', $request->order_ids)->get();
    
    foreach ($orders as $order) {
        // Step 1: Attach pre-existing files
        foreach ($order->getPreExistingDocuments() as $doc) {
            $order->attachments()->attach($doc->id);
        }
        
        // Step 2: Generate missing documents BEFORE webhook
        app(DocumentGenerator::class)->generateFromOrder($order);
    }
    
    // Step 3: NOW send webhook (all data ready)
    $this->dispatchWebhook($orders);
    
    return response()->json(['status' => 'complete']);
}

Key change: Call DocumentGenerator for each order before firing the webhook. This ensures every order has all its attachments—both pre-existing files and generated documents.

When to Watch Out for This

This pattern is especially common when:

  • You repurpose a tool for a broader use case than it was originally built for
  • Your workflow has mixed requirements (some simple, some complex)
  • The tool was built iteratively—”it worked fine until we added feature X”
  • Webhooks notify external systems (they can’t easily retry or detect missing data)

Real-World Indicators

You might be hitting this issue if:

  • Downstream systems complain about missing data even though your tool reports success
  • Re-running the bulk operation “fixes” the problem (because the second run actually generates the missing items)
  • The tool’s definition of “complete” doesn’t match downstream expectations

Key Takeaways

  1. Webhooks are promises—don’t send them until you can fulfill every part of the promise
  2. Mixed workflows need mixed handling—check for items that need generation, not just attachment
  3. Test with real-world data—edge cases reveal assumptions about what “complete” means
  4. Idempotency helps—if downstream systems can safely receive duplicate webhooks, recovery is easier

A webhook saying “job complete” should mean actually complete—not “complete according to the original scope.”

Daryle De Silva

VP of Technology

11+ years building and scaling web applications. Writing about what I learn in the trenches.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *