Time activity webhook
Subscribe to a Time Clock with featureType: time_activity and you receive events when employees clock, when admins change time activities, when employees submit add/edit/delete requests, and when a timesheet period is locked, submitted, approved, or reopened.
Good to knowNew events are opt-in. Existing webhook subscriptions are unchanged. Add the new
eventTypeson the webhook (API or dashboard) to start receiving them. See Setting up webhook via API.
Did you know?Timesheet period events use the same Time Activity webhook. Create or update a
time_activitysubscription on the Time Clock (entityId) and include names such asadmin_day_lockedoruser_timesheet_approved. You do not create a separate webhook type.
Payload envelope
Every delivery is a flat JSON object (fields at the root, not nested under data) and includes:
| Field | Type | Description |
|---|---|---|
requestId | string | Correlation ID inherited from the originating action. Retries keep the same value; different events from one action may share it |
company | string | Company identifier |
eventType | string | The event that fired |
eventTimestamp | integer | Unix timestamp of the event |
activityType | string | shift, manual_break, time_off, or timesheet_period |
webhookVersion | integer | Payload schema version. Time Activity is version 1 |
userId | integer | The employee the event is about |
timeClockId | integer | The Time Clock that produced the event. For a scoped subscription, this matches its entityId |
Older time-activity events also send evnetTimestamp (a deprecated misspelling of eventTimestamp). Keep reading eventTimestamp. The new request, decline, and timesheet-period events send eventTimestamp only.
Empty optional fields are omitted, not sent as null.
timeActivity fields
timeActivity fieldstimeActivity uses the same shapes as the Time Activities API.
| Field | Applies to | Description |
|---|---|---|
id, start, end | All | Activity ID and time points. end is omitted for an open activity |
managerNote, employeeNote, createdAt, modifiedAt | All | Notes and Unix timestamps when available |
start/end.timestamp, start/end.timezone | All | Unix seconds and TZ database timezone |
start/end.locationData, start/end.geofenceId | Shift, manual break | Punch location and matching geofence when available |
start/end.source | Shift, manual break | Punch origin. Contains type and may include mode, name, or id |
jobId, subJobId, schedulerShiftId, shiftAttachments, isAutoClockOut | Shift | Shift-specific fields |
manualBreakId, isAutoClockOut | Manual break | Manual-break type and auto-clock-out status |
policyTypeId, duration, isAllDay | Time off | Time-off policy type and duration |
Time activity events
| eventType | activityType | Description |
|---|---|---|
clock_in | shift, manual_break | User clocked in |
clock_out | shift, manual_break | User clocked out |
auto_clock_out | shift, manual_break | User clocked out automatically from Time Clock settings |
admin_add | shift, manual_break, time_off | Admin added a time activity |
admin_edit | shift, manual_break | Admin edited a time activity |
admin_delete | shift, manual_break | Admin deleted a time activity |
admin_approved_clock_out_outside_geo_fence_request | shift, manual_break | Admin approved a clock-out outside the geofence. The original clock-in shift is deleted and a new shift is created for the approved times |
admin_approved_add_request | shift, manual_break, time_off | Admin approved a user add request |
admin_approved_edit_request | shift, manual_break | Admin approved a user edit request |
admin_approved_delete_request | shift, manual_break | Admin approved a user delete request |
admin_declined_request | shift, manual_break, time_off | Generic decline. Still sent so existing subscriptions keep working. For shift and break declines, the matching admin_declined_*_request event is also published |
auto_approved_clock_out_outside_geo_fence_request | shift, manual_break | Automatically approved a clock-out outside the geofence |
auto_approved_add_request | shift, manual_break, time_off | Automatically approved a user add request |
auto_approved_edit_request | shift, manual_break | Automatically approved a user edit request |
auto_approved_delete_request | shift, manual_break | Automatically approved a user delete request |
admin_approved_clock_out_without_nfc_request | shift, manual_break | Admin approved a clock-out without NFC after the user clocked in with NFC |
auto_approved_clock_out_without_nfc_request | shift, manual_break | Automatically approved a clock-out without NFC after the user clocked in with NFC |
user_add_request | shift, manual_break, time_off | User submitted an add request that is pending approval |
user_edit_request | shift, manual_break | User submitted an edit request that is pending approval |
user_delete_request | shift, manual_break | User submitted a delete request that is pending approval |
admin_declined_add_request | shift, manual_break, time_off | Admin declined a user's add request |
admin_declined_edit_request | shift, manual_break | Admin declined a user's edit request |
admin_declined_delete_request | shift, manual_break | Admin declined a user's delete request |
Request vs completed activity
user_add_requestincludes the requestedtimeActivity. Punchsourcevalues show where the request was submitted (there are no saved punches yet).user_edit_requestincludes the requested times, not the values currently saved on the timesheet. Punch sources stay those of the original punches.user_delete_requestand the threeadmin_declined_*_requestevents sendtimeActivityIdonly, matchingadmin_delete.- Prefer the three specific decline events for new integrations.
admin_declined_requestremains so existing subscriptions keep working.- Do not subscribe to both
admin_declined_requestand the specific decline events. A declined shift or break request publishes both the generic event and the matching specific event. Time-off declines still send onlyadmin_declined_request.- A time-off add request that does not require approval publishes both
user_add_requestandauto_approved_add_request. Auto-approved shift and manual-break requests publish only the matchingauto_approved_*event.
Timesheet period events
These fire on the same time_activity webhook. activityType is always timesheet_period.
| eventType | Description |
|---|---|
admin_day_locked | Admin locked days for a user. The Lock Days API sends one event per date whose state changed, with startDate equal to endDate |
admin_day_unlocked | Admin unlocked days for a user. The Lock Days API sends one event per date whose state changed, with startDate equal to endDate |
user_timesheet_submitted | User submitted their timesheet for approval |
user_timesheet_unsubmitted | User withdrew a submitted timesheet |
user_timesheet_approved | Admin approved a user's timesheet. Fires only when that user's status actually changed |
user_timesheet_reopened | Admin reopened a user's timesheet. Fires only when that user's status actually changed |
| Field | Type | Description |
|---|---|---|
userId | integer | The employee whose period changed |
performedByUserId | integer | Who performed the action. For submit/unsubmit this is the employee |
timeClockId | integer | The Time Clock |
startDate | string | First date of the period, YYYY-MM-DD |
endDate | string | Last date of the period, YYYY-MM-DD. For a single locked day this equals startDate |
timezone | string | Time Clock timezone (for example America/New_York) |
You can also lock or unlock days through the Lock Days API. Those calls fire admin_day_locked / admin_day_unlocked the same way the dashboard does.
Event examples
Older examples below omit the envelope (requestId, company, eventTimestamp, webhookVersion) for brevity. Live deliveries include those fields.
clock_in - shift
{
"userId": 145,
"timeClockId": 67,
"activityType": "shift",
"eventType": "clock_in",
"timeActivity": {
"id": "65d46394a0973d7bbd55cd26",
"start": {
"timestamp": 1708417940,
"timezone": "Asia/Jerusalem",
"locationData": {
"address": "address test",
"longitude": 0,
"latitude": 0
}
},
"jobId": "cc6e0b98-1f9a-4e3f-8535-c02d55a362d8",
"schedulerShiftId": "cc6e0b98c02d55a362d8",
"shiftAttachments": []
}
}clock_out - shift
{
"userId": 145,
"timeClockId": 67,
"activityType": "shift",
"eventType": "clock_out",
"timeActivity": {
"id": "65d5da9df9a1952f149bc47c",
"start": {
"timestamp": 1708513949,
"timezone": "Asia/Jerusalem",
"locationData": {
"address": "string",
"longitude": 0,
"latitude": 0
}
},
"end": {
"timestamp": 1708513979,
"timezone": "Asia/Jerusalem",
"locationData": {
"address": "string",
"longitude": 0,
"latitude": 0
}
},
"jobId": "cc6e0b98-1f9a-4e3f-8535-c02d55a362d8",
"schedulerShiftId": "cc6e0b98c02d55a362d8",
"shiftAttachments": [
{
"shiftAttachmentId": "a69ef24c-41d5-2e91-d84b-e73ddb5773d1",
"attachment": {
"files": [
{
"fileName": "file",
"fileUrl": "https://link_to_file"
}
]
}
}
]
}
}admin_add - time_off
{
"userId": 146,
"timeClockId": 67,
"activityType": "time_off",
"eventType": "admin_add",
"timeActivity": {
"id": "65d32918a28dbd2c492d76bb",
"start": {
"timestamp": 1710885600,
"timezone": "Asia/Jerusalem"
},
"end": {
"timestamp": 1710971999,
"timezone": "Asia/Jerusalem"
},
"policyTypeId": "65af9319dea5e39064086d93",
"duration": {
"value": 1.0,
"units": "days"
},
"isAllDay": true
}
}admin_edit - manual_break
{
"userId": 145,
"timeClockId": 67,
"activityType": "manual_break",
"eventType": "admin_edit",
"timeActivity": {
"id": "65d370a6a28dbd2c492d76bf",
"start": {
"timestamp": 1708671600,
"timezone": "Asia/Jerusalem"
},
"end": {
"timestamp": 1708675200,
"timezone": "Asia/Jerusalem"
},
"manualBreakId": "aad8cb0d-0d27-4adc-84f9-eb2ac4fa26b6"
}
}admin_delete - shift
{
"userId": 145,
"timeClockId": 67,
"activityType": "shift",
"eventType": "admin_delete",
"timeActivityId": "65d4636ba0973d7bbd55cd25"
}admin_approved_edit_request - shift
{
"userId": 145,
"timeClockId": 67,
"activityType": "shift",
"eventType": "admin_approved_edit_request",
"timeActivity": {
"id": "65d1f5cc8cf696ef8fafd9d4",
"start": {
"timestamp": 1708844400,
"timezone": "Asia/Jerusalem"
},
"end": {
"timestamp": 1708876800,
"timezone": "Asia/Jerusalem"
},
"jobId": "cc6e0b98-1f9a-4e3f-8535-c02d55a362d8",
"shiftAttachments": []
}
}admin_declined_request - time_off
{
"userId": 145,
"timeClockId": 67,
"activityType": "time_off",
"eventType": "admin_declined_request",
"timeActivityId": "65d4a72e0fa9ac7f730c91be"
}Request lifecycle examples
These events let payroll and workforce systems react when a request is submitted or declined, before (or instead of) the completed admin_approved_* / auto_approved_* events.
user_add_request - shift
{
"requestId": "550e8400-e29b-41d4-a716-446655440001",
"company": "123456",
"activityType": "shift",
"eventTimestamp": 1786872600,
"eventType": "user_add_request",
"userId": 145,
"timeClockId": 67,
"timeActivity": {
"id": "shift-request-123",
"employeeNote": "Forgot to clock in",
"start": {
"timestamp": 1786856400,
"timezone": "America/New_York",
"source": { "type": "mobile" }
},
"end": {
"timestamp": 1786885200,
"timezone": "America/New_York",
"source": { "type": "mobile" }
},
"jobId": "job-123",
"subJobId": "sub-job-456",
"isAutoClockOut": false
},
"webhookVersion": 1
}user_edit_request - manual_break
timeActivity is the requested break window, not the saved timesheet value.
{
"requestId": "550e8400-e29b-41d4-a716-446655440002",
"company": "123456",
"activityType": "manual_break",
"eventTimestamp": 1786873200,
"eventType": "user_edit_request",
"userId": 145,
"timeClockId": 67,
"timeActivity": {
"id": "break-456",
"start": {
"timestamp": 1786860000,
"timezone": "America/New_York",
"source": { "type": "mobile" }
},
"end": {
"timestamp": 1786864500,
"timezone": "America/New_York",
"source": { "type": "mobile" }
},
"manualBreakId": "lunch-break",
"isAutoClockOut": false
},
"webhookVersion": 1
}user_delete_request - shift
{
"requestId": "550e8400-e29b-41d4-a716-446655440003",
"company": "123456",
"activityType": "shift",
"eventTimestamp": 1786873800,
"eventType": "user_delete_request",
"userId": 145,
"timeClockId": 67,
"timeActivityId": "shift-123",
"webhookVersion": 1
}admin_declined_add_request - time_off
{
"requestId": "550e8400-e29b-41d4-a716-446655440004",
"company": "123456",
"activityType": "time_off",
"eventTimestamp": 1786874400,
"eventType": "admin_declined_add_request",
"userId": 145,
"timeClockId": 67,
"timeActivityId": "time-off-request-789",
"webhookVersion": 1
}admin_declined_edit_request - shift
{
"requestId": "550e8400-e29b-41d4-a716-446655440005",
"company": "123456",
"activityType": "shift",
"eventTimestamp": 1786875000,
"eventType": "admin_declined_edit_request",
"userId": 145,
"timeClockId": 67,
"timeActivityId": "shift-123",
"webhookVersion": 1
}admin_declined_delete_request - manual_break
{
"requestId": "550e8400-e29b-41d4-a716-446655440006",
"company": "123456",
"activityType": "manual_break",
"eventTimestamp": 1786875600,
"eventType": "admin_declined_delete_request",
"userId": 145,
"timeClockId": 67,
"timeActivityId": "break-456",
"webhookVersion": 1
}Timesheet period examples
admin_day_locked
{
"requestId": "550e8400-e29b-41d4-a716-446655440007",
"company": "123456",
"activityType": "timesheet_period",
"eventTimestamp": 1786876200,
"eventType": "admin_day_locked",
"userId": 145,
"performedByUserId": 999,
"timeClockId": 67,
"startDate": "2026-08-16",
"endDate": "2026-08-16",
"timezone": "America/New_York",
"webhookVersion": 1
}admin_day_unlocked
{
"requestId": "550e8400-e29b-41d4-a716-446655440008",
"company": "123456",
"activityType": "timesheet_period",
"eventTimestamp": 1786876800,
"eventType": "admin_day_unlocked",
"userId": 145,
"performedByUserId": 999,
"timeClockId": 67,
"startDate": "2026-08-16",
"endDate": "2026-08-16",
"timezone": "America/New_York",
"webhookVersion": 1
}user_timesheet_submitted
{
"requestId": "550e8400-e29b-41d4-a716-446655440009",
"company": "123456",
"activityType": "timesheet_period",
"eventTimestamp": 1786877400,
"eventType": "user_timesheet_submitted",
"userId": 145,
"performedByUserId": 145,
"timeClockId": 67,
"startDate": "2026-08-10",
"endDate": "2026-08-16",
"timezone": "America/New_York",
"webhookVersion": 1
}user_timesheet_unsubmitted
{
"requestId": "550e8400-e29b-41d4-a716-446655440010",
"company": "123456",
"activityType": "timesheet_period",
"eventTimestamp": 1786877700,
"eventType": "user_timesheet_unsubmitted",
"userId": 145,
"performedByUserId": 145,
"timeClockId": 67,
"startDate": "2026-08-10",
"endDate": "2026-08-16",
"timezone": "America/New_York",
"webhookVersion": 1
}user_timesheet_approved
{
"requestId": "550e8400-e29b-41d4-a716-446655440011",
"company": "123456",
"activityType": "timesheet_period",
"eventTimestamp": 1786878000,
"eventType": "user_timesheet_approved",
"userId": 145,
"performedByUserId": 999,
"timeClockId": 67,
"startDate": "2026-08-10",
"endDate": "2026-08-16",
"timezone": "America/New_York",
"webhookVersion": 1
}user_timesheet_reopened
{
"requestId": "550e8400-e29b-41d4-a716-446655440012",
"company": "123456",
"activityType": "timesheet_period",
"eventTimestamp": 1786878600,
"eventType": "user_timesheet_reopened",
"userId": 145,
"performedByUserId": 999,
"timeClockId": 67,
"startDate": "2026-08-10",
"endDate": "2026-08-16",
"timezone": "America/New_York",
"webhookVersion": 1
}Subscribe to the new events
curl --request POST \
--url https://api.connecteam.com/settings/v1/webhooks \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: YOUR_API_KEY' \
--data '{
"name": "Time Clock requests and periods",
"url": "https://your-endpoint.com/webhooks/timeclock",
"featureType": "time_activity",
"entityId": "67",
"eventTypes": [
"user_add_request",
"user_edit_request",
"user_delete_request",
"admin_declined_add_request",
"admin_declined_edit_request",
"admin_declined_delete_request",
"admin_day_locked",
"admin_day_unlocked",
"user_timesheet_submitted",
"user_timesheet_unsubmitted",
"user_timesheet_approved",
"user_timesheet_reopened"
]
}'This example scopes the subscription to Time Clock 67. Omit entityId to receive matching Time Activity events from all Time Clocks in the company.
To add these to an existing webhook, GET it, then PUT the previous eventTypes plus the new names. Events you do not list are not delivered.
async function handleTimeActivityWebhook(body) {
switch (body.eventType) {
case 'user_add_request':
case 'user_edit_request':
// body.timeActivity is the requested activity (not yet saved, or not yet the saved edit)
await queueReview(body.userId, body.timeActivity);
break;
case 'user_delete_request':
case 'admin_declined_add_request':
case 'admin_declined_edit_request':
case 'admin_declined_delete_request':
await markRequestClosed(body.timeActivityId, body.eventType);
break;
case 'admin_day_locked':
case 'user_timesheet_approved':
await freezePayrollExport(body.userId, body.startDate, body.endDate);
break;
case 'admin_day_unlocked':
case 'user_timesheet_reopened':
case 'user_timesheet_unsubmitted':
await reopenPayrollExport(body.userId, body.startDate, body.endDate);
break;
default:
break;
}
}Receive events safely
- Return any
2xxresponse within 10 seconds and process the event asynchronously. - A failed delivery may be retried up to 3 times without a fixed delay. Retries contain the same JSON.
- Do not use
requestIdalone as an idempotency key. One action can publish several events with the samerequestId. - Build a key from
requestId,eventType,userId, and the affected activity or period:
function timeActivityEventKey(body) {
const entity = body.timeActivity?.id
|| body.timeActivityId
|| `${body.startDate || ''}:${body.endDate || ''}`;
return [body.requestId, body.eventType, body.userId, entity].join(':');
}See Setting up webhook via API for entityId, broadcast subscriptions, and x-webhook-secret.
Notes
Important Considerations
- Subscribe with
featureType: time_activityandentityIdset to the Time Clock ID as a string. OmitentityIdfor a broadcast subscription.- Existing subscriptions do not start receiving the new events until you add their names to
eventTypes.- New request, decline, and period payloads include
eventTimestamponly. Older event types still also sendevnetTimestamp.- Delete and declined payloads carry
timeActivityIdand omittimeActivity.- A declined shift or break request publishes both
admin_declined_requestand the matchingadmin_declined_*_request. Subscribe to one family, not both. Time-off declines send onlyadmin_declined_request.- Lock Days sends one event per user and date whose state changed (
startDateequalsendDate). Approve/reopen sends one event per user whose status actually changed.- You can lock days from the Lock Days API. Pair that with
admin_day_locked/admin_day_unlockedif you need to keep an external system in sync.
Updated 6 days ago
