TimeLeafTimeLeaf
Back to site

Core Features

Webhooks

Send TimeLeaf events to your own systems in real time with outbound webhooks, signed so you can verify them.

Last updated: October 27, 2026

Webhooks send TimeLeaf events, such as an approved leave request or a submitted timesheet, to a URL you choose as they happen. TimeLeaf has no public REST API for reading or writing data; webhooks are how you connect other systems.

What you need

  • The Business plan or above. On a lower plan the Webhooks menu entry is hidden and nothing is sent. Endpoints you already set up are kept, and sending starts again after an upgrade.
  • An admin account.
  • An https URL on a public address that can receive a POST. Private, loopback, link-local and cloud-metadata addresses are refused when you save the URL, and again at every delivery.

Add an endpoint

1

Open Webhooks

Go to Integrations › Webhooks and click Add endpoint.

The Webhooks page with no endpoints yet and the Add endpoint button

2

Enter the URL and events

Enter the Endpoint URL and an optional Description. Under Events, tick the events you want, or tick All events, including ones added later. Click Add endpoint.

3

Copy the signing secret

TimeLeaf shows the Signing secret once. Click Copy, store it with your receiving system, and click Done. After this only its first characters are shown. There is no way to show it again or rotate it; to get a new one, delete the endpoint and add it again.

4

Send a test

Click Send test on the endpoint. TimeLeaf sends a webhook.test event once (it is never retried) and tells you the result.

Manage endpoints

Each endpoint on Integrations › Webhooks shows its URL, whether it is Enabled or Disabled, its events and the start of its secret.

  • Disable stops deliveries. Retries that were waiting resume when you Enable it again.
  • Deliveries shows the 50 most recent deliveries: event, status (delivered, pending or failed), response code, attempts and time.
  • Delete removes the endpoint and its delivery history, after you confirm.

Event types

TimeLeaf supports 21 event types:

EventTrigger
leave.requestedAn employee submits a leave request
leave.approvedA leave request is approved
leave.rejectedA leave request is rejected
leave.cancelledA leave request is cancelled
timesheet.submittedAn employee submits a weekly timesheet
timesheet.approvedA timesheet is approved
timesheet.rejectedA timesheet is rejected
employee.createdA new employee is added
employee.updatedAn employee profile is updated
employee.deactivatedAn employee is deactivated
shift.createdA shift is assigned
shift.updatedA shift is modified
job.createdA job is created. The shifts it makes do not fire shift.created
job.updatedA job is edited, a day's hours change, someone is added to a day, or one of its days is cancelled. The shifts it changes do not fire shift.updated
job.cancelledA job is cancelled
overtime.alertAn overtime threshold is reached
timebank.accruedOvertime hours are banked
timebank.usedBanked hours are used
timebank.adjustedA manual time bank adjustment is posted
timebank.expiredBanked hours expire
timebank.payoutA time bank payout is requested or paid

Job events

Jobs send their own events instead of one shift.created or shift.updated per shift. Creating, editing or cancelling a job, or one of its days, sends one job.* event and no per-shift events for the shifts it makes, changes or cancels. Taking someone off a place, replacing them, assigning an open place, or filling places from the usual people does send shift.updated for each shift it changes, with assignmentId, userId (null when the place is left open) and date.

Eventdata
job.createdjobId, locationId (the job site), title (or null), startDate, endDate, days (days booked), shifts (shifts made) and openShifts (places nobody holds yet)
job.updatedAfter an edit of the whole job: jobId, locationId, title, startDate, endDate, and retimedShifts, madeShifts, cancelledShifts and openedShifts. After a change to one day's hours: the same, plus changedDay. After someone is added to a day: jobId, addedPlaceDay and the four counts. After one day is cancelled: jobId, cancelledDay and cancelledShifts
job.cancelledjobId and cancelledShifts

timebank.payout carries hours (the banked hours debited from the balance), payoutMultiplier (the cash-out rate stored on the payout, 1 when none) and payableHours (the hours to pay in cash: hours × payoutMultiplier). Pay from payableHours, not hours.

The request

A webhook is a POST with a JSON body { "event", "timestamp" (ISO 8601), "data" }, and these headers:

  • Content-Type: application/json
  • X-Webhook-Signature: sha256=<hex-encoded HMAC> — an HMAC-SHA256 of the raw request body, keyed with the endpoint's signing secret
  • X-Webhook-Id: <delivery id>
  • User-Agent: TimeLeaf-Webhooks/1.0

To verify a delivery: read the raw body, compute an HMAC-SHA256 of it with your signing secret, and compare against the value after sha256=.

A 2xx response within 10 seconds counts as success. Redirects are never followed — a 3xx counts as a failed attempt.

Webhook payloads include the event type, a timestamp, and the key fields for that event (the request, employee, shift, or time-bank record involved) — not necessarily every column on the underlying record.

Retries and delivery guarantees

  • A failed delivery is tried up to 3 times in total (the original attempt plus 2 retries): the first retry ~30 seconds after the failure, the second ~2 minutes after that.
  • Retries run on a periodic sweep, so a retry can arrive up to about 15 minutes later.
  • After the third attempt fails, the delivery is marked failed and not retried again.
  • A delivery still unsent 24 hours after the event occurred expires and is never sent.
  • Delivery is at least once — the same delivery can arrive more than once, so de-duplicate on X-Webhook-Id. There's no ordering guarantee across events.