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
| Purpose | Endpoint |
|---|---|
| List persisted events | GET /api/v1/webhook-events |
| Get one event | GET /api/v1/webhook-events/{eventId} |
| List delivery attempts for an event | GET /api/v1/webhook-events/{eventId}/deliveries |
Get one delivery attempt (requires webhooks:read) | GET /api/v1/webhook-deliveries/{deliveryId} |
| Replay an event to active subscriptions | POST /api/v1/webhook-events/{eventId}/replay |
| Retry one DLQ delivery row | POST /api/v1/webhook-deliveries/{deliveryId}/retry |
| Read self-scoped retention config | GET /api/v1/webhook-events/retention |
| Update self-scoped retention config | PUT /api/v1/webhook-events/retention |
| Admin list retention overrides | GET /api/v1/admin/webhook-events/retention |
| Admin update one org retention override | PUT /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.
Related docs
Public API and SDK usage lives on docs.peakgateway.co. Gift-card support workflows are on POS gift cards.