Session Alert Webhooks
Configure, secure, test and troubleshoot signed session-start and session-stop webhooks.
Session Alert Webhooks
WorkPulse can send a signed JSON request to your server when a user starts or stops real tracked work. Break sessions are excluded.
Access required
The company must have Session Activity Notifications, API Access and Webhooks. A tenant owner or user with Manage Session Notifications can configure the endpoint. Open Session Alerts from the main navigation.
Set up the webhook
- Build a receiving endpoint that accepts an HTTPS
POSTon public port 443. - Open Session Alerts, choose Set up beside Webhook and enter the endpoint URL.
- Select
session.started,session.stopped, or both, and enable the endpoint. - Save and immediately copy the generated
wpwh_signing secret. The full secret is shown only once. - Store the secret in your server's protected environment or secret manager.
- Choose Test webhook and confirm the delivery is marked Delivered.
- Turn on the page's global Live alerts switch, select the same live events there, and save the alert settings.
- Start and stop one real work session and confirm both the receiving system and Recent deliveries.
Settings reference
| Setting | What it controls |
|---|---|
| Receiving endpoint URL | A public HTTPS URL on port 443. Redirects and local, private, link-local or reserved destinations are rejected. |
| Events | The event types sent to this endpoint. The same event must also be enabled in the page's global alert rules. |
| Endpoint enabled | Pauses or resumes this endpoint without deleting its configuration or history. |
| Signing secret | Authenticates WorkPulse requests. Only the final four characters remain visible after creation. |
| Generate a new signing secret | Immediately invalidates the old secret. Update the receiver before sending another test. |
| Test webhook | Queues a sample session.notification.test request without starting a real work session. |
| Remove | Deletes the active endpoint configuration. Existing delivery history remains available. |
Request headers
| Header | Purpose |
|---|---|
Content-Type: application/json | The request body is JSON. |
X-Workpulse-Event | session.started, session.stopped or session.notification.test. |
X-Workpulse-Event-ID | Stable UUID for the event. |
X-Workpulse-Timestamp | Unix timestamp used when generating the signature. |
X-Workpulse-Signature | HMAC-SHA256 signature in the form v1=HEX_DIGEST. |
Idempotency-Key | The stable event ID. Store it so duplicate attempts are safe. |
Example session.started payload
{
"id": "019f1234-5678-7abc-9def-0123456789ab",
"type": "session.started",
"occurred_at": "2026-08-23T09:15:00Z",
"tenant": {
"id": "019d1234-5678-7abc-9def-0123456789ab",
"name": "Example Company"
},
"user": {
"id": "019e1234-5678-7abc-9def-0123456789ab",
"name": "Example Employee",
"email": "employee@example.com"
},
"session": {
"id": 321,
"start_time": "2026-08-23T09:15:00Z",
"end_time": null,
"duration_seconds": null,
"team": {"id": 4, "name": "Operations"},
"project": {"id": 18, "name": "Client work"},
"session_type": {"id": 1, "name": "Desktop"},
"auto_closed": false,
"closure_reason": null
},
"is_test": false,
"delivery": {"attempt": 1, "replay": false},
"schema_version": "1.0"
}Team or project can be null when the matching inclusion setting is off or the session has no value. A stopped event includes end_time, duration_seconds, and automatic-closure context.
Verify the signature
Read the request as raw bytes before parsing JSON. Join the timestamp, one period and the exact raw body, then calculate a hexadecimal HMAC-SHA256 with the signing secret.
signed_value = X-Workpulse-Timestamp + "." + exact_raw_request_body
expected = "v1=" + HMAC_SHA256_HEX(signing_secret, signed_value)- Reject the request if the timestamp is missing, invalid or older than five minutes.
- Calculate the expected value from the unmodified raw body.
- Compare the expected and supplied signatures with a constant-time comparison.
- Only then parse and process the JSON.
- Do not log the signing secret or full sensitive payloads.
Responses, retries and duplicates
- Return any
2xxresponse within 10 seconds to mark the delivery successful. - Redirects are not followed.
- HTTP
408,425,429and5xxresponses are retried after approximately 60 seconds, 5 minutes and 30 minutes, for up to four attempts. - Other
4xxresponses are terminal failures and can be replayed from Recent deliveries after the problem is corrected. - Use the event ID or
Idempotency-Keyto make repeated attempts safe. A manual replay retains the event ID and setsdelivery.replaytotrue. - An endpoint is automatically paused after 20 consecutive failures. Correct the receiver, re-enable it and send a test.
Troubleshooting
| What you see | What to check |
|---|---|
| Webhook is locked | Confirm the plan or custom entitlements include API Access, Webhooks and Session Activity Notifications. |
| Test remains pending | The WorkPulse queue worker may be unavailable. Contact support with the approximate test time. |
| HTTP 401 or 403 | Check that the receiver uses the current signing secret and verifies the exact raw body. |
| HTTP 404 | Check the full path and deployment of the receiving endpoint. |
| HTTP 429 or 5xx | WorkPulse retries automatically. Review receiver capacity and logs. |
| Test works but live events do not | Enable global Live alerts and select the event both globally and on the endpoint. |
| Unexpected duplicate | Deduplicate with the event ID or Idempotency-Key. |
Rotate the secret immediately if it is exposed or lost. Rotation invalidates the old value, so update the receiver and send a new test before relying on live alerts.
Was this article helpful?