Webhook Delivery Logs
Webhooks
Webhook Delivery Logs
Inspect, retry, and audit individual webhook delivery attempts
GET
Webhook Delivery Logs
Webhook Delivery Logs
Every webhook dispatch — merchant-registered webhooks and app-scoped webhooks — is persisted as a delivery log. The platform exposes three views over these logs:- 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.
Delivery log fields
| Field | Type | Notes |
|---|---|---|
deliveryId | UUID | Unique id. Surfaced as :deliveryId in URLs. |
webhookId | string | The app or merchant webhook this delivery belongs to. |
webhookType | 'app' | 'merchant' | Whether the webhook is app-registered or merchant-registered. |
storeId | UUID | The merchant who owns this delivery. |
topic | string | e.g. orders/create, customers/redact. |
callbackUrl | string | URL we POST to. |
payload | object | 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 next retry is scheduled. 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. |
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 — the platform verifies it first and returns403 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 signing secret is captured with each delivery so that retries
(both automatic and manual) can re-sign the body. Rotating
clientSecretdoes 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.