Webhooks

Receive real-time HTTP notifications when events happen in TempClock. Webhooks let your system react instantly to clock-ins, worker updates, and more — no polling required.

Overview

Webhooks are HTTP POST requests sent from TempClock to your server whenever specific events occur. Instead of polling the API for changes, your application receives updates in real time.

Each webhook delivery includes:

  • A JSON payload with the event type and data
  • An HMAC-SHA256 signature for verification
  • Custom headers identifying the event and delivery

Note: Webhook endpoints must use HTTPS. TempClock will not deliver to plain HTTP URLs.

Available Events

Subscribe to any combination of these events, or use * to receive all events.

Event Description Triggered When
clock_inWorker clocked inA time entry is created via API or kiosk
clock_outWorker clocked outAn open time entry is closed via API or kiosk
worker_createdNew worker addedA worker is created in the system
worker_updatedWorker details changedA worker record is updated via API
shift_createdNew shift scheduledA shift is created via API
document_expiredCompliance document expiredA worker's document passes its expiry date
pingTest eventManually triggered via API or admin

Subscribing via API

Create a webhook subscription with a POST request. The response includes a signing secret that is only shown once — store it securely.

Create Subscription

POST /api/v1/webhooks.php { "url": "https://your-system.com/webhooks/tempclock", "events": ["clock_in", "clock_out", "worker_updated"], "description": "Bullhorn CRM sync" }

Response (201)

{ "data": { "id": 1, "url": "https://your-system.com/webhooks/tempclock", "events": ["clock_in", "clock_out", "worker_updated"], "active": true, "secret": "a1b2c3d4e5f6..." }, "message": "Webhook subscription created. Store the secret securely." }

Important: The secret field is only returned in the creation response. If you lose it, you must delete and re-create the subscription.

Other Operations

Method Endpoint Description
GET/webhooks.phpList all subscriptions
GET/webhooks.php?id=1Get subscription with recent deliveries
PUT/webhooks.php?id=1Update URL, events, active, or description
DELETE/webhooks.php?id=1Delete subscription and delivery history
POST/webhooks.php?id=1&action=testSend a test ping event

Requires scopes: webhooks:read and/or webhooks:write

Payload Format

Every webhook delivery sends a JSON POST body with a consistent envelope structure:

{ "event": "clock_in", "timestamp": "2026-03-16T14:30:00Z", "delivery_id": 42, "data": { "entry_id": 1234, "worker_id": 56, "worker_name": "John Smith", "clock_in": "2026-03-16 14:30:00", "location_id": 3 } }

HTTP Headers

Header Example Description
Content-Typeapplication/jsonAlways JSON
X-TempClock-Signaturesha256=abc123...HMAC-SHA256 of body
X-TempClock-Eventclock_inEvent type
X-TempClock-Delivery42Unique delivery ID
User-AgentTempClock-Webhooks/1.0Sender identification

Signature Verification

Every delivery includes an X-TempClock-Signature header. Always verify this signature to ensure the request genuinely came from TempClock and hasn't been tampered with.

The signature is an HMAC-SHA256 hash of the raw request body, using your subscription's signing secret as the key.

// PHP — Verify webhook signature $payload = file_get_contents('php://input'); $secret = 'your_signing_secret'; $signature = $_SERVER['HTTP_X_TEMPCLOCK_SIGNATURE'] ?? ''; $expected = 'sha256=' . hash_hmac('sha256', $payload, $secret); if (!hash_equals($expected, $signature)) { http_response_code(401); die('Invalid signature'); } $event = json_decode($payload, true); // Process $event['event'] and $event['data']...
# Python (Flask) — Verify webhook signature import hmac, hashlib @app.route('/webhooks/tempclock', methods=['POST']) def handle_webhook(): payload = request.get_data() secret = b'your_signing_secret' signature = request.headers.get('X-TempClock-Signature', '') expected = 'sha256=' + hmac.new(secret, payload, hashlib.sha256).hexdigest() if not hmac.compare_digest(expected, signature): return 'Invalid signature', 401 event = request.get_json() # Process event['event'] and event['data']... return 'OK', 200
// Node.js (Express) — Verify webhook signature const crypto = require('crypto'); app.post('/webhooks/tempclock', express.raw({type: 'application/json'}), (req, res) => { const payload = req.body; const secret = 'your_signing_secret'; const signature = req.headers['x-tempclock-signature'] || ''; const expected = 'sha256=' + crypto .createHmac('sha256', secret) .update(payload) .digest('hex'); if (signature !== expected) { return res.status(401).send('Invalid signature'); } const event = JSON.parse(payload); // Process event.event and event.data... res.status(200).send('OK'); });

Retry Policy

If your endpoint doesn't respond with a 2xx status code within 30 seconds, TempClock will retry with exponential backoff:

Attempt Delay Approx. Time
1Immediate0 seconds
21 minute+60s
35 minutes+300s
430 minutes+1,800s
52 hours+7,200s

After 5 failed attempts, the delivery is marked as permanently failed. You can monitor delivery status via the API by fetching a subscription with GET /webhooks.php?id=N.

Best Practices

Respond quickly

Return a 200 response within a few seconds. Process the event asynchronously in a queue if your logic is complex.

Always verify signatures

Use constant-time comparison (e.g. hash_equals()) to prevent timing attacks when validating the HMAC signature.

Handle duplicates

Use the delivery_id field to deduplicate events. In rare cases, the same event may be delivered more than once.

Use HTTPS

Webhook URLs must use HTTPS. Use a valid TLS certificate — self-signed certificates will be rejected.

Test with ping events

Use the POST /webhooks.php?id=N&action=test endpoint to send test pings during development.