Overview
Webhooks let you receive real-time notifications when something happens in your Casework tenant. Instead of polling the API for changes, you register an HTTPS endpoint and Casework sends a POST request to it whenever a subscribed event occurs.
Each delivery includes:
- A JSON payload describing the event
- An HMAC-SHA256 signature so you can verify the request is authentic
- Headers identifying the event type and delivery ID
Admin role required
Creating and managing webhook endpoints requires the admin role and the webhooks:read / webhooks:write API key scopes. Webhooks are available on the Team plan and above.
Webhook events
Casework supports nine webhook events across four categories.
Case events
| Event | Fired when |
|---|---|
case_created | A new case is created (via the API or the public submission form) |
case_status_changed | A case transitions to a different status (e.g. open to in_progress) |
case_resolved | A case status is changed to resolved |
case_closed | A case status is changed to closed |
case_note_added | A note is added to a case timeline |
Contact events
| Event | Fired when |
|---|---|
contact_submitted | A member of the public submits a contact/issue form |
Content events
| Event | Fired when |
|---|---|
news_published | A news item is approved and published |
event_published | A community event is approved and published |
Profile events
| Event | Fired when |
|---|---|
councillor_profile_updated | A councillor updates their public profile |
Example payloads
Every webhook payload follows a consistent envelope structure with an event field, an ISO-8601 timestamp, and a data object containing event-specific fields.
case_created
{
"event": "case_created",
"timestamp": "2026-03-08T14:22:00.000Z",
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"reference": "CW-2026-0042",
"status": "open",
"residentName": "Jane Smith"
}
}
case_status_changed
{
"event": "case_status_changed",
"timestamp": "2026-03-08T15:10:00.000Z",
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"reference": "CW-2026-0042",
"oldStatus": "open",
"newStatus": "in_progress"
}
}
case_resolved
{
"event": "case_resolved",
"timestamp": "2026-03-09T09:30:00.000Z",
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"reference": "CW-2026-0042",
"status": "resolved"
}
}
case_closed
{
"event": "case_closed",
"timestamp": "2026-03-10T11:00:00.000Z",
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"reference": "CW-2026-0042",
"status": "closed"
}
}
case_note_added
{
"event": "case_note_added",
"timestamp": "2026-03-08T16:45:00.000Z",
"data": {
"caseId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"reference": "CW-2026-0042",
"activityId": "f9e8d7c6-b5a4-3210-fedc-ba0987654321"
}
}
contact_submitted
{
"event": "contact_submitted",
"timestamp": "2026-03-08T12:00:00.000Z",
"data": {
"caseId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"reference": "CW-2026-0043",
"wardId": "ward-123",
"categoryId": "cat-456"
}
}
news_published
{
"event": "news_published",
"timestamp": "2026-03-08T10:00:00.000Z",
"data": {
"newsItemId": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
}
}
event_published
{
"event": "event_published",
"timestamp": "2026-03-08T10:05:00.000Z",
"data": {
"eventId": "c3d4e5f6-a7b8-9012-cdef-123456789012"
}
}
councillor_profile_updated
{
"event": "councillor_profile_updated",
"timestamp": "2026-03-08T13:30:00.000Z",
"data": {
"userId": "d4e5f6a7-b8c9-0123-defa-234567890123"
}
}
Managing webhook endpoints
Create a webhook endpoint
/api/webhooksRegister a new webhook endpoint. The signing secret is returned only in the creation response — store it securely.
Example response (201 Created)
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Slack Notifications",
"url": "https://hooks.example.com/casework",
"events": ["case_created", "case_resolved"],
"secret": "whsec_abc123def456ghi789jkl012mno345pq",
"enabled": true,
"createdById": "user-123",
"createdBy": { "id": "user-123", "name": "Alice Admin" },
"createdAt": "2026-03-08T10:00:00.000Z",
"updatedAt": "2026-03-08T10:00:00.000Z"
}
Store your secret
The webhook signing secret (whsec_...) is only returned in full when the endpoint is created. Subsequent GET requests mask the secret. Copy it immediately and store it in a secure location such as an environment variable or secrets manager.
List webhook endpoints
/api/webhooksReturns all webhook endpoints for your tenant, ordered by creation date (newest first).
Example response
[
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Slack Notifications",
"url": "https://hooks.example.com/casework",
"events": ["case_created", "case_resolved"],
"secret": "whsec_abc1...",
"enabled": true,
"createdBy": { "id": "user-123", "name": "Alice Admin" },
"createdAt": "2026-03-08T10:00:00.000Z",
"updatedAt": "2026-03-08T10:00:00.000Z",
"_count": { "deliveries": 47 }
}
]
Update a webhook endpoint
/api/webhooks/[id]Update the name, URL, subscribed events, or enabled status of an existing endpoint.
Delete a webhook endpoint
/api/webhooks/[id]Permanently delete a webhook endpoint and all of its delivery history.
Send a test delivery
/api/webhooks/[id]/testSend a test webhook delivery to verify your endpoint is reachable. The delivery is executed synchronously and the result is returned.
Test payload
Your endpoint will receive a payload with event set to "test":
{
"event": "test",
"timestamp": "2026-03-08T14:00:00.000Z",
"data": {
"message": "This is a test webhook delivery",
"endpointId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"endpointName": "Slack Notifications"
}
}
List deliveries
/api/webhooks/[id]/deliveriesReturns a paginated list of delivery attempts for a specific webhook endpoint, ordered by most recent first.
Example response
{
"deliveries": [
{
"id": "del-uuid-001",
"endpointId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"event": "case_created",
"payload": { "event": "case_created", "timestamp": "...", "data": { "..." : "..." } },
"statusCode": 200,
"response": "OK",
"attempts": 1,
"success": true,
"errorMessage": null,
"nextRetry": null,
"createdAt": "2026-03-08T14:22:00.000Z",
"updatedAt": "2026-03-08T14:22:01.000Z"
}
],
"pagination": {
"page": 1,
"pageSize": 10,
"total": 47,
"totalPages": 5
}
}
Verifying webhook signatures
Every webhook delivery includes an X-Webhook-Signature header containing an HMAC-SHA256 signature of the raw request body, prefixed with sha256=. You should always verify this signature before processing a webhook to ensure the request genuinely came from Casework.
Headers sent with each delivery
| Header | Description |
|---|---|
Content-Type | Always application/json |
X-Webhook-Event | The event type, e.g. case_created |
X-Webhook-Delivery | Unique delivery ID (UUID) |
X-Webhook-Signature | sha256=<hex-encoded HMAC-SHA256 of the raw body> |
Verification examples
Use timing-safe comparison
Always use crypto.timingSafeEqual (Node.js) or hmac.compare_digest (Python) to compare signatures. Standard string equality (=== or ==) is vulnerable to timing attacks.
Retry behavior
If your endpoint does not return a 2xx status code (or the request times out after 10 seconds), Casework retries the delivery using an exponential backoff schedule:
| Attempt | Delay after failure |
|---|---|
| 1 | Immediate (first attempt) |
| 2 | 30 seconds |
| 3 | 2 minutes |
| 4 | 15 minutes |
| 5 | 1 hour |
| 6 | 4 hours |
After 6 failed attempts, the delivery is marked as permanently failed and no further retries are scheduled. You can inspect failed deliveries via the List deliveries endpoint.
Each delivery record includes:
attempts-- how many delivery attempts have been madesuccess-- whether the most recent attempt succeededstatusCode-- the HTTP status code returned by your endpoint (if any)errorMessage-- a description of the failure (e.g.HTTP 500orConnection timed out)nextRetry-- the ISO-8601 timestamp of the next scheduled retry, ornullif no more retries are planned
Idempotency
Your webhook handler may receive the same event more than once due to retries. Use the X-Webhook-Delivery header (a unique UUID per delivery) to deduplicate events on your end.
Security requirements
Casework enforces two security requirements for all webhook endpoint URLs:
- HTTPS only -- Webhook URLs must use the
https://scheme. Plain HTTP endpoints are rejected. - No private/internal addresses -- URLs pointing to private IP ranges (
127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16), IPv6 link-local/unique-local addresses (fc00::/7,fe80::/10), andlocalhostare blocked to prevent SSRF attacks.
These validations are enforced when creating or updating a webhook endpoint. If you provide an invalid URL, the API returns a 400 error with a descriptive message.
Best practices
- Respond quickly -- Return a
200response as soon as possible. Process the webhook payload asynchronously (e.g. push it onto a queue) rather than doing heavy work inside the request handler. Casework times out webhook deliveries after 10 seconds. - Verify signatures -- Always validate the
X-Webhook-Signatureheader before trusting the payload. - Handle retries gracefully -- Use the
X-Webhook-DeliveryID to detect and deduplicate retried deliveries. - Monitor delivery health -- Use the deliveries endpoint to check for persistent failures that may indicate a misconfigured URL or an endpoint outage.
- Use the test endpoint -- After creating a webhook, send a test delivery to confirm connectivity before relying on it in production.