Skip to content

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
events - eu-central-1
Live tail
sample data
Sample event log
14:47:02.118stripe invoice.paidbilling-svc
Delivered
14:47:02.118stripe invoice.paidnotify-svc
Delivered
14:47:01.904shopify orders/createorders-svc
Retrying
14:46:59.310github pull_requestci-bridge
Delivered
14:46:58.002unknown POST /in/stripe -
Rejected
14:46:57.771adyen AUTHORISATIONpayments-svc
Failed
14:46:55.436twilio message.statuscomms-svc
Replayed

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
  1. 14:02:00Deploy starts. Old pods begin draining.
  2. 14:02:14Customer pays for Pro. Stripe sends invoice.paid.
  3. 14:02:14Old pod returns 200 and queues the job in memory.
  4. 14:02:15Pod receives SIGTERM. The in-memory job is gone.
  5. 14:02:15Stripe saw a 200. As far as it knows, you have the event.
  6. 14:40:31Support ticket: “I paid, but my account still says Free.”

With Marevans in front

event delivered
  1. 14:02:00Deploy starts. Old pods begin draining.
  2. 14:02:14invoice.paid arrives. Signature verified.
  3. 14:02:14Event written to durable storage, then 200 sent to Stripe.
  4. 14:02:14Delivery to billing-svc: 502. Retry in 10 s.
  5. 14:02:25Delivery to billing-svc: 503. Retry in 30 s.
  6. 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.

How webhooks flow through MarevansProviders send webhooks into your cloud environment. Signatures are verified, forged requests get 401. Events are persisted to a durable queue, then routed to your services. Failures retry with backoff, then move to a dead-letter queue, from which they can be replayed.stripeshopifygithubany sourceYour cloud · your regionVerifysignature + time401rejectedDurable queuepersist, then 2xxRouterules, fan-outbilling-svcnotify-svcledger-svcretryDead-letter queueinspect, fixreplaylogged, measured, alertable
Everything inside the dashed boundary runs in infrastructure you control.
  1. 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.

  2. 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.

  3. 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.

  4. Deliver and retry

    Deliveries retry with exponential backoff and jitter. Idempotency keys stop duplicates from reaching your code twice.

  5. 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.yaml
yaml
# 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: 72h​routes:  - name: stripe-invoice-paid    match:      source: stripe      type: invoice.paid    deliver:      - billing-svc      - notify-svc​destinations:  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_letter
Illustrative configuration. The full schema is in the docs.

What 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

dead-letter queue - billing-svc
status:failed destination:billing-svcreceived 14:02–14:47 UTC
1,284 events match

invoice.paid

Failed · in DLQ

evt_1QxT8r2eZvKYlo2C4bW9kPzA

Sends this event to billing-svc again as a new attempt
Source
stripe
Destination
billing-svc
Received
14:02:14.198 UTC
Signature
Verified · v1
Idempotency key
payload.id
Attempts
12 / 12
  1. Attempt 150041 ms
    14:02:14.201 · next in 10 s
  2. Attempt 250038 ms
    14:02:24.388 · next in 20 s
  3. Attempt 350044 ms
    14:02:44.912 · next in 40 s
  4. attempts 4–10 · 500 · backoff to 1 h
  5. Attempt 1150039 ms
    16:27:26.057 · next in 1 h
  6. Attempt 1250042 msFailed
    17: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

Ingress + verify
Queue + storage
Delivery workers
KMS / key vault (yours)
your services

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.yaml
yaml
# retention.yamlretention:  payloads: 30d            # bodies purged after 30 days  metadata: 400d           # ids, status, attempts, timings  dead_letter: 90d​redaction:  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 joinable

Self-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.

  1. Step 1

    License

    Choose a plan by event volume. We send install credentials and deployment guides for your platform.

  2. Step 2

    Deploy

    Apply the module or chart in AWS, GCP, Azure, or your Kubernetes cluster. Pick region and network layout.

  3. 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.