Webhook Delivery Logs
Webhooks
Webhook Delivery Logs
Inspect, retry, and audit individual webhook delivery attempts
GET
Webhook Delivery Logs
Documentation Index
Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt
Use this file to discover all available pages before exploring further.
Webhook Delivery Logs
Every webhook dispatch — merchant-registered webhooks and app-scoped webhooks — is persisted as aWebhookDeliveryLog row. The platform
exposes three views over this table:
- Merchant-scoped — store owner/staff inspects deliveries across all apps installed on their store.
- Developer-scoped (per-app) — app owner inspects every delivery to their app, across every install.
- Manual retry — kick off a fresh delivery attempt that respects
the original
secretand exponential-backoff retry queue.
UserRole.MERCHANT or
UserRole.STAFF_ADMIN). There is no public app-scoped endpoint — apps
can subscribe to delivery-failure notifications, but cannot read merchant
logs over the OAuth API.
Data Model: WebhookDeliveryLog
| Field | Type | Notes |
|---|---|---|
deliveryId | UUID | Primary key. Surfaced as :deliveryId in URLs. |
webhookId | string | References AppWebhook.appWebhookId (apps) or Webhook.webhookId (merchant). |
webhookType | 'app' | 'merchant' | Which table webhookId points to. |
storeId | UUID | The merchant who owns this delivery. |
topic | string | e.g. orders/create, customers/redact. |
callbackUrl | string | URL we POST to. |
payload | JSONB | Exact body sent (without HMAC; HMAC is in headers). |
status | enum | PENDING, RETRYING, SUCCESS, FAILED. |
attempts | integer | Number of delivery attempts so far (0-3). |
lastAttemptAt | timestamp | When the most recent send happened. |
nextRetryAt | timestamp | null | When the worker will retry. null once SUCCESS or terminal FAILED. |
responseCode | integer | null | HTTP status from the last attempt. |
responseBody | text | null | First few KB of the response body (truncated). |
errorMessage | text | null | Network error message (timeout, DNS, TLS) if no HTTP response. |
secret | string | null | HMAC secret used for signing — stored so retries can re-sign. |
(storeId, status), (status, nextRetryAt) (the retry-queue
worker scan), and (webhookId).
Retry Schedule
Failed dispatches retry 3 times with exponential backoff:| Attempt | Delay after previous | Cumulative time |
|---|---|---|
| 1 (initial) | – | 0 |
| 2 | 1 minute | 1m |
| 3 | 5 minutes | 6m |
| 4 (final) | 15 minutes | 21m |
status is set to FAILED and
nextRetryAt is cleared. The delivery can still be retried manually
via the /retry endpoint below — this re-arms the row but starts the
attempt counter fresh.
Merchant Endpoints
GET /apps/store/webhook-logs
List webhook deliveries for the caller’s store.
Page number.
Items per page.
Filter:
PENDING, RETRYING, SUCCESS, FAILED.Filter by webhook topic (e.g.
orders/create).GET /apps/store/webhook-logs/:deliveryId
Return the full record including payload and responseBody.
404 Delivery log not found when no row matches (deliveryId, storeId).
POST /apps/store/webhook-logs/:deliveryId/retry
Manually trigger a fresh send. The retry uses the original secret,
callbackUrl, and payload from the row — only attempts is reset.
Developer Endpoints (per-app, all stores)
These views require ownership of the app —verifyAppOwnership(appId, storeId) runs first and returns 403 Forbidden if the JWT’s storeId is
not the app’s developerId.
GET /apps/developer/:appId/webhook-deliveries
Same query parameters as the merchant endpoint, but scoped to one app
across all of its installations.
GET /apps/developer/:appId/webhook-deliveries/stats
Aggregate counters (last 24h / 7d / 30d) plus per-topic failure
rates — useful for the developer dashboard’s health page.
GET /apps/developer/:appId/webhook-deliveries/:deliveryId
Same as the merchant single-delivery view but scoped to one app.
POST /apps/developer/:appId/webhook-deliveries/:deliveryId/retry
Manually retry. Same semantics as the merchant retry, scoped to one app.
Error Codes
| HTTP | Error | When |
|---|---|---|
401 | (auth guard) | Missing/invalid merchant JWT. |
403 | Forbidden | Developer endpoints — caller doesn’t own :appId. |
404 | Delivery log not found | :deliveryId doesn’t exist or belongs to another store/app. |
500 | (generic) | DB read error. |
Operational Notes
- The HMAC secret column is stored on the log row so that retries (both
automatic and manual) can re-sign the body without re-deriving from
the app’s
clientSecret. RotatingclientSecretdoes not invalidate pending retries — they continue with the secret as of first dispatch. responseBodyis truncated to the first ~64KB. Use it for debugging; do not rely on it for full audit retention.- For the live retry-queue scan, see
WebhookDispatchService—idx_webhook_delivery_logs_retry_queue(status, nextRetryAt) keeps the worker’sSELECT ... WHERE status = 'RETRYING' AND nextRetryAt < NOW()cheap even with millions of rows.