Skip to main content

Webhook Event Log and Replay

The gateway persists outbound webhook events and their delivery attempts so operators and integrators can inspect failures, replay events, and manage retention without digging directly through service logs.

This page describes the persisted webhook-event surface, the difference between event replay and DLQ retry, and the SDK helpers exposed from the normal admin clients.

Ownership: event subscriptions vs. merchant endpoint delivery​

The Event Log reads webhook_events persisted by originating services through the shared publisher and management's subscription-linked event_deliveries. Management owns the subscription delivery attempts, subscriptions, replay and retention settings. Merchant callers see and replay only attempts to their organization's own subscriptions; platform operators see every attempt linked to a readable event, including platform subscriptions.

Online transaction processing separately owns webhook_deliveries for merchant-endpoint dispatch at the storefront edge. These are a different table and delivery path, not attempts in the Event Log's event_deliveries. Neither service writes the other's delivery table.

Core API surfaces​

PurposeEndpoint
List persisted eventsGET /api/v1/webhook-events
Get one eventGET /api/v1/webhook-events/{eventId}
List delivery attempts for an eventGET /api/v1/webhook-events/{eventId}/deliveries
Get one delivery attempt (requires webhooks:read)GET /api/v1/webhook-deliveries/{deliveryId}
Replay an event to active subscriptionsPOST /api/v1/webhook-events/{eventId}/replay
Retry one DLQ delivery rowPOST /api/v1/webhook-deliveries/{deliveryId}/retry
Read self-scoped retention configGET /api/v1/webhook-events/retention
Update self-scoped retention configPUT /api/v1/webhook-events/retention
Admin list retention overridesGET /api/v1/admin/webhook-events/retention
Admin update one org retention overridePUT /api/v1/admin/webhook-events/retention/{organizationId}

Self-scoped retention reads and updates require an explicit organization grant. A location-only grant does not authorize organization-wide retention, even for a platform caller with a location grant. Platform operators without an explicit organization grant use the scoped admin retention route, which retains its webhook-events:admin requirement.

Security audit trail​

The persisted webhook event log is an event-delivery record, not a complete security audit trail. For organization administration and security-relevant management activity, use GET /api/v1/organizations/{organizationId}/audit-log. That route requires the server-derived MERCHANT_AUDIT_READ permission, enforces the caller's organization (and optional location) scope, uses opaque cursor pagination, and sanitizes JSON detail fields. The audit route covers persisted audit_log rows only; neither customer-visible surface is a substitute for central operator logs or an incident-response channel.

Replay vs. DLQ retry​

The merchant Event Log shows only deliveries to the organization's own subscriptions. Deliveries to platform subscriptions, and deliveries whose ownership cannot be established, are not listed, counted, or replayable for merchant users; the API answers requests for them with the same 404 as a missing delivery. Platform operators still see every delivery, labeled Platform or Ownership unavailable where applicable. A failed merchant delivery with a scheduled nextRetryAt is still retrying automatically, not a terminal failure requiring manual attention.

These two actions are related but not interchangeable.

Replay an event​

Use event replay when you want to enqueue delivery again from the persisted event record.

  • Endpoint: POST /api/v1/webhook-events/{eventId}/replay
  • Typical use: replay the event to authorized, active merchant-owned subscriptions, or to a filtered subset of those subscriptions
  • Result: new delivery attempt rows are queued for eligible destinations; a merchant replay that names any subscription outside the organization's own delivery set is rejected with 404 and replays nothing

Retry a DLQ delivery​

Use DLQ retry when a specific delivery row already failed all automatic attempts and was marked DLQ_PENDING or DLQ_EXHAUSTED.

  • Endpoint: POST /api/v1/webhook-deliveries/{deliveryId}/retry
  • Typical use: the merchant fixed their endpoint and you want to re-drive the exact failed delivery row
  • Result: the gateway resets that delivery row back into the delivery worker

Rule of thumb:

  • if you are operating from an event-centric view, use replay
  • if you are operating from a delivery-row / DLQ view, use retry

SDK helpers​

The persisted-event surface is exposed from the normal admin clients in both SDKs.

TypeScript​

import {
GatewayAdminClient,
findWebhookEventRetentionConfigForOrganization,
getWebhookEventDetails,
replayFailedWebhookEventDeliveries,
replayWebhookEventDeliveries,
} from '@peakgateway/sdk'

const client = new GatewayAdminClient({
environment: 'staging',
tokenProvider: async () => await firebaseUser.getIdToken(),
})

const details = await getWebhookEventDetails(client.webhookEvents, 'evt_123')

// Replay only failed / DLQ deliveries for the event.
await replayFailedWebhookEventDeliveries(client.webhookEvents, details.event.id)

// Or replay a specific subset of prior delivery rows.
await replayWebhookEventDeliveries(client.webhookEvents, details.event.id, {
deliveryIds: details.deliveries.filter((delivery) => delivery.status === 'FAILED').map((delivery) => delivery.id),
})

// Retry one DLQ delivery row directly.
await client.webhookEvents.retryDeliveryFromDlq('del_456')

const retention = await findWebhookEventRetentionConfigForOrganization(client.webhookEvents, 'org_123')

Kotlin​

import com.myriad.gateway.GatewayAdminClient
import com.myriad.gateway.GatewayConfig
import com.myriad.gateway.webhookevents.findRetentionConfigForOrganization
import com.myriad.gateway.webhookevents.getDetails
import com.myriad.gateway.webhookevents.replayDeliveries
import com.myriad.gateway.webhookevents.replayFailedDeliveries

suspend fun operateWebhooks() {
val client = GatewayAdminClient(GatewayConfig("client-id", "client-secret"))

val details = client.webhookEvents.getDetails("evt_123")

client.webhookEvents.replayFailedDeliveries(details.event.id)

client.webhookEvents.replayDeliveries(
eventId = details.event.id,
deliveryIds =
details.deliveries
.filter { it.status == "FAILED" }
.map { it.id }
.toSet(),
)

client.webhookEvents.retryDeliveryFromDlq("del_456")

val retention = client.webhookEvents.findRetentionConfigForOrganization("org_123")
client.close()
}

Delivery diagnostics​

Persisted delivery rows can include:

  • delivery status
  • HTTP status from the destination endpoint
  • attempt count
  • last error message
  • allowlisted response headers (lastResponseHeaders)
  • replay lineage (replayOfDeliveryId)

That data is the first place to look before jumping to raw service logs.

Correlation IDs inside received webhook handlers​

If your receiver wants a durable way to map a received webhook back to gateway traces or support investigations, use the verifier helpers that preserve the delivery correlation id.

  • TypeScript: WebhookVerifier.verifyDelivery(...)
  • Kotlin: WebhookVerifier.verifyDelivery(...)

Those helpers return the decoded event plus a correlationId extracted from traceparent, X-Cloud-Trace-Context, or X-Request-Id headers when present.

Public API and SDK usage lives on docs.peakgateway.co. Gift-card support workflows are on POS gift cards.