Webhook Logs
View delivery history, inspect request and response payloads, filter by status, and diagnose webhook failures.
Webhook Logs
HelloJohn keeps a detailed log of every webhook delivery attempt. Use these logs to diagnose failures, verify delivery, and replay events.
Delivery Statuses
| Status | Description |
|---|---|
succeeded | Endpoint responded with 2xx |
failed | Endpoint returned non-2xx or timed out |
pending | Initial attempt in flight |
permanently_failed | All retry attempts exhausted |
Viewing Logs in the Dashboard
- Go to Developer → Webhooks
- Click your webhook endpoint
- Open the Deliveries tab
Each delivery shows:
- Event type and delivery ID
- Status and HTTP response code
- Timestamp and response time
- Request headers and body
- Response body
Click any delivery to see the full payload and response, and to manually retry it.
Listing Deliveries via API
All Deliveries for a Webhook
curl "https://api.hellojohn.dev/v1/webhooks/wh_01HABCDEF777001/deliveries" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321"Filter by Status
curl "https://api.hellojohn.dev/v1/webhooks/wh_01HABCDEF777001/deliveries?status=failed" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321"Filter by Event Type
curl "https://api.hellojohn.dev/v1/webhooks/wh_01HABCDEF777001/deliveries?event=user.created" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321"Query Parameters
| Parameter | Description |
|---|---|
status | Filter by status: succeeded, failed, permanently_failed |
event | Filter by event type (e.g., user.created) |
since | ISO timestamp — return deliveries after this time |
until | ISO timestamp — return deliveries before this time |
cursor | Pagination cursor |
limit | Results per page (default: 20, max: 100) |
Response:
{
"data": [
{
"id": "del_01HABCDEF888002",
"webhook_id": "wh_01HABCDEF777001",
"event": "user.created",
"status": "failed",
"http_status": 500,
"attempts": 3,
"response_time_ms": 2103,
"next_retry_at": "2024-01-20T10:30:00Z",
"created_at": "2024-01-20T08:15:00Z",
"last_attempted_at": "2024-01-20T09:45:00Z"
}
],
"next_cursor": "cur_01HABCDEF...",
"has_more": true
}Getting a Single Delivery
Fetch the full details of a delivery, including request and response payloads:
curl "https://api.hellojohn.dev/v1/webhooks/deliveries/del_01HABCDEF888002" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321"Response:
{
"id": "del_01HABCDEF888002",
"webhook_id": "wh_01HABCDEF777001",
"event": "user.created",
"status": "failed",
"http_status": 500,
"attempts": 3,
"next_retry_at": "2024-01-20T10:30:00Z",
"created_at": "2024-01-20T08:15:00Z",
"request": {
"url": "https://example.com/webhooks/hellojohn",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"HelloJohn-Signature": "sha256=abc123...",
"HelloJohn-Delivery-ID": "del_01HABCDEF888002",
"HelloJohn-Event": "user.created",
"HelloJohn-Timestamp": "1705744500"
},
"body": {
"id": "evt_01HABCDEF111002",
"type": "user.created",
"data": {
"user": {
"id": "usr_01HABCDEF123456",
"email": "alice@example.com",
"created_at": "2024-01-20T08:15:00Z"
}
},
"created_at": "2024-01-20T08:15:00Z"
}
},
"response": {
"http_status": 500,
"body": "Internal Server Error",
"response_time_ms": 2103
},
"attempts_detail": [
{
"attempt": 1,
"http_status": 500,
"response_time_ms": 2103,
"attempted_at": "2024-01-20T08:15:00Z"
},
{
"attempt": 2,
"http_status": 500,
"response_time_ms": 1840,
"attempted_at": "2024-01-20T08:20:00Z"
},
{
"attempt": 3,
"http_status": 500,
"response_time_ms": 1950,
"attempted_at": "2024-01-20T09:45:00Z"
}
]
}Retrying a Failed Delivery
Manually retry a failed delivery:
curl -X POST "https://api.hellojohn.dev/v1/webhooks/deliveries/del_01HABCDEF888002/retry" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321"Response:
{
"id": "del_01HABCDEF888002",
"status": "pending",
"message": "Retry scheduled"
}You can also retry from the dashboard — navigate to the delivery and click Retry.
Monitoring Delivery Health Programmatically
Poll delivery logs to detect failures and alert your team:
async function checkWebhookHealth(webhookId: string) {
const res = await fetch(
`https://api.hellojohn.dev/v1/webhooks/${webhookId}/deliveries?status=failed&limit=10`,
{
headers: {
Authorization: `Bearer ${process.env.HELLOJOHN_SECRET_KEY}`,
"X-Tenant-ID": process.env.HELLOJOHN_TENANT_ID!,
},
}
);
const { data } = await res.json();
if (data.length > 0) {
console.warn(`⚠️ ${data.length} failed deliveries for webhook ${webhookId}`);
for (const delivery of data) {
console.warn(
` - ${delivery.event} (${delivery.id}): HTTP ${delivery.http_status}, ` +
`${delivery.attempts} attempts, next retry: ${delivery.next_retry_at}`
);
}
// Alert your team
await sendSlackAlert(`Webhook failures detected: ${data.length} events`);
}
}Log Retention
| Plan | Retention |
|---|---|
| Free | 3 days |
| Starter | 7 days |
| Pro | 30 days |
| Enterprise | 90 days |
After retention expires, delivery logs are purged. Export logs to your own storage if you need longer retention.
Exporting Delivery Logs
Use pagination to export all deliveries within a time window:
async function exportDeliveries(webhookId: string, since: string) {
const deliveries = [];
let cursor: string | null = null;
do {
const params = new URLSearchParams({ since, limit: "100" });
if (cursor) params.set("cursor", cursor);
const res = await fetch(
`https://api.hellojohn.dev/v1/webhooks/${webhookId}/deliveries?${params}`,
{
headers: {
Authorization: `Bearer ${process.env.HELLOJOHN_SECRET_KEY}`,
"X-Tenant-ID": process.env.HELLOJOHN_TENANT_ID!,
},
}
);
const { data, next_cursor, has_more } = await res.json();
deliveries.push(...data);
cursor = has_more ? next_cursor : null;
} while (cursor);
return deliveries;
}
// Export all deliveries from the last 30 days
const since = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();
const logs = await exportDeliveries("wh_01HABCDEF777001", since);
console.log(`Exported ${logs.length} deliveries`);Common Failure Reasons
| HTTP Status | Likely Cause | Fix |
|---|---|---|
401 | Signature verification failing | Check HELLOJOHN_WEBHOOK_SECRET; ensure raw body is used for HMAC |
404 | Webhook path not found | Verify the URL path matches your route |
408 / Timeout | Handler taking > 30s | Respond immediately and process async |
500 | Unhandled exception in handler | Add try/catch; check your error logs |
503 | Server down or overloaded | Check server health; scale up if needed |