Endpoint Reference

Complete reference for all TempClock API v1 endpoints. All endpoints require Bearer token authentication and return JSON responses.

New in v1.2, backwards compatible

  • New /payroll.php endpoint: approved, priced hours per worker per pay week. It pays exactly the shifts the Sage 50 Payroll export pays, with the same totals.
  • Approval status and paid_minutes on /timesheets.php, from the same pricing engine as payroll.
  • Scopes per resource (workers:read, payroll:read, shifts:write and so on). A call without its scope gets 403 insufficient_scope.
  • Clock-outs in the repeated hour when the clocks go back keep the real time worked. A clock into a closed and locked pay week is recorded and flagged for payroll, never refused.
  • Workers created or changed with a key are written to the audit log under that key, including pay and charge rate changes with before and after values.

The full, machine-readable reference is the OpenAPI 3.0.3 file. Where this page and the OpenAPI file differ, the OpenAPI file is right.

New in v1.1, backwards compatible

  • GPS & geofence telemetry on /clock.php and /timesheets.php (latitude, longitude, gps_accuracy, within_geofence, distance_meters, low_gps_confidence).
  • Pre-clock-in surveys on /clock.php: supply survey_id and survey_answers, or get a 403 blocked_by_survey.
  • New /on-site-now.php endpoint, live snapshot of clocked-in workers grouped by site with geofence rollups.
  • preferred_language on workers (en, pl, ro, lt); geofence_mode, kiosk_language and gps_accuracy_threshold on locations.
  • entry_filter query param on /timesheets.php (shifts, active, breaks, all); default is shifts: matches previous behaviour.
  • Four new webhook events: clock.blocked_by_survey, clock.geofence_breach, clock.geofence_refused, clock.low_gps_confidence: opt in per subscription.

Base URL

https://tempclock.com/api/v1/
GET

/workers.php

Retrieve a paginated list of workers, or a single worker by ID. Returns worker names, contact details, locations, and status.

Required scope: workers:read

Query Parameters

Parameter Type Required Description
id integer No Return a single worker by ID. Overrides other filters.
location_id integer No Filter workers by their assigned location.
active boolean No Filter by active status. Use 1 for active, 0 for inactive.
search string No Search by first name, last name, or employee reference.
limit integer No Number of results per page. Default: 50, max: 100.
offset integer No Number of results to skip for pagination. Default: 0.

Example Request, List Workers

curl -H "Authorization: Bearer tc_live_a1b2c3..." \ "https://tempclock.com/api/v1/workers.php?active=1&location_id=3&limit=2"

Example Response

{ "data": [ { "id": 142, "first_name": "Sarah", "last_name": "Mitchell", "employee_ref": "EMP-0142", "email": "s.mitchell@example.com", "phone": "07700 900142", "location_id": 3, "location_name": "Manchester Warehouse", "department": "Picking", "active": true, "created_at": "2025-06-15T09:30:00Z" }, { "id": 156, "first_name": "James", "last_name": "Okonkwo", "employee_ref": "EMP-0156", "email": "j.okonkwo@example.com", "phone": "07700 900156", "location_id": 3, "location_name": "Manchester Warehouse", "department": "Packing", "active": true, "created_at": "2025-08-22T14:15:00Z" } ], "pagination": { "total": 34, "limit": 2, "offset": 0, "has_more": true } }

Example Request, Single Worker

curl -H "Authorization: Bearer tc_live_a1b2c3..." \ "https://tempclock.com/api/v1/workers.php?id=142"

Example Response

{ "data": { "id": 142, "first_name": "Sarah", "last_name": "Mitchell", "employee_ref": "EMP-0142", "email": "s.mitchell@example.com", "phone": "07700 900142", "location_id": 3, "location_name": "Manchester Warehouse", "department": "Picking", "active": true, "created_at": "2025-06-15T09:30:00Z" } }
GET

/timesheets.php

Retrieve a paginated list of time entries (clock-in/clock-out records). Filter by date range, worker, or location. Ideal for payroll integrations and reporting.

Required scope: timesheets:read

Query Parameters

Parameter Type Required Description
date_from string No Start date filter in YYYY-MM-DD format.
date_to string No End date filter in YYYY-MM-DD format.
worker_id integer No Filter entries for a specific worker.
location_id integer No Filter entries by location.
entry_filter
v1.1
string No One of shifts (default, completed clock-in/out shifts only), active (currently clocked-in entries within the last 14h), breaks (break entries only), or all (no filter).
limit integer No Results per page. Default: 50, max: 100.
offset integer No Number of results to skip. Default: 0.

v1.1 response fields

Time entries now also include GPS / geofence telemetry when the clock-in supplied a location: within_geofence (0 / 1 / null when geofencing is off), distance_meters, gps_accuracy, and low_gps_confidence.

Example Request

curl -H "Authorization: Bearer tc_live_a1b2c3..." \ "https://tempclock.com/api/v1/timesheets.php?date_from=2026-03-01&date_to=2026-03-07&location_id=3&entry_filter=shifts"

Example Response

{ "data": [ { "id": 8921, "worker_id": 142, "worker_name": "Sarah Mitchell", "location_id": 3, "location_name": "Manchester Warehouse", "date": "2026-03-03", "clock_in": "2026-03-03T07:02:14Z", "clock_out": "2026-03-03T15:31:47Z", "hours_worked": 8.49, "break_minutes": 30, "status": "approved", "clock_in_method": "facial_recognition", "notes": null }, { "id": 8935, "worker_id": 156, "worker_name": "James Okonkwo", "location_id": 3, "location_name": "Manchester Warehouse", "date": "2026-03-03", "clock_in": "2026-03-03T06:58:33Z", "clock_out": "2026-03-03T14:05:12Z", "hours_worked": 7.11, "break_minutes": 0, "status": "approved", "clock_in_method": "pin", "notes": "Early finish, medical appointment" } ], "pagination": { "total": 128, "limit": 50, "offset": 0, "has_more": true } }
GET

/payroll.php

One line per worker per pay week, with paid hours and pay split into elements (regular, overtime, weekend, bank holiday, night). The lines hold exactly the shifts the Sage 50 Payroll export pays for the same pay week, priced by the same engine, so for each pay week the gross_pay of the lines adds up to the Sage file's total pay.

  • Which shifts belong to your key: a shift belongs to the client that owns the site it was worked at, or to the worker's home client when it has no site. A pooled worker from another client who worked at your site is included; your worker who worked at another client's site is not.
  • Which shifts are paid: completed, approved by the agency, not rejected by the client, not disputed, with no open query, approved by the client too when the client requires approval before payroll, and with a pay rate. A shift the client rejected is never paid here, even if the agency approved it.
  • Not complete yet: meta.shifts_not_approved counts the completed shifts the approval gate leaves out. Above 0 means the period is not ready for payroll.
  • Closed weeks: shifts in a closed and locked pay week return their frozen figures (source is locked). A shift clocked into a locked week and paid as an adjustment in a later week is left out of its own week, so it is never paid twice.
  • Check before paying: flags on a line names shifts that passed approval but were not paid, for example no_pay_rate.
Required scope: payroll:read

Query Parameters

Parameter Type Required Description
week string No Any date (YYYY-MM-DD) inside the pay week you want.
date_from string No Start of a range (YYYY-MM-DD). Widened to the start of its pay week. Use with date_to, not with week.
date_to string No End of a range. Widened to the end of its pay week. At most 26 pay weeks per request. With no week or range you get the last complete pay week.
worker_id integer No Only this worker.
location_id integer No Only shifts worked at this site.
include_entries boolean No 1 lists the time entry ids behind each line in entry_ids.
limit integer No Worker-week lines per page. Default: 50, max: 200.
offset integer No Number of lines to skip. Default: 0.

Example Request

curl -H "Authorization: Bearer tc_live_a1b2c3..." \ "https://tempclock.com/api/v1/payroll.php?week=2026-03-04&limit=1"

Example Response

{ "data": [ { "worker_id": 142, "employee_id": "EMP-0142", "worker_name": "Sample Worker", "pay_week_start": "2026-03-02", "pay_week_end": "2026-03-08", "shifts": 5, "paid_minutes": 2400, "paid_hours": 40.00, "gross_pay": 480.00, "charge_total": 640.00, "margin": 160.00, "base_pay_rate": 12, "elements": { "regular": { "minutes": 2400, "hours": 40, "pay": 480, "charge": 640, ... }, ... }, "source": "live", "flags": [] } ], "pagination": { "total": 37, "limit": 1, "offset": 0, "has_more": true }, "meta": { "period_from": "2026-03-02", "period_to": "2026-03-08", "pay_week_start_day": 1, "approval_rule": "agency_approved", "shifts_not_approved": 0, "currency": "GBP" } }
GET

/locations.php

Retrieve a list of locations (work sites), or a single location by ID. Includes address, capacity, and active status.

Required scope: locations:read

Query Parameters

Parameter Type Required Description
id integer No Return a single location by ID.
active boolean No Filter by active status. 1 for active, 0 for inactive.
limit integer No Results per page. Default: 50, max: 100.
offset integer No Number of results to skip. Default: 0.

Example Request

curl -H "Authorization: Bearer tc_live_a1b2c3..." \ "https://tempclock.com/api/v1/locations.php?active=1"

Example Response

{ "data": [ { "id": 1, "name": "London Head Office", "address": "45 King Street, London EC2V 8DQ", "capacity": 200, "active": true, "created_at": "2024-01-10T08:00:00Z" }, { "id": 3, "name": "Manchester Warehouse", "address": "Unit 12, Trafford Park, Manchester M17 1HH", "capacity": 150, "active": true, "created_at": "2024-03-15T10:30:00Z" } ], "pagination": { "total": 5, "limit": 50, "offset": 0, "has_more": false } }
POST

/clock.php

Clock a worker in or out. This creates a real time entry in the system, identical to a kiosk clock-in. Use with care in production environments.

Required scope: clock:write

Request Body (JSON)

Field Type Required Description
worker_id integer Yes The ID of the worker to clock in or out.
action string Yes Either "in" or "out".
location_id integer No Location to clock in at. Defaults to the worker's assigned location.
notes string No Optional note attached to the time entry.
latitude
v1.1
number Conditional GPS latitude (WGS84). Required when the location is configured with geofence_mode = strict. The response will include within_geofence and distance_meters when this is supplied.
longitude
v1.1
number Conditional GPS longitude (WGS84). Paired with latitude.
gps_accuracy
v1.1
integer No Horizontal accuracy of the GPS fix in metres. Compared against the location's gps_accuracy_threshold; fixes worse than the threshold set low_gps_confidence: true in the response.
survey_id
v1.1
integer Conditional ID of the pre-clock-in survey being responded to. Required when the location has an active blocking survey, otherwise the call returns 403 blocked_by_survey.
survey_answers
v1.1
array Conditional Array of answer objects: { question_id, answer_text?, answer_bool?, answer_choice? }. Required alongside survey_id.

Example Request, Clock In

curl -X POST \ "https://tempclock.com/api/v1/clock.php" \ -H "Authorization: Bearer tc_live_a1b2c3..." \ -H "Content-Type: application/json" \ -d '{ "worker_id": 142, "action": "in", "location_id": 3, "notes": "Clocked in via API integration" }'

Example Response (201 Created)

{ "data": { "id": 9012, "worker_id": 142, "worker_name": "Sarah Mitchell", "action": "in", "location_id": 3, "location_name": "Manchester Warehouse", "timestamp": "2026-03-10T07:02:00Z", "notes": "Clocked in via API integration" }, "message": "Worker clocked in successfully." }

Error Responses

409 Conflict - Returned if the worker is already clocked in and you attempt another clock-in, or if the worker has no open clock-in and you attempt a clock-out.

{ "error": true, "message": "Worker is already clocked in.", "status": 409 }

400 Bad Request - Returned if required fields are missing or the action value is not “in” or “out”.

404 Not Found - Returned if the worker_id does not exist or belongs to another client.

403 Forbidden, blocked_by_survey (new in v1.1) - The location has a blocking pre-clock-in survey that has not been satisfied. Submit a fresh survey_id + survey_answers in a follow-up request.

{ "error": true, "error_code": "blocked_by_survey", "question_id": 34, "message": "Please see your supervisor before clocking in.", "status": 403 }

403 Forbidden, outside_geofence (new in v1.1) - The location has geofence_mode = strict and the supplied GPS coordinates were outside the radius.

{ "error": true, "error_code": "outside_geofence", "distance_meters": 412, "radius_meters": 150, "message": "You are not at Manchester Warehouse. 412m away (max 150m).", "status": 403 }
GET

/on-site-now.php

new in v1.1

Live snapshot of currently-clocked-in workers, grouped by location, with GPS / geofence rollups. Mirrors the “Site occupancy” panel on the dashboard. A worker counts as live when clock_out IS NULL and clock_in is within the last 14 hours, forgotten clock-outs are excluded so the totals match reality.

Required scope: timesheets:read

Query Parameters

Parameter Type Required Description
location_id integer No Return only the slice for a single location.
include_workers integer No Default 1. Set to 0 for an aggregate-only payload (no per-worker arrays). Use this for lightweight dashboards.

Example Request

curl -H "Authorization: Bearer tc_live_a1b2c3..." \ "https://tempclock.com/api/v1/on-site-now.php"

Example Response

{ "data": { "as_of": "2026-05-27T15:42:00Z", "totals": { "clocked_in": 12, "on_site": 11, "off_site": 1, "geofence_unknown": 0, "low_gps_confidence": 0 }, "locations": [ { "location_id": 3, "location_name": "Manchester Warehouse", "clocked_in": 5, "on_site": 5, "off_site": 0, "geofence_unknown": 0, "workers": [ { "worker_id": 142, "name": "Sarah Mitchell", "employee_id": "EMP-0142", "entry_id": 9012, "clock_in": "2026-05-27T08:01:00Z", "minutes_on_shift": 461, "within_geofence": 1, "distance_meters": 12, "gps_accuracy": 8, "low_gps_confidence": false } ] } ], "unassigned": [] } }

Note: within_geofence is null for any location where geofencing has not been configured, those workers count towards on_site and are surfaced in the geofence_unknown tally so dashboards can distinguish “inside the fence” from “no fence”.

GET POST

/shifts.php

List scheduled shifts or create new ones. GET retrieves existing shifts with filtering; POST creates a new shift assignment for a worker.

Required scopes: shifts:read (GET) shifts:write (POST)

GET Query Parameters

Parameter Type Required Description
date_from string No Start date filter (YYYY-MM-DD).
date_to string No End date filter (YYYY-MM-DD).
worker_id integer No Filter shifts for a specific worker.
location_id integer No Filter shifts by location.
limit integer No Results per page. Default: 50, max: 100.
offset integer No Number of results to skip. Default: 0.

GET Example Request

curl -H "Authorization: Bearer tc_live_a1b2c3..." \ "https://tempclock.com/api/v1/shifts.php?date_from=2026-03-10&date_to=2026-03-14&location_id=3"

GET Example Response

{ "data": [ { "id": 504, "worker_id": 142, "worker_name": "Sarah Mitchell", "location_id": 3, "location_name": "Manchester Warehouse", "shift_date": "2026-03-10", "start_time": "07:00", "end_time": "15:00", "notes": null }, { "id": 505, "worker_id": 156, "worker_name": "James Okonkwo", "location_id": 3, "location_name": "Manchester Warehouse", "shift_date": "2026-03-10", "start_time": "06:00", "end_time": "14:00", "notes": "Early start, goods delivery" } ], "pagination": { "total": 18, "limit": 50, "offset": 0, "has_more": false } }

POST Request Body (JSON)

Field Type Required Description
worker_id integer Yes The worker to assign the shift to.
shift_date string Yes Date of the shift (YYYY-MM-DD).
start_time string Yes Shift start time in HH:MM 24-hour format.
end_time string Yes Shift end time in HH:MM 24-hour format.
location_id integer No Location for the shift. Defaults to the worker's assigned location.
notes string No Optional note for the shift.

POST Example Request

curl -X POST \ "https://tempclock.com/api/v1/shifts.php" \ -H "Authorization: Bearer tc_live_a1b2c3..." \ -H "Content-Type: application/json" \ -d '{ "worker_id": 142, "shift_date": "2026-03-15", "start_time": "08:00", "end_time": "16:00", "location_id": 3, "notes": "Saturday overtime shift" }'

POST Example Response (201 Created)

{ "data": { "id": 512, "worker_id": 142, "worker_name": "Sarah Mitchell", "location_id": 3, "location_name": "Manchester Warehouse", "shift_date": "2026-03-15", "start_time": "08:00", "end_time": "16:00", "notes": "Saturday overtime shift" }, "message": "Shift created successfully." }
GET

/departments.php

Retrieve a list of departments configured for your account. Useful for mapping workers to organisational units.

Required scope: departments:read

Query Parameters

Parameter Type Required Description
active boolean No Filter by active status. 1 for active, 0 for inactive.
limit integer No Results per page. Default: 50, max: 100.
offset integer No Number of results to skip. Default: 0.

Example Request

curl -H "Authorization: Bearer tc_live_a1b2c3..." \ "https://tempclock.com/api/v1/departments.php?active=1"

Example Response

{ "data": [ { "id": 1, "name": "Picking", "active": true, "created_at": "2024-01-10T08:00:00Z" }, { "id": 2, "name": "Packing", "active": true, "created_at": "2024-01-10T08:00:00Z" }, { "id": 3, "name": "Driving", "active": true, "created_at": "2024-02-20T11:15:00Z" }, { "id": 4, "name": "Administration", "active": true, "created_at": "2024-03-01T09:00:00Z" } ], "pagination": { "total": 4, "limit": 50, "offset": 0, "has_more": false } }
GET

/cost-codes.php

Retrieve a list of cost codes used for project tracking and financial reporting. Cost codes allow you to allocate time entries to specific projects or budgets.

Required scope: cost-codes:read

Query Parameters

Parameter Type Required Description
active boolean No Filter by active status.
limit integer No Results per page. Default: 50, max: 100.
offset integer No Number of results to skip. Default: 0.

Example Request

curl -H "Authorization: Bearer tc_live_a1b2c3..." \ "https://tempclock.com/api/v1/cost-codes.php?active=1"

Example Response

{ "data": [ { "id": 1, "code": "PRJ-001", "name": "Warehouse Expansion Phase 2", "active": true, "created_at": "2025-09-01T08:00:00Z" }, { "id": 2, "code": "PRJ-002", "name": "Seasonal Q1 Campaign", "active": true, "created_at": "2026-01-05T10:30:00Z" }, { "id": 3, "code": "GEN-OPS", "name": "General Operations", "active": true, "created_at": "2024-01-10T08:00:00Z" } ], "pagination": { "total": 3, "limit": 50, "offset": 0, "has_more": false } }

Changelog

API version history and notable changes.

v1.2.1 Current October 2026
  • /payroll.php pays exactly the shifts the Sage 50 Payroll export pays: same approval gate (client-rejected, disputed and queried shifts left out), same site-client rule, same totals
  • /clock.php: clock-outs in the repeated hour when the clocks go back keep the real time worked; clocks into a locked pay week are recorded and flagged for payroll
  • /shifts.php: dormant (off-season) workers cannot be booked
  • Workers created or changed with a key are written to the audit log under that key, including pay and charge rate changes with before and after values
v1.2 September 2026
  • New /payroll.php endpoint
  • Approval status and paid minutes on /timesheets.php
  • Scopes per resource, read only or read and write
v1.1 May 2026
  • GPS and geofencing on /clock.php, pre-clock-in surveys, /on-site-now.php, kiosk language, four new webhook events
v1.0 January 2026
  • Initial public API release
  • 7 endpoints: workers, timesheets, locations, clock, shifts, departments, cost-codes
  • Bearer token authentication with scoped API keys
  • Rate limiting at 60 requests/minute per key
  • Paginated list responses with consistent format