Skip to main content

Webhooks

Webhooks let you receive real-time notifications when events occur in your account. Peak Gateway delivers an HTTP POST to your endpoint with a signed JSON payload.

Registering an endpoint

POST /api/v1/webhooks
Authorization: Bearer <API_KEY>
Content-Type: application/json

{
  "url": "https://yourapp.com/webhooks/gateway",
  "events": [
    "payment.completed",
    "payment.declined",
    "subscription.billed"
  ]
}

Verifying signatures

Every webhook request includes X-Gateway-Signature and X-Gateway-Timestamp headers. Verify both to ensure the payload came from Peak Gateway and is fresh:

import express from 'express';
import { WebhookVerifier } from '@gateway/sdk';

const verifier = new WebhookVerifier(process.env.WEBHOOK_SECRET);

app.post('/webhooks/gateway', express.raw({ type: 'application/json' }), async (req, res) => {
  const signature = req.headers['x-gateway-signature'] as string;
  const timestamp = req.headers['x-gateway-timestamp'] as string;

  if (!signature || !timestamp) {
    return res.status(400).send('Missing signature or timestamp');
  }

  const rawBody = req.body instanceof Buffer ? req.body.toString('utf8') : String(req.body ?? '');

  try {
    const event = await verifier.verify(rawBody, signature, timestamp);
    // handle event...
    res.sendStatus(200);
  } catch (err) {
    return res.status(401).send('Invalid signature');
  }
});

The signature is an HMAC-SHA256 hex digest of <timestamp>.<rawBody> using your webhook secret. Requests with timestamps older than 5 minutes are rejected to prevent replay attacks.

Retry policy

Peak Gateway retries failed deliveries (non-2xx responses) with exponential backoff:

AttemptDelay
1Immediate
230 seconds
35 minutes
41 hour
56 hours

After 5 failed attempts the event is marked as FAILED and no further retries occur.

Return HTTP 2xx within 30 seconds. Timeouts are treated as failures.

Event types and payloads

All events share a common envelope:

{
  "eventId": "evt_01j...",
  "eventType": "payment.completed",
  "timestamp": "2026-03-31T12:00:00Z",
  "merchantId": "merch_123"
}

payment.completed

Fired when a transaction is successfully authorized and captured.

{
  "eventId": "evt_01j9abc",
  "eventType": "payment.completed",
  "timestamp": "2026-03-31T12:00:00Z",
  "transaction": {
    "transactionId": "txn_01j9xyz",
    "merchantId": "merch_123",
    "type": "SALE",
    "status": "APPROVED",
    "amount": 2500,
    "currency": "USD",
    "cardBrand": "VISA",
    "cardLast4": "1111",
    "approvalCode": "TXN123",
    "createdAt": "2026-03-31T12:00:00Z"
  }
}

payment.declined

Fired when a transaction is declined by the issuer.

{
  "eventId": "evt_01j9def",
  "eventType": "payment.declined",
  "timestamp": "2026-03-31T12:01:00Z",
  "transaction": {
    "transactionId": "txn_01j9abc",
    "merchantId": "merch_123",
    "type": "SALE",
    "status": "DECLINED",
    "amount": 2500,
    "currency": "USD",
    "responseCode": "D0001",
    "responseMessage": "Do Not Honor",
    "createdAt": "2026-03-31T12:01:00Z"
  }
}

payment.voided

Fired when a transaction is voided before settlement.

{
  "eventId": "evt_01j9ghi",
  "eventType": "payment.voided",
  "timestamp": "2026-03-31T13:00:00Z",
  "transaction": {
    "transactionId": "txn_01j9xyz",
    "merchantId": "merch_123",
    "type": "VOID",
    "status": "APPROVED",
    "amount": 2500,
    "currency": "USD",
    "parentTransactionId": "txn_01j9abc",
    "createdAt": "2026-03-31T13:00:00Z"
  }
}

payment.refunded

Fired when a refund is processed.

{
  "eventId": "evt_01j9jkl",
  "eventType": "payment.refunded",
  "timestamp": "2026-03-31T14:00:00Z",
  "transaction": {
    "transactionId": "txn_refund_01",
    "merchantId": "merch_123",
    "type": "REFUND",
    "status": "APPROVED",
    "amount": 1000,
    "currency": "USD",
    "parentTransactionId": "txn_01j9abc",
    "createdAt": "2026-03-31T14:00:00Z"
  }
}

payment.settled

Fired when a transaction is included in a closed settlement batch.

{
  "eventId": "evt_01j9mno",
  "eventType": "payment.settled",
  "timestamp": "2026-03-31T22:30:00Z",
  "transaction": {
    "transactionId": "txn_01j9abc",
    "merchantId": "merch_123",
    "amount": 2500,
    "currency": "USD",
    "settledAt": "2026-03-31T22:30:00Z"
  }
}

checkout.expired

Fired when a checkout session expires without being completed.

{
  "eventId": "evt_01j9pqr",
  "eventType": "checkout.expired",
  "timestamp": "2026-03-31T13:30:00Z",
  "checkoutSessionId": "cs_01j9stu",
  "merchantId": "merch_123",
  "expiresAt": "2026-03-31T13:30:00Z"
}

subscription.created

{
  "eventId": "evt_01j9vwx",
  "eventType": "subscription.created",
  "timestamp": "2026-03-31T10:00:00Z",
  "subscription": {
    "subscriptionId": "sub_01j9abc",
    "merchantId": "merch_123",
    "customerId": "cust_456",
    "amount": 1999,
    "currency": "USD",
    "interval": "monthly",
    "status": "ACTIVE",
    "nextBillingDate": "2026-04-30"
  }
}

subscription.billed

Fired when a recurring billing cycle completes successfully.

{
  "eventId": "evt_01j9yza",
  "eventType": "subscription.billed",
  "timestamp": "2026-04-01T00:00:00Z",
  "subscription": {
    "subscriptionId": "sub_01j9abc",
    "merchantId": "merch_123",
    "customerId": "cust_456",
    "amount": 1999,
    "currency": "USD",
    "cycle": 2,
    "transactionId": "txn_01j9billing"
  }
}

subscription.payment_failed

{
  "eventId": "evt_01j9bcd",
  "eventType": "subscription.payment_failed",
  "timestamp": "2026-04-01T00:01:00Z",
  "subscription": {
    "subscriptionId": "sub_01j9abc",
    "merchantId": "merch_123",
    "customerId": "cust_456",
    "amount": 1999,
    "currency": "USD",
    "failureReason": "CARD_DECLINED",
    "retryCount": 1
  }
}

subscription.cancelled

{
  "eventId": "evt_01j9efg",
  "eventType": "subscription.cancelled",
  "timestamp": "2026-04-01T09:00:00Z",
  "subscription": {
    "subscriptionId": "sub_01j9abc",
    "merchantId": "merch_123",
    "customerId": "cust_456",
    "cancelledAt": "2026-04-01T09:00:00Z"
  }
}

subscription.past_due

Fired when a subscription enters past-due status after a failed billing attempt.

subscription.paused / subscription.resumed

Fired when a subscription is paused or resumed manually or programmatically.

All event types

EventTrigger
payment.completedSale, auth-capture, or card-present transaction succeeds
payment.declinedTransaction declined by issuer
payment.voidedTransaction voided before settlement
payment.refundedRefund processed
payment.settledTransaction included in closed batch
checkout.expiredCheckout session expires without payment
subscription.createdNew subscription created
subscription.billedRecurring billing cycle succeeds
subscription.payment_failedRecurring billing cycle fails
subscription.past_dueSubscription enters past-due after failed retry
subscription.cancelledSubscription cancelled
subscription.pausedSubscription paused
subscription.resumedSubscription resumed