Webhook API Endpoints
All webhook subscription endpoints require admin authentication.
List Subscriptions
GET /v1/webhook-subscriptions
List all webhook subscriptions for the current organization.
Auth: Admin only.
Response (200):
{
"data": [
{
"id": 1,
"uid": "a1b2c3d4-...",
"url": "https://hook.eu1.make.com/abc123",
"description": "Slack notifications via Make",
"events": ["appointment.created", "appointment.cancelled"],
"is_active": true,
"created_at": "2025-01-15T10:30:00Z"
}
]
}Note: signing_secret is never returned in list responses. It's only shown once at creation time.
Create Subscription
POST /v1/webhook-subscriptions
Create a new webhook subscription.
Auth: Admin only.
Request:
{
"url": "https://hook.eu1.make.com/abc123",
"description": "Slack notifications via Make",
"events": ["appointment.created", "appointment.cancelled"]
}Validation:
url: Required, must be HTTPSevents: Required, non-empty array. Each must be a valid event type or"*"for all eventssigning_secret: Generated server-side (32 random bytes, hex-encoded, prefixed withwhsec_)
Response (201):
{
"data": {
"id": 1,
"uid": "a1b2c3d4-...",
"url": "https://hook.eu1.make.com/abc123",
"description": "Slack notifications via Make",
"events": ["appointment.created", "appointment.cancelled"],
"signing_secret": "whsec_a1b2c3d4e5f6g7h8...",
"is_active": true,
"created_at": "2025-01-15T10:30:00Z"
}
}IMPORTANT: The signing_secret is only returned in this response. Store it securely — you'll need it to verify webhook signatures. It cannot be retrieved later.
Update Subscription
PATCH /v1/webhook-subscriptions/{uid}
Update a subscription (URL, events, active status).
Auth: Admin only.
Request:
{
"events": ["*"],
"is_active": false
}Updatable fields:
url— Change the delivery endpointdescription— Update the descriptionevents— Modify event subscriptionsis_active— Enable/disable the subscription
Response (200):
{
"data": {
"id": 1,
"uid": "a1b2c3d4-...",
"url": "https://hook.eu1.make.com/abc123",
"description": "Slack notifications via Make",
"events": ["*"],
"is_active": false,
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-16T14:20:00Z"
}
}Note: The signing secret cannot be changed. If you need a new secret, delete the subscription and create a new one.
Delete Subscription
DELETE /v1/webhook-subscriptions/{uid}
Delete a subscription and all its delivery history.
Auth: Admin only.
Response (204): No content.
Note: This action is permanent. All delivery history for this subscription will be deleted.
List Delivery Events
GET /v1/webhook-subscriptions/{uid}/events
List recent delivery attempts for a subscription (for debugging).
Auth: Admin only.
Query Parameters:
status(optional) — Filter by status:pending,delivered,failed,exhaustedpage(optional, default: 1) — Page numberpage_size(optional, default: 25) — Results per page
Example:
GET /v1/webhook-subscriptions/a1b2c3d4-..../events?status=failed&page=1&page_size=25Response (200):
{
"data": [
{
"id": 5678,
"event_type": "appointment.created",
"status": "failed",
"attempts": 3,
"last_status_code": 500,
"last_error": "Internal Server Error",
"next_retry_at": "2025-07-15T15:30:00Z",
"created_at": "2025-07-15T14:30:00Z"
}
],
"pagination": {
"page": 1,
"page_size": 25,
"total": 142
}
}Test Subscription
POST /v1/webhook-subscriptions/{uid}/test
Send a test event to verify the subscription URL is reachable.
Auth: Admin only.
Response (200):
{
"status": "delivered",
"status_code": 200,
"latency_ms": 142
}Response (400) — Delivery Failed:
{
"status": "failed",
"status_code": 500,
"error": "Internal Server Error"
}How it works:
- Sends a
webhook.testevent with a dummy payload - Delivery is synchronous (no retry)
- Returns the result immediately
- Useful for verifying endpoint configuration before going live
Test event payload:
{
"id": "evt_test_01HQ3K5M7N8P9R0S",
"event": "webhook.test",
"timestamp": "2025-07-15T14:30:00Z",
"organization_id": 42,
"data": {
"message": "This is a test webhook delivery"
}
}Error Responses
All endpoints return standard error responses:
400 Bad Request:
{
"error": "invalid_request",
"message": "URL must use HTTPS"
}401 Unauthorized:
{
"error": "unauthorized",
"message": "Authentication required"
}403 Forbidden:
{
"error": "forbidden",
"message": "Admin access required"
}404 Not Found:
{
"error": "not_found",
"message": "Webhook subscription not found"
}