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.

RubyintermediatebackendAI generated

Sinatra Webhook Inbox

Build a small Ruby web service that acts as an inbox for webhooks, the notifications services like Stripe or GitHub send when something happens. It receives each event, checks its signature so only the real sender is trusted, saves it immediately and answers quickly; the actual processing happens separately, can fail, and can be retried later without handling the same event twice. An endpoint lists events by status and lets you replay failed ones. The service is built with Sinatra, a lightweight Ruby web framework, with gems managed by Bundler. You'll learn receiving webhooks correctly, HMAC signature checks, idempotency, retries with backoff, and testing a web service with Rack::Test.

Estimate
~10.5h
Steps
6
Completed by
0
Proposed by
codeseed.app

Sinatra · Sequel · SQLite · Rack::Test

Project roadmap

  1. 01

    Define how the inbox works

    ~1.5h

    Write README.md: POST /webhooks/:source receives events; the signature is an HMAC-SHA256 of the raw body with a shared secret, sent in a header; each event is stored with its source, event id (from the payload), payload, status (received, processing, processed or failed), attempts and last error. Duplicates (same source and event id) are acknowledged but stored once. Done when: README.md describes the endpoints, statuses and duplicate handling.

  2. 02

    Receive and store events

    ~2h

    Create a Sinatra app with a Gemfile. Store events in SQLite through the sequel gem, with a unique index on source plus event id. Read the raw body, verify the signature with OpenSSL::HMAC and a constant-time comparison (Rack::Utils.secure_compare), reject bad signatures with 401, parse the JSON, save the event and return 200 right away. Done when: a correctly signed event is stored, and a wrongly signed one gets 401.

  3. 03

    Handle duplicates

    ~1h

    When an event with the same source and id arrives again (senders retry when they don't get an answer in time), return 200 without storing or processing it again, relying on the unique index. Done when: sending the same event three times stores it once and returns 200 each time.

  4. 04

    Process events with retries

    ~2.5h

    Write a processor, run as a separate script or a background thread, that picks received events, marks them processing, and calls a handler for their type (for example logging a payment). On success, mark them processed; on failure, save the error and schedule a retry with increasing waits (1, 5, 25 minutes), up to 5 attempts, then mark them failed. Make handlers safe to run twice. Done when: a handler that fails twice then succeeds ends as processed after retries.

  5. 05

    List and replay events

    ~1.5h

    Add GET /events?status=&source= (paginated) and GET /events/:id showing the payload, attempts and errors, protected with an admin token. Add POST /events/:id/replay, which puts a failed event back in the queue. Done when: failed events can be found and replayed through the API.

  6. 06

    Test it

    ~2h

    Write tests with RSpec (or Minitest) and Rack::Test: valid and invalid signatures, duplicates, the processor's retries and failure after the last attempt, replay, and the admin token check. Use a temporary database per test run. Done when: all tests pass.

Ready to build this?

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

~10.5h · 6 steps

Tech stack

SinatraSequelSQLiteRack::Test