Self-hosted webhook gateway
Never lose a webhook again.
Marevans sits between your providers and your services. It verifies every signature, stores every event before acknowledging it, and delivers with retries you control. When a delivery fails, you see why and replay it.
- Deploy on any major cloud
- Your network, your keys
- At-least-once with dedupe
| Received (UTC) | Event | Destination | Status |
|---|---|---|---|
| 14:47:02.118 | stripe invoice.paid | billing-svc | Delivered201 · 38 ms |
| 14:47:02.118 | stripe invoice.paid | notify-svc | Delivered202 · 51 ms |
| 14:47:01.904 | shopify orders/create | orders-svc | 503 · retry in 40 s |
| 14:46:59.310 | github pull_request | ci-bridge | Delivered200 · 22 ms |
| 14:46:58.002 | unknown POST /in/stripe | - | Rejected401 · bad signature |
| 14:46:57.771 | adyen AUTHORISATION | payments-svc | Failed500 · 12/12 → DLQ |
| 14:46:55.436 | twilio message.status | comms-svc | Replayed200 · replay |
The problem
A payment succeeds. The webhook doesn’t.
Most webhook endpoints do the work inline or hand it to an in-memory queue. That works until a deploy, a slow database, or a crashed pod lands at the wrong second. Then the event is gone, and nothing on your side knows it existed.
Today: a single endpoint
event lost- 14:02:00Deploy starts. Old pods begin draining.
- 14:02:14Customer pays for Pro. Stripe sends invoice.paid.
- 14:02:14Old pod returns 200 and queues the job in memory.
- 14:02:15Pod receives SIGTERM. The in-memory job is gone.
- 14:02:15Stripe saw a 200. As far as it knows, you have the event.
- 14:40:31Support ticket: “I paid, but my account still says Free.”
With Marevans in front
event delivered- 14:02:00Deploy starts. Old pods begin draining.
- 14:02:14invoice.paid arrives. Signature verified.
- 14:02:14Event written to durable storage, then 200 sent to Stripe.
- 14:02:14
- 14:02:25
- 14:02:56Delivery to billing-svc: 200. Subscription active.
- No record
- There is no copy of the event on your side. You find out from the customer.
- No history
- You can’t see what was attempted, when, or what your service returned.
- No way back
- No button to send it again. Someone digs through the provider dashboard, one event at a time.
How it works
A durable queue between the internet and your code.
Providers talk to Marevans. Marevans talks to your services. Everything in between is stored, retried, and visible.
Receive and verify
Each source gets its own endpoint. Signatures and timestamps are checked before anything is stored. Forged or replayed requests get a 401.
Persist, then acknowledge
The event is written to durable storage first. Only then does the provider get its 2xx. If your app is down, nothing is lost.
Route and fan out
Rules match on source, event type, or payload fields. One event can go to several services, each with its own retry policy.
Deliver and retry
Deliveries retry with exponential backoff and jitter. Idempotency keys stop duplicates from reaching your code twice.
Dead-letter and replay
When attempts run out, the event moves to the dead-letter queue with its full history. Fix the cause, then replay one or thousands.
Config is a file in your repo. This one sends Stripe invoice.paid to billing-svc and notify-svc.
# routes.yamlsources: stripe: path: /in/stripe verify: scheme: stripe secret: ${ssm:/webhooks/stripe/signing-secret} tolerance: 300s dedupe: key: payload.id # evt_… is unique per event window: 72hroutes: - name: stripe-invoice-paid match: source: stripe type: invoice.paid deliver: - billing-svc - notify-svcdestinations: billing-svc: url: http://billing.internal:8080/webhooks/stripe timeout: 10s retry: max_attempts: 12 backoff: exponential # 10s, 20s, 40s … capped at 1h initial_interval: 10s max_interval: 1h on_exhausted: dead_letter notify-svc: url: http://notify.internal:8080/events timeout: 5s retry: max_attempts: 6 backoff: exponential on_exhausted: dead_letterWhat you get
The webhook plumbing you keep rebuilding, done once.
Each piece is something a team usually builds after an incident. Here they ship together and share one event log.
Signature verification
Provider signatures and timestamps are checked at the edge. Forged and stale requests are rejected before they reach storage.
Stripe-Signature · X-Hub-Signature-256 · HMAC
Durable queuing
Every verified event is persisted before the provider gets a 2xx. Your app can be down, slow, or mid-deploy.
persist → ack → deliver
Retries with backoff
Per-destination timeouts, attempt limits, and exponential backoff with jitter. No more hand-rolled retry loops.
10s · 20s · 40s · … · 1h
Deduplication
Idempotency keys from headers or payload fields mean a provider retry doesn’t become a double charge in your system.
dedupe.key: payload.id
Routing and fan-out
Match on source, event type, or payload fields. Send one event to billing, notifications, and your warehouse at once.
1 inbound → n destinations
Dead-letter queue
Failed events land in a queue you can browse. See the request, every attempt, and the response your service sent back.
on_exhausted: dead_letter
Replay
Replay a single event while debugging, or every failed event from an outage window, in one action.
by id · by filter · by time range
Observability
Searchable event log, delivery success rate, latency, queue depth, and failures by destination. Alerts when they drift.
success · p95 latency · queue depth
Retention and redaction
Mask card numbers, emails, or any field before storage. Purge payloads on a schedule and keep only metadata.
mask · hash · drop · purge after 30d
Dead-letter queue
See exactly what failed. Fix it. Replay it.
A migration broke billing-svc at 14:02. Every invoice.paid event after that exhausted its retries. Open one, read the 500 your service returned, ship the fix, then replay the whole window.
Interactive mockup · sample data
- invoice.paid14:02:14evt_1QxT8r2e…PzA12/12
- invoice.paid14:02:31evt_1QxT9a4f…m2Q12/12
- customer.subscription.updated14:02:31evt_1QxTAc7k…Z8c12/12
- invoice.paid14:03:02evt_1QxTB70p…r4T12/12
- invoice.paid14:03:40evt_1QxTCx3b…aa112/12
- + 1,279 more
invoice.paid
Failed · in DLQevt_1QxT8r2eZvKYlo2C4bW9kPzA
- Source
- stripe
- Destination
- billing-svc
- Received
- 14:02:14.198 UTC
- Signature
- Verified · v1
- Idempotency key
- payload.id
- Attempts
- 12 / 12
- Attempt 150041 ms14:02:14.201 · next in 10 s
- Attempt 250038 ms14:02:24.388 · next in 20 s
- Attempt 350044 ms14:02:44.912 · next in 40 s
- attempts 4–10 · 500 · backoff to 1 h
- Attempt 1150039 ms16:27:26.057 · next in 1 h
- Attempt 1250042 msFailed17:27:25.740 · moved to dead-letter queue
- Full request, every attempt
- Headers, body, signature result, and the status code and response body from each delivery attempt.
- Replay one or thousands
- Replay a single event, a filtered set, or a time range. Bulk replays are rate limited so you don’t knock over the service you just fixed.
- Replays are traceable
- A replay is a new attempt on the same event, with the same idempotency key and a replay flag. The history shows who ran it and when.
Sources
Built-in verification for the providers you already use.
Each provider signs webhooks differently. Marevans knows the common schemes, and anything else can use HMAC with a shared secret.
Stripe
invoice.paid · customer.subscription.updated
Adyen
AUTHORISATION · REFUND
PayPal
PAYMENT.CAPTURE.COMPLETED
Shopify
orders/create · orders/paid
GitHub
push · pull_request · workflow_run
Twilio
message status callbacks
HubSpot
contact.propertyChange
Salesforce
outbound messages
Any HTTP source
Internal services, partners, or providers not listed here. Verify with HMAC-SHA256 over the body, a shared secret header, or an IP allowlist.
Data residency and privacy
Your payloads stay in your account, in your region.
Webhook bodies carry names, emails, addresses, and payment details. Sending them through another company’s infrastructure adds a processor to every privacy review. Here there isn’t one.
Your cloud · your region · your network
In: providers over HTTPS to your endpoint.
Out: delivery to internal services only. No relay through Marevans cloud.
Runs in your environment
Container images you deploy into your VPC or cluster. Ingress, queue, and storage stay on infrastructure you control.
No third-party processor
Payloads are received, stored, and delivered inside your boundary. Marevans never receives them.
Encrypted with your keys
Use your cloud KMS or equivalent for data at rest. TLS in transit, including to your services.
Retention you set
Mask or drop fields before storage. Purge payloads on a schedule and keep only delivery metadata.
Pick the region that matches your compliance needs.
Supported on Amazon Web Services, Google Cloud, Microsoft Azure, Kubernetes (any cluster). Examples:
AWS
- us-east-1 · N. Virginia
- eu-central-1 · Frankfurt
- ap-southeast-1 · Singapore
Google Cloud
- us-central1 · Iowa
- europe-west1 · Belgium
- asia-northeast1 · Tokyo
Azure
- eastus · Virginia
- westeurope · Netherlands
- southeastasia · Singapore
Mask card and email fields. Purge payloads after 30 days.
# retention.yamlretention: payloads: 30d # bodies purged after 30 days metadata: 400d # ids, status, attempts, timings dead_letter: 90dredaction: apply: before_storage # redacted fields never hit disk rules: - match: source: "*" fields: - path: $..card.number action: drop - path: $..card.cvc action: drop - path: $..email action: mask # stored as "[redacted]" - path: $..phone action: hash # sha256, still joinableSelf-hosting doesn’t make you GDPR compliant on its own. It does keep personal data in infrastructure and a region you control, which removes a processor from your data map and shortens the review. Read the security overview.
Deployment
Deploy on the cloud you already use.
Marevans is self-hosted software. You choose the provider, region, and network. We provide images, templates, and support for production rollouts.
- Amazon Web Services
- Google Cloud
- Microsoft Azure
- Kubernetes (any cluster)
Your cloud, your contract
Run Marevans in the environment you already operate. No multi-tenant relay that copies payloads through our infrastructure.
Same gateway everywhere
One configuration model for verify, queue, retry, and replay whether you ship on managed Kubernetes or VMs behind a load balancer.
Container-native
Deploy from published images with Helm or Terraform modules. Scale replicas per availability zone like any stateless service.
Step 1
License
Choose a plan by event volume. We send install credentials and deployment guides for your platform.
Step 2
Deploy
Apply the module or chart in AWS, GCP, Azure, or your Kubernetes cluster. Pick region and network layout.
Step 3
Point a provider at it
Swap one webhook URL. Verified events appear in the log immediately.
On-prem and hybrid setups are supported when you can run containers and expose HTTPS ingress to webhook providers.
FAQ
Questions engineers ask first.
How is this different from a hosted webhook service?
A hosted service receives your webhooks on its own infrastructure, which means your payloads pass through another company. This runs in your cloud or cluster. You operate it, and in exchange no third party sees the data.
Is delivery exactly-once?
No system can promise that over HTTP. Delivery is at-least-once, with deduplication on the idempotency key you configure. Every delivery carries that key in a header, so your handler can ignore a repeat if one ever gets through.
What happens if Marevans itself is down?
Run at least two replicas across Availability Zones. A provider only gets a 2xx after the event is persisted, so an event is either stored or the provider knows it failed. Most providers retry failed deliveries on their own schedule.
Do I need to change my application code?
Usually not. Point the provider at your Marevans endpoint. Your service receives the original body and headers, plus X-Marevans-Event-Id and X-Marevans-Idempotency-Key.
Can we keep everything in the EU?
Yes. Deploy in an EU region and payloads are received, stored, and delivered there. This helps with GDPR and data residency reviews. It doesn’t replace your own assessment.
How is it priced?
In tiers by monthly event volume. See the pricing page for the plan structure.
Put a durable queue in front of every webhook.
Deploy on AWS, Google Cloud, Azure, or Kubernetes. Point one provider at the gateway and watch the event log fill in.