Webhook Delivery
GET /api/organizations/:org_slug/webhooks/:webhook_id/deliveries
Get paginated delivery history for a webhook, including request payload, response status, and retry information.
Authentication: Required (Organization Management JWT)
Permissions: Organization owner or admin
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
webhook_id |
string | Webhook ID |
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page |
integer | No | 1 | Page number (minimum: 1) |
limit |
integer | No | 50 | Results per page (maximum: 100) |
event_type |
string | No | - | Filter by event type |
delivered |
boolean | No | - | Filter by delivery status (true/false) |
Request Headers:
Authorization: Bearer {jwt_token}
Example Request:
curl -X GET "https://sso.example.com/api/organizations/acme-corp/webhooks/550e8400-e29b-41d4-a716-446655440000/deliveries?page=1&limit=20&event_type=user.login.success" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Response (200 OK):
{
"deliveries": [
{
"id": "770e8400-e29b-41d4-a716-446655440222",
"webhook_id": "550e8400-e29b-41d4-a716-446655440000",
"webhook_name": "Production Webhook",
"event_type": "user.login.success",
"payload": {
"event": "user.login.success",
"timestamp": "2025-01-15T10:35:00Z",
"organization_id": "org-123",
"actor_user_id": "user-456",
"actor_email": "user@example.com",
"data": {
"provider": "github",
"service_id": "service-789"
}
},
"response_status_code": 200,
"response_body": "{\"status\":\"received\"}",
"attempt_count": 1,
"max_attempts": 5,
"next_retry_at": null,
"delivered": true,
"delivery_error": null,
"created_at": "2025-01-15T10:35:00Z",
"updated_at": "2025-01-15T10:35:01Z"
},
{
"id": "880e8400-e29b-41d4-a716-446655440333",
"webhook_id": "550e8400-e29b-41d4-a716-446655440000",
"webhook_name": "Production Webhook",
"event_type": "user.signup.success",
"payload": {
"event": "user.signup.success",
"timestamp": "2025-01-15T10:30:00Z",
"organization_id": "org-123",
"actor_user_id": "user-789",
"actor_email": "newuser@example.com",
"data": {
"provider": "google",
"service_id": "service-789"
}
},
"response_status_code": 500,
"response_body": "{\"error\":\"Internal Server Error\"}",
"attempt_count": 3,
"max_attempts": 5,
"next_retry_at": "2025-01-15T10:45:00Z",
"delivered": false,
"delivery_error": "HTTP 500: Internal Server Error",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:40:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 156,
"total_pages": 8,
"has_next": true,
"has_prev": false
}
}
Response Fields:
Delivery Object:
id(string): Delivery unique identifierwebhook_id(string): Webhook IDwebhook_name(string): Webhook nameevent_type(string): Event type that triggered deliverypayload(object): Full webhook payload sentresponse_status_code(integer, nullable): HTTP status code from target endpointresponse_body(string, nullable): Response body from target endpointattempt_count(integer): Number of delivery attemptsmax_attempts(integer): Maximum retry attempts (5)next_retry_at(string, nullable): ISO 8601 timestamp of next retrydelivered(boolean): Whether delivery was successfuldelivery_error(string, nullable): Error message if delivery failedcreated_at(string): ISO 8601 timestamp of first delivery attemptupdated_at(string): ISO 8601 timestamp of last delivery attempt
Pagination Object:
page(integer): Current page numberlimit(integer): Results per pagetotal(integer): Total number of deliveriestotal_pages(integer): Total number of pageshas_next(boolean): Whether there is a next pagehas_prev(boolean): Whether there is a previous page
Error Responses:
401 Unauthorized: Missing or invalid JWT token403 Forbidden: User is not an admin or owner404 Not Found: Organization or webhook not found
Retry Logic
Failed webhook deliveries are automatically retried with exponential backoff:
Retries provide additional attempts, not an exactly-once or eventual-delivery guarantee. The same delivery may be received more than once.
Retry Configuration:
- Max Retries: 5 attempts total
- Initial Delay: 5 seconds
- Max Delay: 30 minutes
- Backoff Formula:
delay = base_delay × 2^(attempt - 1) + jitter - Jitter: 0-9 seconds (prevents thundering herd)
Retry Schedule Example:
- Attempt 1: Immediate
- Attempt 2: 5 seconds + jitter
- Attempt 3: 10 seconds + jitter
- Attempt 4: 20 seconds + jitter
- Attempt 5: 40 seconds + jitter (capped at 30 minutes)
Success Criteria:
- HTTP status codes 2xx (200-299) are considered successful
- All other status codes (3xx, 4xx, 5xx) trigger retries
- Network timeouts and connection errors trigger retries
Retry Headers: Each retry includes headers indicating the attempt number:
X-Webhook-Delivery-Id: {delivery_id}
X-Webhook-Attempt: {attempt_number}
Best Practices for Webhook Endpoints:
- Return 2xx status codes immediately upon receipt
- Process webhooks asynchronously in background jobs
- Implement idempotency using
X-Webhook-Delivery-Id - Return non-2xx only for malformed payloads or authentication failures
- Avoid long-running operations in webhook handlers
Rate Limiting
Webhook endpoints should implement rate limiting to prevent abuse:
Recommended Limits:
- Per Webhook: 100 deliveries per minute
- Per Organization: 1000 deliveries per minute across all webhooks
Rate Limit Headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1642244400