Create a Simple Webhook in Laravel
Create a Simple Webhook in Laravel

Create a Simple Webhook in Laravel

Create a Simple Webhook in Laravel

 

Hello learners, in many time or scenario, you heard the term called webhook. So what is a webhhok? According to Redhat.com:

A webhook is a lightweight, event-driven communication that automatically sends data between applications via HTTP. Triggered by specific events, webhooks automate communication between application programming interfaces (APIs) and can be used to activate workflows, such as in GitOps environments.

At its core, a webhook is primarily an HTTP POST request sent to a predefined URL on a server when a specific (yes specific) event occurs. Webhhok has some characteristics. These are:

  1. Event-driven – A webhook is triggered when a specific event happens, such as a payment being processed, a new user signing up, or a file being uploaded.

  2. Payload – The request typically includes a JSON (or sometimes XML) payload with data related to the event.

  3. Security – Webhooks often use secret tokens, API keys, or signatures to verify that the request is legitimate.

  4. Response Handling – The server receiving the webhook may respond with a success (200 OK) or failure (e.g., 400, 500), and the sender might retry failed attempts.

 

Let’s learn about implementation of a simple webhook in Laravel. In Laravel, handling a webhook typically involves:

  1. Defining a route to receive the webhook request.

  2. Creating a controller to process the request.

  3. Verifying the request (if required).

  4. Processing the payload and provide responding appropriately.

 

Step 1: Define the Webhook Route

First, in routes/api.php or routes/web.php file, define a POST route:

use App\Http\Controllers\WebhookController;
use Illuminate\Support\Facades\Route;

Route::post('/webhook', [WebhookController::class, 'handleWebhook']);

Step 2: Create a Webhook Controller

Now we have to create that Webhook controller. Run the below command to create the controller:

php artisan make:controller WebhookController

Then, in app/Http/Controllers/WebhookController.php file we will write the required logic.

  • In handleWebhook method we are receiving a request.
  • We are saving full request body in our log file for future reference.
  • We may need to validate our request for sanitization and also for authentication or authorization purposes.
  • We are storing the event in a variable
  • Finally, based on event type, we are doing our necessary works and giving a success or error response.
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\Response;

class WebhookController extends Controller
{
    public function handleWebhook(Request $request)
    {
        // Log request for debugging
        \Log::info('Webhook received:', $request->all());

        // Validate signature (if required)
        // if ($request->header('X-Signature') !== 'your-secret-token') {
        //     return response()->json(['error' => 'Unauthorized'], 403);
        // }

        // Process the webhook payload
        $event = $request->input('event'); // Example payload key

        if ($event === 'user.created') {
            // Handle user creation event
        } elseif ($event === 'payment.success') {
            // Handle payment success event
        }

        // Respond to webhook sender
        return response()->json(['status' => 'success'], 200);
    }
}

Step 3: Test Your Webhook

You can use Postman or cURL to test the webhook:

curl -X POST https://yourdomain.com/webhook \
     -H "Content-Type: application/json" \
     -d '{"event": "user.created", "user": {"id": 1, "name": "John"}}'

Or, if you’re testing locally with Laravel’s built-in server, use:

curl -X POST http://127.0.0.1:8000/webhook \
     -H "Content-Type: application/json" \
     -d '{"event": "user.created", "user": {"id": 1, "name": "John"}}'

Step 4: Securing the Webhook (Optional)

To verify requests, many webhook providers send an HMAC signature in headers (e.g., X-Signature). You can validate it like this:

$signature = $request->header('X-Signature');
$secret = 'your-secret-key';
$expectedSignature = hash_hmac('sha256', $request->getContent(), $secret);

if (!hash_equals($expectedSignature, $signature)) {
    return response()->json(['error' => 'Invalid signature'], 403);
}

Step 5: Logging Webhook Requests (For Debugging)

If you want to log incoming webhooks for debugging, add this to handleWebhook():

\Log::info('Webhook received:', $request->all());

When producing (sending) and consuming (receiving) webhooks, there are several important considerations to ensure reliability, security, and scalability.

Producing Webhooks (Sending Webhooks)

If our Laravel app sends webhooks to other services, we should consider:

Retry Mechanism for Reliability

  • The receiving server might be down or slow. We should implement retry logic with exponential backoff mechanism.

  • Example: Retry after 10s, then 30s, then 1m, then 5m, etc.

  • Store failed webhooks in a queue job and retry later.

Example: Queueing Webhooks in Laravel

dispatch(new SendWebhookJob($payload))->delay(now()->addSeconds(10));

Laravel’s queue system ensures reliability.

Event-Driven Architecture

  • Instead of sending webhooks directly inside controllers, we can use Laravel Events & Listeners.

  • Example: If a user registers, fire a UserRegistered event and let listeners handle webhook sending.

Example: Dispatching an Event

event(new UserRegistered($user));

 

Secure Webhooks with Signatures

  • Webhooks should include an HMAC signature in the headers.

  • The receiver can verify the signature to ensure it’s authentic.

Example: Signing a Webhook in Laravel

$payload = json_encode($data);
$secret = env('WEBHOOK_SECRET');
$signature = hash_hmac('sha256', $payload, $secret);

Include the signature in headers when sending:

$headers = [
'Content-Type' => 'application/json',
'X-Signature' => $signature
];

Handling Delivery Failures

  • Log every webhook request, whether successful or failed.

  • Use a database table to track webhook status (pending, failed, success).

Example: Creating a sample Webhook Logs Table

php artisan make:migration create_webhook_logs_table
Schema::create('webhook_logs', function (Blueprint $table) {
    $table->id();
    $table->string('url');
    $table->json('payload');
    $table->integer('status')->default(0); // 0 = pending, 1 = success, 2 = failed
    $table->timestamps();
});

 Consuming Webhooks (Receiving Webhooks)

If our Laravel app receives webhooks, we should consider:

Validate Incoming Webhooks

  • Check HTTP headers for an authentication token or HMAC signature.

  • See below example:

$signature = $request->header('X-Signature');
$expectedSignature = hash_hmac('sha256', $request->getContent(), env('WEBHOOK_SECRET'));
if (!hash_equals($expectedSignature, $signature)) {
return response()->json(['error' => 'Invalid signature'], 403);
}

Respond Quickly

  • Webhook providers expect a fast response (200 OK) within a few seconds.

  • If processing takes time, we can queue the job and return 200 OK immediately.

Example: Queueing Webhook Processing

dispatch(new ProcessWebhookJob($request->all()));
return response()->json(['status' => 'received'], 200);

Idempotency (Avoid Duplicate Processing)

  • Some providers send the same webhook multiple times to ensure delivery.

  • Store received webhook event IDs in a database to avoid duplicate processing.

Example: Checking for Duplicate Webhooks

if (WebhookLog::where('event_id', $request->input('id'))->exists()) {
return response()->json(['status' => 'duplicate'], 200);
}

Logging for Debugging

  • Store all webhook requests in a log file or a database table for debugging.

  • Sample Laravel logging:

\Log::info('Webhook received:', $request->all());
  • Check logs in storage/logs/laravel.log.

Other common security best practices

  • Use HTTPS to prevent data interception.

  • Whitelist webhook sources (only allow requests from trusted IPs).

  • Rate limiting: Protect your API from spammy webhook requests.

Example: Laravel Rate Limiting

In app/Http/Middleware/ThrottleRequests.php:

RateLimiter::for('webhooks', function (Request $request) {
return Limit::perMinute(10);
});

Apply it to your webhook route:

Route::post('/webhook', [WebhookController::class, 'handle'])
->middleware('throttle:webhooks');

At a glance summary

✅ Feature📤 Producing Webhooks📥 Consuming Webhooks
Retry MechanismYes (queue & backoff)No (depends on provider)
Event-DrivenYes (use events)No (direct call)
Signature CheckYes (HMAC signing)Yes (validate)
Response TimeN/AFast! (200 OK immediately)
Duplicate HandlingNo (provider’s job)Yes (check event ID)
LoggingYesYes
SecurityAPI keys, HTTPSHTTPS, IP whitelisting

Hope this article will help you to teach about a complete Laravel example for webhook producer and consumer. 🚀

Editorial Staff

A Learner and trying to share what I learned!