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 know

New events are opt-in. Existing webhook subscriptions are unchanged. Add the new eventTypes on 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_activity subscription on the Time Clock (entityId) and include names such as admin_day_locked or user_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:

FieldTypeDescription
requestIdstringCorrelation ID inherited from the originating action. Retries keep the same value; different events from one action may share it
companystringCompany identifier
eventTypestringThe event that fired
eventTimestampintegerUnix timestamp of the event
activityTypestringshift, manual_break, time_off, or timesheet_period
webhookVersionintegerPayload schema version. Time Activity is version 1
userIdintegerThe employee the event is about
timeClockIdintegerThe 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 uses the same shapes as the Time Activities API.

FieldApplies toDescription
id, start, endAllActivity ID and time points. end is omitted for an open activity
managerNote, employeeNote, createdAt, modifiedAtAllNotes and Unix timestamps when available
start/end.timestamp, start/end.timezoneAllUnix seconds and TZ database timezone
start/end.locationData, start/end.geofenceIdShift, manual breakPunch location and matching geofence when available
start/end.sourceShift, manual breakPunch origin. Contains type and may include mode, name, or id
jobId, subJobId, schedulerShiftId, shiftAttachments, isAutoClockOutShiftShift-specific fields
manualBreakId, isAutoClockOutManual breakManual-break type and auto-clock-out status
policyTypeId, duration, isAllDayTime offTime-off policy type and duration

Time activity events

eventTypeactivityTypeDescription
clock_inshift, manual_breakUser clocked in
clock_outshift, manual_breakUser clocked out
auto_clock_outshift, manual_breakUser clocked out automatically from Time Clock settings
admin_addshift, manual_break, time_offAdmin added a time activity
admin_editshift, manual_breakAdmin edited a time activity
admin_deleteshift, manual_breakAdmin deleted a time activity
admin_approved_clock_out_outside_geo_fence_requestshift, manual_breakAdmin 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_requestshift, manual_break, time_offAdmin approved a user add request
admin_approved_edit_requestshift, manual_breakAdmin approved a user edit request
admin_approved_delete_requestshift, manual_breakAdmin approved a user delete request
admin_declined_requestshift, manual_break, time_offGeneric 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_requestshift, manual_breakAutomatically approved a clock-out outside the geofence
auto_approved_add_requestshift, manual_break, time_offAutomatically approved a user add request
auto_approved_edit_requestshift, manual_breakAutomatically approved a user edit request
auto_approved_delete_requestshift, manual_breakAutomatically approved a user delete request
admin_approved_clock_out_without_nfc_requestshift, manual_breakAdmin approved a clock-out without NFC after the user clocked in with NFC
auto_approved_clock_out_without_nfc_requestshift, manual_breakAutomatically approved a clock-out without NFC after the user clocked in with NFC
user_add_requestshift, manual_break, time_offUser submitted an add request that is pending approval
user_edit_requestshift, manual_breakUser submitted an edit request that is pending approval
user_delete_requestshift, manual_breakUser submitted a delete request that is pending approval
admin_declined_add_requestshift, manual_break, time_offAdmin declined a user's add request
admin_declined_edit_requestshift, manual_breakAdmin declined a user's edit request
admin_declined_delete_requestshift, manual_breakAdmin declined a user's delete request
📝

Request vs completed activity

  • user_add_request includes the requested timeActivity. Punch source values show where the request was submitted (there are no saved punches yet).
  • user_edit_request includes the requested times, not the values currently saved on the timesheet. Punch sources stay those of the original punches.
  • user_delete_request and the three admin_declined_*_request events send timeActivityId only, matching admin_delete.
  • Prefer the three specific decline events for new integrations. admin_declined_request remains so existing subscriptions keep working.
  • Do not subscribe to both admin_declined_request and 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 only admin_declined_request.
  • A time-off add request that does not require approval publishes both user_add_request and auto_approved_add_request. Auto-approved shift and manual-break requests publish only the matching auto_approved_* event.

Timesheet period events

These fire on the same time_activity webhook. activityType is always timesheet_period.

eventTypeDescription
admin_day_lockedAdmin locked days for a user. The Lock Days API sends one event per date whose state changed, with startDate equal to endDate
admin_day_unlockedAdmin unlocked days for a user. The Lock Days API sends one event per date whose state changed, with startDate equal to endDate
user_timesheet_submittedUser submitted their timesheet for approval
user_timesheet_unsubmittedUser withdrew a submitted timesheet
user_timesheet_approvedAdmin approved a user's timesheet. Fires only when that user's status actually changed
user_timesheet_reopenedAdmin reopened a user's timesheet. Fires only when that user's status actually changed
FieldTypeDescription
userIdintegerThe employee whose period changed
performedByUserIdintegerWho performed the action. For submit/unsubmit this is the employee
timeClockIdintegerThe Time Clock
startDatestringFirst date of the period, YYYY-MM-DD
endDatestringLast date of the period, YYYY-MM-DD. For a single locked day this equals startDate
timezonestringTime 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 2xx response 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 requestId alone as an idempotency key. One action can publish several events with the same requestId.
  • 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_activity and entityId set to the Time Clock ID as a string. Omit entityId for 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 eventTimestamp only. Older event types still also send evnetTimestamp.
  • Delete and declined payloads carry timeActivityId and omit timeActivity.
  • A declined shift or break request publishes both admin_declined_request and the matching admin_declined_*_request. Subscribe to one family, not both. Time-off declines send only admin_declined_request.
  • Lock Days sends one event per user and date whose state changed (startDate equals endDate). 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_unlocked if you need to keep an external system in sync.

Setting up webhook via API · Lock Days · API Reference


Did this page help you?