Real-time event delivery

Webhooks that
actually
get delivered

Paymentgate sends signed HTTPS POST events the moment anything changes — payment.succeeded, subscription.renewed, dispute.opened, and 40+ more. With guaranteed delivery, automatic retries, and a built-in test console, your integrations stay in sync.

Built into

NordCommerce AB · HelsiPay Oy · Stockholm Games AB · Riga Marketplace SIA
Webhook Delivery Log
Live
200 payment.succeeded 142ms just now
200 subscription.renewed 98ms 2s ago
503 payment.succeeded retry #1
200 payment.succeeded 183ms retry #2 ✓
200 refund.created 74ms 14s ago
dispute.opened queued
200 fraud.flagged 61ms 28s ago
200 payout.paid 118ms 1m ago
Last 24 h: 28,471 events · 99.97% delivered View all →
99.97%
Delivery success rate (30-day avg)
<300ms
P99 event-to-delivery latency
40+
Event types across all products
72 h
Retry window with exponential back-off

40+ event types — one endpoint

Subscribe to exactly the events you care about. Every event ships with a full payload — no additional API calls needed.

Payments

payment.succeeded payment.failed payment.captured payment.authorized payment.canceled payment.requires_action payment.processing

Subscriptions

subscription.created subscription.renewed subscription.canceled subscription.past_due subscription.trial_ended subscription.upgraded subscription.downgraded subscription.paused subscription.resumed

Refunds & Disputes

refund.created refund.updated refund.failed dispute.opened dispute.updated dispute.closed dispute.won dispute.lost

Fraud & Risk

fraud.flagged fraud.review_opened fraud.review_closed fraud.blocked risk_rule.triggered

Payment Links

payment_link.paid payment_link.expired payment_link.completed

Payouts

payout.created payout.paid payout.failed

Account & KYC

Delivery Guarantee

At-least-once delivery — guaranteed

Paymentgate persists every event in a durable queue before sending it to your endpoint. If your server is down, overloaded, or returns a non-2xx response, we retry automatically for up to 72 hours using exponential back-off.

Durable event queue

Events are written to persistent storage before dispatch — never dropped in memory.

Automatic retries with back-off

Intervals: 5 s → 30 s → 2 min → 10 min → 30 min → 1 h → 2 h → 6 h → 12 h → 24 h

Idempotency IDs

Every event carries a unique event_id. Store it to deduplicate at-least-once retries safely.

Failure alerts

Receive email and Slack alerts when your endpoint starts failing — before the retry window closes.

Retry schedule — exponential back-off

Attempt 1 Immediate First try
Attempt 2 + 5 seconds Retry
Attempt 3 + 30 seconds Retry
Attempt 4 + 2 minutes Retry
Attempt 5 + 10 minutes Retry
Attempt 6 + 1 hour Retry
Attempt 7–10 +2h / +6h / +12h / +24h Retry
Final state 72 hours exceeded Abandoned

Abandoned events are stored for 30 days. You can re-deliver them manually from the dashboard.

HMAC-SHA256 Signing

Verify every event is authentic

Each request carries a Paymentgate-Signature header containing a HMAC-SHA256 digest of the raw request body, signed with your endpoint's secret. Recompute it server-side before processing any event — reject anything that doesn't match.

Incoming request headers

POST /webhooks/paymentgate
Content-Type: application/json
Paymentgate-Event: payment.succeeded
Paymentgate-Signature: sha256=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
Paymentgate-Timestamp: 1745326451
Paymentgate-Event-Id: evt_01HXYZ123ABCDEF
Python
Node.js
PHP
Go
# Python — verify Paymentgate webhook signature import hmac, hashlib from flask import request, abort WEBHOOK_SECRET = "whsec_••••••••••••••••••••••••" def verify_signature(raw_body: bytes, signature_header: str) -> bool: """ Paymentgate sends: Paymentgate-Signature: sha256=<hex_digest> We recompute the HMAC-SHA256 over the raw request body. """ if not signature_header or not signature_header.startswith("sha256="): return False expected = "sha256=" + hmac.new( WEBHOOK_SECRET.encode(), raw_body, hashlib.sha256 ).hexdigest() # Use compare_digest to prevent timing-attack leakage return hmac.compare_digest(expected, signature_header) # ── Flask route handler ───────────────────────────────────── @app.route("/webhooks/paymentgate", methods=["POST"]) def handle_webhook(): raw_body = request.get_data() sig = request.headers.get("Paymentgate-Signature", "") event_id = request.headers.get("Paymentgate-Event-Id") if not verify_signature(raw_body, sig): abort(401, "Invalid signature") # Deduplicate — idempotency guard if event_already_processed(event_id): return "", 200 event = request.get_json() if event["type"] == "payment.succeeded": fulfill_order(event["data"]["object"]) elif event["type"] == "subscription.renewed": extend_access(event["data"]["object"]) elif event["type"] == "dispute.opened": alert_team(event["data"]["object"]) mark_processed(event_id) return "", 200 # Respond 2xx fast — do heavy work async

Security tip: Always verify the signature on the raw request body before parsing JSON. Parsing first and re-serialising can alter byte order and produce a different digest. Use hmac.compare_digest (Python) or timingSafeEqual (Node.js) to prevent timing attacks.

Production-grade webhook infrastructure

Everything teams need to build reliable, maintainable event-driven integrations.

Event Filtering

Subscribe per endpoint to exactly the event types you need. Separate fraud alerts from billing events by routing to different services.

Test Console

Trigger any event type with synthetic test payloads directly from the dashboard. No real money moves — inspect request/response in the delivery log.

Multiple Endpoints

Register up to 25 webhook endpoints per account. Route billing events to your billing service, fraud alerts to your SIEM, and disputes to your CRM.

30-Day Event Log

Browse the full history of every event with request body, response code, latency, and delivery attempt timeline. Re-deliver any event in one click.

IP Allowlisting

All Paymentgate webhook requests originate from a published, stable CIDR range. Add it to your firewall to guarantee only Paymentgate can call your endpoint.

Health Alerts

Receive email or Slack alerts when an endpoint failure rate exceeds a configurable threshold, before the retry window closes and events are abandoned.

Trusted by engineering teams across Europe

"The signature verification example in the docs works first try — no surprises. We migrated from a competitor whose webhook delivery was flaky. Paymentgate's retry logic recovered 340 events during a 2-hour outage we had last quarter. Zero revenue impact."

JE
Jonas Eriksson
Lead Engineer · Stockholm Games AB

"Event filtering is the feature we didn't know we needed. We route payment.succeeded to our fulfilment service, dispute.opened to the ops team, and fraud.flagged straight to our SIEM — all from one account. Clean separation without a message broker."

EB
Elīna Bērziņa
CTO · Riga Marketplace SIA

"The 30-day event log saved us during a bug investigation. We were able to replay specific payment.succeeded events from 3 weeks back and pinpoint exactly where our order service dropped the ball. Re-delivery from the dashboard fixed 47 affected orders in minutes."

AH
Anders Holmström
Platform Architect · HelsiPay Oy

Frequently asked questions

What HTTP response code should my endpoint return?

Return any 2xx status code (200, 201, 202, 204) to acknowledge receipt. Any non-2xx response — including 3xx redirects — is treated as a failure and triggers the retry schedule. Respond within 30 seconds; requests that time out are also retried.

Can the same event be delivered more than once?

Yes — Paymentgate operates at-least-once delivery. If your endpoint is slow, timeouts or a transient network error may cause the event to be retried even if your handler already processed it. Always persist the Paymentgate-Event-Id header and deduplicate before taking business actions.

How do I test webhooks in development?

Use the Test Console in the Paymentgate dashboard to fire synthetic events at any registered endpoint — including localhost via our CLI tunnel. Alternatively, use the Paymentgate CLI: pg webhook listen --forward-to localhost:3000/webhooks to proxy live test events to your machine.

Are webhooks delivered in order?

Delivery order is best-effort. Under normal conditions events arrive in the order they occurred, but retries and parallel delivery can cause out-of-order arrival. Always use the created_at timestamp inside the event payload rather than arrival order to sequence state transitions.

How do I rotate my webhook secret without downtime?

From the dashboard, generate a new secret — Paymentgate enters a 24-hour grace period during which both the old and new secrets are accepted. Deploy your updated handler before the grace period ends, then revoke the old secret. Zero-downtime rotation with no dropped events.

Start receiving events in minutes

Register your first webhook endpoint, verify the signature, and go live — no approval needed.

Available on all plans · Up to 25 endpoints · 30-day event history