Skip to content

Scalable webhook delivery system with event ingestion API, PostgreSQL storage, Redis queue, and async worker pools. Handles reliable event delivery with full audit trails. Building towards production-ready retry logic and HMAC signing.

Notifications You must be signed in to change notification settings

Flack74/Webhook-Delivery-Platform

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

10 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸš€ Webhook Delivery Platform

A production-grade, scalable webhook delivery system built with Go, PostgreSQL, and Redis

Go Version PostgreSQL Redis License


πŸ“‹ Table of Contents


🎯 Overview

The Webhook Delivery Platform is a robust, system designed to reliably deliver webhook events to customer endpoints. Inspired by production systems like Svix, Stripe, and Convoy, this platform handles event ingestion, persistent storage, asynchronous delivery, retry logic with exponential backoff, and comprehensive observability.

Why This Project?

  • πŸ”’ Reliability First: Events are never lost, even during system failures
  • ⚑ High Performance: Asynchronous processing with worker pools
  • πŸ”„ Smart Retries: Exponential backoff with jitter for transient failures
  • πŸ“Š Full Observability: Complete audit trail of every delivery attempt
  • πŸ›‘οΈ Security: HMAC-SHA256 signing for webhook authenticity
  • 🎯 Production Ready: Built with real-world patterns and best practices

✨ Features

βœ… Phase 4 Complete: Durable Queue & Async Delivery

  • Event Ingestion API - Accept webhook events via POST /events
  • Payload Validation - JSON schema validation with Gin bindings
  • PostgreSQL Persistence - Events stored as JSONB for flexibility
  • UUID Generation - Unique identifiers for all events
  • Delivery Tracking - Separate table for delivery attempts and status
  • Database Migrations - Version-controlled schema management
  • Docker Compose Setup - One-command local development environment
  • Health Checks - PostgreSQL readiness probes
  • Error Handling - Comprehensive error responses
  • Redis Queue Integration - Durable message queue with AOF persistence
  • Worker Pool - 3 concurrent workers for parallel delivery
  • Asynchronous Processing - Decoupled ingestion from delivery
  • Crash Recovery - Automatic recovery of stalled jobs on restart
  • Reliable Queue Pattern - BRPopLPush for atomic job processing
  • Job Acknowledgment - Nack/Ack pattern for retry handling

🚧 Coming Soon

  • Retry Logic - Exponential backoff (5s β†’ 5m β†’ 30m β†’ 2h β†’ 12h)
  • HMAC Signing - Cryptographic signatures for webhook security
  • Dead Letter Queue - Handle permanently failed deliveries
  • Metrics & Monitoring - Prometheus metrics and Grafana dashboards
  • Rate Limiting - Protect customer endpoints from overload
  • Admin Dashboard - Web UI for event inspection and replay

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Client Application                       β”‚
β”‚                  (Sends webhook events)                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   API Service (Gin)                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  POST /events                                        β”‚   β”‚
β”‚  β”‚  β€’ Validate JSON payload                             β”‚   β”‚
β”‚  β”‚  β€’ Generate UUID                                     β”‚   β”‚
β”‚  β”‚  β€’ Store in PostgreSQL                               β”‚   β”‚
β”‚  β”‚  β€’ Create delivery record                            β”‚   β”‚
β”‚  β”‚  β€’ Enqueue to Redis                                  β”‚   β”‚
β”‚  β”‚  β€’ Return 202 Accepted                               β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    PostgreSQL Database                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚
β”‚  β”‚  events          β”‚      β”‚  deliveries              β”‚     β”‚
β”‚  β”‚  β€’ id (UUID)     │◄─────│  β€’ id (UUID)             β”‚     β”‚
β”‚  β”‚  β€’ event_type    β”‚      β”‚  β€’ event_id (FK)         β”‚     β”‚
β”‚  β”‚  β€’ payload       β”‚      β”‚  β€’ endpoint_url          β”‚     β”‚
β”‚  β”‚  β€’ created_at    β”‚      β”‚  β€’ status                β”‚     β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚  β€’ attempts              β”‚     β”‚
β”‚                            β”‚  β€’ next_retry_at         β”‚     β”‚
β”‚                            β”‚  β€’ last_error            β”‚     β”‚
β”‚                            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         Redis Queue (AOF Persistence Enabled)               β”‚
β”‚  β€’ BRPopLPush for reliable delivery                         β”‚
β”‚  β€’ Crash recovery on startup                                β”‚
β”‚  β€’ 3 concurrent workers                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Worker Pool (3 Goroutines)                     β”‚
β”‚  β€’ Concurrent HTTP POST to customer endpoints               β”‚
β”‚  β€’ Ack on success / Nack on failure                         β”‚
β”‚  β€’ Update delivery status in PostgreSQL                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ› οΈ Tech Stack

Component Technology Purpose
Language Go 1.25.6 High-performance, concurrent backend
Web Framework Gin Fast HTTP router and middleware
Database PostgreSQL 15 Persistent event and delivery storage
Queue Redis 7 (AOF) Durable message queue with persistence
Driver pgx/v5 Native PostgreSQL driver with connection pooling
Containerization Docker & Docker Compose Local development environment
JSON Handling encoding/json Event payload serialization
UUID uuid Unique event identifiers
Queue Pattern BRPopLPush Reliable queue with crash recovery

πŸš€ Getting Started

Prerequisites

  • Go 1.25.6 or higher
  • Docker & Docker Compose
  • PostgreSQL 15 (via Docker)
  • Make (optional, for convenience commands)

Installation

  1. Clone the repository

    git clone https://github.com/Flack74/Webhook-Delivery-Platform.git
    cd Webhook-Delivery-Platform
  2. Set up environment variables

    cp .env.example .env

    Edit .env:

    DB_NAME=webhook_delivery_platform
    DB_USER=webhookuser
    DB_PASSWORD=webhook123
    DB_PORT=5432
    DB_HOST=localhost
  3. Start PostgreSQL with Docker

    docker compose --env-file .env up -d
  4. Wait for database to be ready

    sleep 5
  5. Run database migrations

    PGPASSWORD=webhook123 psql -h localhost -U webhookuser -d webhook_delivery_platform -f migrations/001_init.sql
  6. Install Go dependencies

    go mod download
  7. Run the application

    DB_NAME=webhook_delivery_platform \
    DB_USER=webhookuser \
    DB_PASSWORD=webhook123 \
    DB_PORT=5432 \
    DB_HOST=localhost \
    go run cmd/api/main.go
  8. Verify the server is running

    curl http://localhost:8000/health

πŸ“‘ API Documentation

Create Event

Endpoint: POST /events

Description: Accept a webhook event, validate the payload, and store it in the database.

Request Headers:

Content-Type: application/json

Request Body:

{
  "event_type": "user.created",
  "data": {
    "id": "usr_123",
    "email": "user@example.com"
  }
}

Response: 202 Accepted

{
  "status": "accepted",
  "event_id": "550e8400-e29b-41d4-a716-446655440000"
}

Error Response: 400 Bad Request

{
  "error": "Key: 'Event.EventType' Error:Field validation for 'EventType' failed on the 'required' tag"
}

Example with cURL:

curl -X POST http://localhost:8000/events \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "user.created",
    "data": {
      "id": "usr_123",
      "email": "user@example.com"
    }
  }'

πŸ—„οΈ Database Schema

Events Table

Stores all incoming webhook events immutably.

CREATE TABLE events (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    event_type TEXT NOT NULL,
    payload JSONB NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX idx_events_event_type ON events (event_type);
Column Type Description
id UUID Unique event identifier
event_type TEXT Event category (e.g., "user.created")
payload JSONB Flexible JSON payload
created_at TIMESTAMPTZ Event creation timestamp

Deliveries Table

Tracks delivery attempts for each event to customer endpoints.

CREATE TABLE deliveries (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    event_id UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE,
    endpoint_url TEXT NOT NULL,
    status TEXT NOT NULL CHECK (status IN ('pending', 'in_progress', 'succeeded', 'failed')),
    attempts INTEGER NOT NULL DEFAULT 0,
    next_retry_at TIMESTAMPTZ,
    last_error TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (event_id, endpoint_url)
);

CREATE INDEX idx_deliveries_pending ON deliveries (status, next_retry_at);
Column Type Description
id UUID Unique delivery identifier
event_id UUID Foreign key to events table
endpoint_url TEXT Customer webhook endpoint
status TEXT Delivery status (pending/in_progress/succeeded/failed)
attempts INTEGER Number of delivery attempts
next_retry_at TIMESTAMPTZ Scheduled retry time
last_error TEXT Last error message
created_at TIMESTAMPTZ Delivery record creation time
updated_at TIMESTAMPTZ Last update time

πŸ“ Project Structure

Webhook-Delivery-Platform/
β”œβ”€β”€ cmd/
β”‚   β”œβ”€β”€ api/
β”‚   β”‚   └── main.go              # API server entry point
β”‚   └── receiver/
β”‚       └── main.go              # Test webhook receiver (Phase 1)
β”œβ”€β”€ internal/
β”‚   β”œβ”€β”€ handler/
β”‚   β”‚   β”œβ”€β”€ events.go            # Event creation handler
β”‚   β”‚   └── routes.go            # Route definitions
β”‚   β”œβ”€β”€ repository/
β”‚   β”‚   β”œβ”€β”€ events.go            # Event database operations
β”‚   β”‚   β”œβ”€β”€ deliveries.go        # Delivery database operations
β”‚   β”‚   └── postgres.go          # PostgreSQL connection setup
β”‚   β”œβ”€β”€ queue/
β”‚   β”‚   └── queue.go             # Redis queue (Phase 4)
β”‚   └── worker/
β”‚       └── worker.go            # Delivery worker pool (Phase 4)
β”œβ”€β”€ migrations/
β”‚   β”œβ”€β”€ 001_init.sql             # Initial schema
β”‚   └── 002_drop.sql             # Rollback script
β”œβ”€β”€ .env                         # Environment variables
β”œβ”€β”€ .gitignore
β”œβ”€β”€ docker-compose.yml           # PostgreSQL container
β”œβ”€β”€ go.mod                       # Go dependencies
β”œβ”€β”€ go.sum
β”œβ”€β”€ Architecture.md              # Detailed architecture diagram
└── README.md                    # This file

🎯 Development Phases

This project follows an incremental build approach, implementing one concept at a time.

βœ… Phase 0: Event Ingestion (Complete)

  • HTTP server with Gin
  • POST /events endpoint
  • Payload validation
  • 202 Accepted response

βœ… Phase 1: Synchronous Delivery (Complete)

  • Test webhook receiver
  • Direct HTTP POST to endpoint
  • Success/failure logging

βœ… Phase 2: Asynchronous Boundary (Complete)

  • In-memory queue with Go channels
  • Worker goroutine
  • Decoupled ingestion from delivery

βœ… Phase 3: Persistent Storage (Complete)

  • PostgreSQL integration
  • Event and delivery tables
  • Database migrations
  • Connection pooling with pgx

βœ… Phase 4: Durable Queue (Complete)

  • Redis integration with AOF persistence
  • Durable message queue (BRPopLPush pattern)
  • Worker pool (3 concurrent workers)
  • Job acknowledgment (Ack/Nack)
  • Crash recovery on startup
  • Enhanced error logging

πŸ“… Phase 5: Retry & Backoff (Planned)

  • Exponential backoff algorithm
  • Retry scheduling
  • Max attempt limits
  • Transient vs permanent failure detection

πŸ“… Phase 6: Observability (Planned)

  • Delivery attempt logging
  • Dead letter queue
  • Metrics (Prometheus)
  • Grafana dashboards

πŸ“… Phase 7: Security (Planned)

  • HMAC-SHA256 signing
  • Timestamp validation
  • Replay attack prevention

πŸ“… Phase 8: Production Features (Planned)

  • Rate limiting
  • Manual replay API
  • Admin dashboard
  • Docker deployment

...


βš™οΈ Configuration

Environment Variables

Variable Description Default
DB_NAME PostgreSQL database name webhook_delivery_platform
DB_USER PostgreSQL username webhook8me
DB_PASSWORD PostgreSQL password webhooks74621
DB_PORT PostgreSQL port 5432
DB_HOST PostgreSQL host localhost
GIN_MODE Gin mode (debug/release) debug

Database Connection String

The application constructs the connection string as:

postgres://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?sslmode=disable

πŸ§ͺ Testing

Manual Testing

  1. Start the server

    go run cmd/api/main.go
  2. Send a test event

    curl -X POST http://localhost:8000/events \
      -H "Content-Type: application/json" \
      -d '{
        "event_type": "order.completed",
        "data": {
          "id": "order_789",
          "email": "customer@example.com"
        }
      }'
  3. Verify in database

    PGPASSWORD=webhooks74621 psql -h localhost -U webhook8me -d webhook_delivery_platform
    SELECT * FROM events ORDER BY created_at DESC LIMIT 5;
    SELECT * FROM deliveries ORDER BY created_at DESC LIMIT 5;

Unit Tests (Coming Soon)

go test ./...

πŸ—ΊοΈ Roadmap

  • Phase 0: Event ingestion API
  • Phase 1: Synchronous webhook delivery
  • Phase 2: Asynchronous processing
  • Phase 3: PostgreSQL persistence
  • Phase 4: Redis queue integration
  • Phase 5: Retry logic with exponential backoff
  • Phase 6: Delivery attempt logging & DLQ
  • Phase 7: HMAC-SHA256 signing
  • Phase 8: Admin dashboard & metrics
  • Phase 9: Multi-endpoint support
  • Phase 10: Rate limiting & circuit breakers
  • Phase 11: Kubernetes deployment
  • Phase 12: Horizontal scaling & load balancing

🀝 Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


πŸ™ Acknowledgments


πŸ“ž Contact

Flack74 - @Flack74

Project Link: https://github.com/Flack74/Webhook-Delivery-Platform


⭐ Star this repo if you find it helpful!

Made with ❀️ By Flack74

About

Scalable webhook delivery system with event ingestion API, PostgreSQL storage, Redis queue, and async worker pools. Handles reliable event delivery with full audit trails. Building towards production-ready retry logic and HMAC signing.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages