Real-Time Clocking

Clock in and clock out users in real-time. By default, the server captures the punch time when the request is received. Pass an optional timestamp (Unix seconds) to record a punch that already happened — typically when a physical clock device flushes queued punches after reconnecting from offline mode.

Endpoints

MethodEndpointDescription
POST/time-clock/v1/time-clocks/{timeClockId}/clock-inClock in a user
POST/time-clock/v1/time-clocks/{timeClockId}/clock-outClock out a user

Offline Mode (Retroactive Punches)

Physical clock devices (kiosks, NFC stations, biometric terminals) usually call clock-in/out the moment an employee punches. When a device loses connectivity, punches queue locally. On reconnect, pass the original punch time via timestamp so the timesheet reflects when the employee actually clocked — not when the API call arrived.

👍

Prefer clock-in/out over time activities for offline sync

Use clock-in/out with timestamp, not Create time activities. Time activities write a completed shift. If the employee later clocks out online via the normal clock-out flow, there is no open shift to close. Retroactive clock-in/out preserves the open-shift lifecycle.

📘

Timestamp rules (when timestamp is supplied)

  • Unix time in seconds
  • Not in the future
  • No more than 12 hours in the past
  • Clock-out only: must be strictly after the open shift's clock-in time
  • Clock-out only: the punch day must not be locked or approved
  • When omitted, behavior is unchanged (server captures time at request receipt)

Typical offline flush order for one shift:

  1. POST .../clock-in with the queued clock-in timestamp
  2. POST .../clock-out with the queued clock-out timestamp

For punches older than 12 hours, use Create time activities instead.


Clock In

Record the start of a work period. By default the start time is captured when the call is executed. Pass an optional timestamp to record a clock-in that occurred earlier.

Path Parameters

ParameterTypeRequiredDescription
timeClockIdintegerYesTime clock ID

Request Body

FieldTypeRequiredDescription
userIdintegerYesUser's ID (must be assigned to the time clock)
jobIdstringConditionalJob or sub-job ID (required if job enforcement is enabled)
timezonestringNoTz format (e.g., America/New_York). Defaults to time clock setting
schedulerShiftIdstringNoLink to a scheduled shift
locationDataobjectNoGPS coordinates and address
timestampintegerNoClock-in time in Unix seconds. Omitted = server-now. Max 12 hours in the past; not in the future. Use when flushing offline punches.
⚠️

User Assignment Required

The user must be assigned to the specified time clock before they can clock in. Use the Users API to manage assignments.

Example: Basic Clock In

curl --request POST \
  --url https://api.connecteam.com/time-clock/v1/time-clocks/12345/clock-in \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --data '{
    "userId": 9170357
  }'

Example: Clock In with Job

curl --request POST \
  --url https://api.connecteam.com/time-clock/v1/time-clocks/12345/clock-in \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --data '{
    "userId": 9170357,
    "jobId": "job-123",
    "timezone": "America/New_York"
  }'

Example: Clock In with Location

curl --request POST \
  --url https://api.connecteam.com/time-clock/v1/time-clocks/12345/clock-in \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --data '{
    "userId": 9170357,
    "jobId": "job-123",
    "timezone": "America/New_York",
    "locationData": {
      "address": "123 Main St, New York, NY 10001",
      "latitude": 40.7128,
      "longitude": -74.0060
    }
  }'

Example: Clock In with Scheduler Shift

curl --request POST \
  --url https://api.connecteam.com/time-clock/v1/time-clocks/12345/clock-in \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --data '{
    "userId": 9170357,
    "schedulerShiftId": "sched-shift-789",
    "timezone": "America/New_York"
  }'

Example: Retroactive Clock In (Offline Mode)

curl --request POST \
  --url https://api.connecteam.com/time-clock/v1/time-clocks/12345/clock-in \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --data '{
    "userId": 9170357,
    "jobId": "job-123",
    "timezone": "America/New_York",
    "timestamp": 1748345820
  }'

The returned data.shift.start.timestamp equals 1748345820.

Response

{
  "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "data": {
    "shift": {
      "id": "shift-abc123",
      "userId": 9170357,
      "start": {
        "timestamp": 1704110400,
        "timezone": "America/New_York",
        "locationData": {
          "address": "123 Main St, New York, NY 10001",
          "latitude": 40.7128,
          "longitude": -74.0060
        },
        "source": {
          "type": "api"
        }
      },
      "jobId": "job-123",
      "schedulerShiftId": null,
      "createdAt": 1704110400,
      "modifiedAt": 1704110400
    }
  }
}

Clock Out

Record the end of a work period. By default the end time is captured when the call is executed. Pass an optional timestamp to record a clock-out that occurred earlier. Associates with the user's current open shift.

Path Parameters

ParameterTypeRequiredDescription
timeClockIdintegerYesTime clock ID

Request Body

FieldTypeRequiredDescription
userIdintegerYesUser's ID
timezonestringNoTz format. Defaults to time clock setting
timestampintegerNoClock-out time in Unix seconds. Omitted = server-now. Max 12 hours in the past; not in the future; must be after the open shift's clock-in. Use when flushing offline punches.
locationDataobjectNoGPS coordinates and address
📝

Open Shift Required

The user must have an open shift (clocked in but not out) in this time clock. The clock-out will be associated with the most recent open shift.

Example: Basic Clock Out

curl --request POST \
  --url https://api.connecteam.com/time-clock/v1/time-clocks/12345/clock-out \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --data '{
    "userId": 9170357
  }'

Example: Clock Out with Location

curl --request POST \
  --url https://api.connecteam.com/time-clock/v1/time-clocks/12345/clock-out \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --data '{
    "userId": 9170357,
    "timezone": "America/New_York",
    "locationData": {
      "address": "456 Oak Ave, New York, NY 10002",
      "latitude": 40.7145,
      "longitude": -74.0055
    }
  }'

Example: Retroactive Clock Out (Offline Mode)

curl --request POST \
  --url https://api.connecteam.com/time-clock/v1/time-clocks/12345/clock-out \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --data '{
    "userId": 9170357,
    "timezone": "America/New_York",
    "timestamp": 1748374620
  }'

The returned data.shift.end.timestamp equals 1748374620.

Response

{
  "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "data": {
    "shift": {
      "id": "shift-abc123",
      "userId": 9170357,
      "start": {
        "timestamp": 1704110400,
        "timezone": "America/New_York",
        "source": {
          "type": "api"
        }
      },
      "end": {
        "timestamp": 1704139200,
        "timezone": "America/New_York",
        "locationData": {
          "address": "456 Oak Ave, New York, NY 10002",
          "latitude": 40.7145,
          "longitude": -74.0055
        },
        "source": {
          "type": "api"
        }
      },
      "jobId": "job-123",
      "createdAt": 1704110400,
      "modifiedAt": 1704139200
    }
  }
}

Integration Example

Complete clock-in/clock-out workflow, including flushing an offline queue:

class TimeClockIntegration {
  constructor(apiKey, timeClockId) {
    this.apiKey = apiKey;
    this.timeClockId = timeClockId;
    this.baseUrl = 'https://api.connecteam.com/time-clock/v1/time-clocks';
  }

  async clockIn(userId, { jobId, location, timestamp } = {}) {
    const body = {
      userId,
      timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
    };
    if (jobId) body.jobId = jobId;
    if (location) body.locationData = location;
    if (timestamp != null) body.timestamp = timestamp;

    const response = await fetch(`${this.baseUrl}/${this.timeClockId}/clock-in`, {
      method: 'POST',
      headers: { 'X-API-KEY': this.apiKey, 'Content-Type': 'application/json' },
      body: JSON.stringify(body),
    });
    return response.json();
  }

  async clockOut(userId, { location, timestamp } = {}) {
    const body = {
      userId,
      timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
    };
    if (location) body.locationData = location;
    if (timestamp != null) body.timestamp = timestamp;

    const response = await fetch(`${this.baseUrl}/${this.timeClockId}/clock-out`, {
      method: 'POST',
      headers: { 'X-API-KEY': this.apiKey, 'Content-Type': 'application/json' },
      body: JSON.stringify(body),
    });
    return response.json();
  }

  /** Flush queued punches after device reconnects (oldest first). */
  async flushOfflineQueue(events) {
    for (const event of events) {
      if (event.type === 'clock-in') {
        await this.clockIn(event.userId, {
          jobId: event.jobId,
          location: event.location,
          timestamp: event.timestamp,
        });
      } else if (event.type === 'clock-out') {
        await this.clockOut(event.userId, {
          location: event.location,
          timestamp: event.timestamp,
        });
      }
    }
  }
}

// Live punch (no timestamp — server-now)
const tc = new TimeClockIntegration('YOUR_API_KEY', 12345);
await tc.clockIn(9170357, { jobId: 'job-123' });
await tc.clockOut(9170357);

// Offline flush after reconnect
await tc.flushOfflineQueue([
  { type: 'clock-in', userId: 9170357, jobId: 'job-123', timestamp: 1748345820 },
  { type: 'clock-out', userId: 9170357, timestamp: 1748374620 },
]);

Error Responses

HTTP StatusError CodeWhen
400INVALID_TIMESTAMP_FUTURESupplied timestamp is in the future
400INVALID_TIMESTAMP_TOO_OLDtimestamp is more than 12 hours in the past
400INVALID_TIMESTAMP_BEFORE_CLOCK_IN(Clock-out only.) timestamp is at or before the open shift's clock-in time
409HAS_LOCKED_DAYStimestamp falls on a locked or approved timesheet day
400User not assigned to the time clock
400User already has an open shift (clock-in)
400NO_OPEN_SHIFT(Clock-out only.) No open shift to close
400JOB_REQUIRED(Clock-in only.) Job ID required by time clock settings
400OPEN_BREAK(Clock-in only.) User is clocked into a manual break
400OUTSIDE_GEOFENCEPunch location is outside the configured geofence

All pre-existing error codes for these endpoints remain unchanged.


Clock In API Reference · Clock Out API Reference