Go Webhook Receiver Starter

A small Go service that receives webhooks safely: verified, deduplicated, stored durably and retried, with your code in one function.

Buy on Polar: $24 One-time purchase · v0.2.1 · instant download via Polar

Go Webhook Receiver Starter cover
Output from a real test run, not a mock-up.

What you get

A complete service you can read in an afternoon, not a framework. Your logic goes in one function, handle(); everything around it is already done and tested.

sender --POST--> /webhook/{source}
                   1. read the body (size limit)
                   2. verify the HMAC signature   -> 401 if wrong, nothing stored
                   3. dedupe by delivery id       -> the same event twice is handled once
                   4. insert into SQLite          -> 202 Accepted, fast
worker ----------> handle(event)   retries with backoff, dead after N attempts

Features

  • Three signature schemes. GitHub style, Stripe style (with a replay window) and Standard Webhooks, the scheme Polar uses. Constant-time comparison.
  • Secret rotation. WEBHOOK_SECRET takes a comma-separated list, so old and new secrets are both accepted during a rotation and no events are dropped.
  • Deduplication. By delivery id (the webhook-id header is used automatically for Standard Webhooks) or, if there is none, by a hash of the body. Backed by a uniqueness constraint, so it survives restarts.
  • Durable queue. SQLite in WAL mode; the request is acknowledged after one commit. Exponential backoff, dead-letter after MAX_ATTEMPTS, and recovery of events left in processing by a crash.
  • Concurrency. WORKERS=N workers; an event is never given to two workers.
  • Operations. server replay for dead events, server purge and RETENTION_DAYS for old rows, /metrics in Prometheus format, a JSON access log with request ids that never contains payloads, headers or secrets.
  • Safe handlers. worker.Permanent(err) parks a payload that can never succeed without burning retries. A shutdown mid-handler returns the event to the queue without losing an attempt.
  • Examples. A Polar order.paid handler and a generic JSON router, both with tests. Dockerfile and systemd unit included.

Pure Go (no cgo). Dependencies: the standard library and modernc.org/sqlite.

Measured

Method: the included load tester (make loadtest) against the compiled binary on an Apple Silicon laptop, 32 connections, 512-byte payloads, real HTTP over loopback, default durability (WAL, synchronous=FULL), 5,000 events per run, three runs.

WhatResult
Events accepted over HTTP (verify, durable insert, 202), GitHub styleabout 7,200 to 9,200 per second (p50 2.7 ms, p99 about 17 ms)
Same, Standard Webhooksabout 9,400 per second
End to end, from first request to empty queue (no-op handler), 1 workerabout 3,100 to 3,200 per second
Queue drain, handler that sleeps 5 ms (like a network call), 1 / 8 / 32 workersabout 163 / 1,463 / 5,264 per second

The database is the main cost, not HTTP: all SQLite access goes through one connection, so extra workers pay off when your handler waits on something such as a network call or another database.

How it was tested

make test runs go vet and 41 tests with the race detector. They cover tampered, stale, future and malformed signatures, the Standard Webhooks known-answer vector from the reference Go library, secret rotation, 401 storing nothing, duplicate deliveries, oversize and wrong-method requests, retries and backoff, dead-letter, crash recovery, ordering, many workers never processing an event twice, replay, purge and upgrading a 0.1.0 database. The compiled binary, built from the packaged zip, was also exercised with real signed HTTP requests for all three schemes, including a replay of the same delivery.

Who it is for

For developers who need to receive webhooks from Polar, Stripe, GitHub or similar and want the unglamorous parts done right: signatures, retries, dedupe and a queue that survives a crash.

It is built for one server: a single process, a single SQLite writer, events handled in order by default. Delivery is at-least-once, so your handler should be idempotent.

FAQ

Does it verify Polar webhooks?

Yes. Polar uses Standard Webhooks, which is supported, and the starter includes an example handler for Polar order events.

Can I use only the signature code?

Yes. It is available separately as a small MIT-licensed module, webhooksig; see the free tools page.

Do I need CGO or a C compiler?

No. The SQLite driver is pure Go.

Which Go version?

Go 1.24 or newer.

What is the license?

Use it in your own projects. Do not resell the source as a competing starter.

Changelog

  • 0.2.1: SQLite driver updated to v1.44.0 (Go 1.24 or newer required).
  • 0.2.0: Standard Webhooks, secret rotation, multiple workers, replay and purge commands, /metrics, access log, Polar example, measured numbers.
  • 0.1.0: first release.

Read more

Get it

Buy on Polar: $24

Payment, tax and invoices are handled by Polar as merchant of record. You receive a download link after checkout.