Help us measure site usage?

With your permission, we use self-hosted analytics to understand page usage. It is optional and does not affect the Service. Learn more.

PHPintermediatebackendAI generated

Webhook Inbox

Build a Laravel service that receives webhooks from other systems, like payment or Git providers. Every incoming request is checked for a valid signature, so fake requests are rejected, then stored in PostgreSQL right away and answered quickly with 200. The actual processing happens later in a background queue, with retries if it fails. The same event delivered twice is recognized and processed only once. An API shows every event and its status: received, processing, processed or failed. You'll learn HMAC signature verification, idempotency, Laravel queues and jobs, migrations and Eloquent models, and why webhook receivers should acknowledge first and process later.

Estimate
~13h
Steps
7
Completed by
0
Proposed by
codeseed.app

Laravel · Composer · PostgreSQL

Project roadmap

  1. 01

    Set up Laravel and the database

    ~1.5h

    Create a Laravel project with Composer and configure it to use PostgreSQL through .env (commit .env.example only). Create a migration and an Eloquent model for webhook_events: id, source (for example github), external_event_id, event_type, payload (jsonb), status, attempts, last_error, received_at and processed_at, with a unique index on source plus external_event_id. Done when: php artisan migrate creates the table.

  2. 02

    Receive and verify webhooks

    ~2.5h

    Add POST /webhooks/{source}. Read each source's secret from config. Compute an HMAC-SHA256 of the raw request body with that secret and compare it with the signature header (for example X-Signature-256: sha256=...) using hash_equals, never ==. Reject missing or invalid signatures with 401 and bodies over 1 MB with 413. Exclude this route from CSRF protection. Done when: a correctly signed request is accepted and a tampered one gets 401.

  3. 03

    Store first, answer fast

    ~1.5h

    After verifying, save the event with status received and return 200 immediately, without doing any real processing in the request. Take the event id from the payload or a header, and if the same source and event id already exist, return 200 without storing it again. Done when: sending the same signed event twice creates only one row, and both requests get 200.

  4. 04

    Process events in a queue

    ~2.5h

    Use Laravel's database queue driver. After storing an event, dispatch a ProcessWebhookEvent job. The job sets the status to processing, runs a handler chosen by event type (write a simple example handler, such as logging the event summary), then sets it to processed. Unknown event types are marked processed with a note. Run the worker with php artisan queue:work. Done when: stored events are processed by the worker and their status changes.

  5. 05

    Retry failures with backoff

    ~1.5h

    Configure the job with $tries = 5 and a backoff array (for example 10, 30, 60, 120 seconds). On each failure, save the error message and increase attempts; after the last try, set the status to failed. Make handlers safe to run twice, since a retry can repeat work. Done when: a handler that throws an exception is retried and the event ends as failed with its error saved.

  6. 06

    Add a status API

    ~1.5h

    Add GET /events (filter by status and source, paginated), GET /events/{id} with the full payload and error, and POST /events/{id}/replay, which puts a failed event back in the queue. Protect these routes with a simple API token from config. Done when: failed events can be listed, inspected and replayed through the API.

  7. 07

    Write tests

    ~2h

    Write feature tests with PHPUnit or Pest: a valid signature is accepted, an invalid one is rejected, a duplicate event isn't stored twice, the job moves an event to processed, and a failing handler leaves it failed after the retries. Use Queue::fake or the sync queue driver in tests, and a separate test database. Done when: php artisan test passes.

Ready to build this?

Get a GitHub repo and start building. Your AI reviewer checks each step as you go.

~13h · 7 steps

Tech stack

LaravelComposerPostgreSQL