Time tracking

The time tracking API provides endpoints to manage employee work hours through shifts and shift rates. Shifts record when employees work, including start times, end times, breaks, and compensation calculations. Shift rates define how different types of work are compensated based on hourly rates or multipliers.

Use the time tracking API to automate payroll calculations, maintain accurate work records, and ensure compliance with local labor regulations.

The time tracking API only works for Global Payroll employees. Independent contractors use timesheets to track their time. For more information, see Timesheets.

Overview

The time tracking API consists of two main resources:

Shifts

Record employee work hours with detailed metadata including start times, end times, breaks, and approval dates.

Shift rates

Define compensation rules for different shift types using flat rates or percentage multipliers.

Prerequisites

Before using the time tracking API, ensure you have the following:

Shifts are linked to contracts. Retrieve contract IDs using the GET list of contracts endpoint.

Create shift rates before submitting shifts. Each shift must reference a valid shift rate for payroll calculations.

All requests require a valid API token. Include it in the Authorization header as a Bearer token.

Key concepts

Payroll cycle processing

Shifts are processed and compensated at the end of each payroll cycle. Understanding the payroll cycle is essential for accurate time tracking:

  • Shifts submitted for past payroll cycles are compensated in the current cycle
  • The system does not validate whether shift dates fall within the current cycle
  • Only unprocessed shifts can be updated or deleted
  • Payroll cutoff dates can be configured in the Deel app

Compensation calculation

Shift compensation combines the employee base salary with the shift rate:

  • Hourly contracts: Uses the hourly salary directly
  • Non-hourly contracts: Calculates an equivalent hourly salary
  • Total compensation: Shift rate × Base salary × Total payable hours

Payable hours exclude unpaid breaks and include only approved work time.

Manage shifts

Shifts represent individual work periods for employees. Each shift includes timing information, break periods, and links to a shift rate for compensation calculations.

Create shifts

You can create multiple shifts for a single contract in one API call. Before creating shifts, ensure you have created the shift rates you want to reference.

1

Prepare shift data

Gather the contract ID, shift dates, work hours, break information, and the shift rate external ID.

2

Submit the request

Make a POST request to the Create a time tracking shift endpoint with your shift data.

3

Verify the response

Check the response for the created shift details including system-generated timestamps.

1import requests
2import os
3
4url = "https://api.letsdeel.com/rest/v2/time_tracking/shifts"
5
6headers = {
7 "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
8 "Content-Type": "application/json"
9}
10
11payload = {
12 "data": {
13 "contract_id": "123456",
14 "shifts": [
15 {
16 "external_id": "shift_123456",
17 "description": "Regular weekday shift",
18 "date_of_work": "2023-10-01",
19 "meta": {
20 "start": {
21 "date": "2023-10-01",
22 "time": "08:00",
23 "is_rest_day": False,
24 "is_public_holiday": False
25 },
26 "end": {
27 "date": "2023-10-01",
28 "time": "17:00",
29 "is_rest_day": False,
30 "is_public_holiday": False
31 },
32 "breaks": [
33 {
34 "start": {
35 "date": "2023-10-01",
36 "time": "12:00"
37 },
38 "end": {
39 "date": "2023-10-01",
40 "time": "12:30"
41 },
42 "is_paid": True
43 }
44 ],
45 "approval_date": "2023-10-02"
46 },
47 "summary": {
48 "shift_rate_external_id": "rate1234",
49 "shift_duration_hours": 8,
50 "total_break_hours": 1,
51 "payable_break_hours": 0.5,
52 "total_payable_hours": 7.5
53 }
54 }
55 ]
56 }
57}
58
59response = requests.post(url, json=payload, headers=headers)
60print(response.json())

Request parameters

The request body contains shift data organized into metadata and summary sections:

NameRequiredTypeDescription
contract_idYesstringUnique identifier of the contract for which shifts are being submitted
shiftsYesarrayArray of shift objects to create

Shift object structure

Each shift in the array must include the following fields:

NameTypeDescription
external_idstringUser-defined unique identifier for the shift
descriptionstringDescription of the work performed during the shift
date_of_workstring (date)Date when the shift was performed (YYYY-MM-DD)
summary.shift_rate_external_idstringExternal ID of the shift rate to apply
summary.total_payable_hoursnumberTotal hours to be compensated for this shift

Response

A successful response returns HTTP 200 with the created shift details:

1{
2 "data": [
3 {
4 "external_id": "shift_123456",
5 "organization_id": 123456,
6 "description": "Regular weekday shift",
7 "date_of_work": "2023-10-01T00:00:00Z",
8 "contract_id": "123456",
9 "meta": {
10 "start": {
11 "date": "2023-10-01",
12 "time": "08:00",
13 "is_rest_day": false,
14 "is_public_holiday": false
15 },
16 "end": {
17 "date": "2023-10-01",
18 "time": "17:00",
19 "is_rest_day": false,
20 "is_public_holiday": false
21 },
22 "breaks": [
23 {
24 "start": {
25 "date": "2023-10-01",
26 "time": "12:00"
27 },
28 "end": {
29 "date": "2023-10-01",
30 "time": "12:30"
31 },
32 "is_paid": true
33 }
34 ],
35 "approval_date": "2023-10-02"
36 },
37 "summary": {
38 "shift_rate_external_id": "rate1234",
39 "shift_duration_hours": 8,
40 "total_break_hours": 1,
41 "payable_break_hours": 0.5,
42 "total_payable_hours": 7.5
43 },
44 "created_at": "2023-10-02T10:30:00Z",
45 "updated_at": "2023-10-02T10:30:00Z"
46 }
47 ]
48}

List shifts

Retrieve all shifts in your organization with optional pagination. Results are sorted by creation time.

Make a GET request to the List time tracking shifts endpoint:

1import requests
2import os
3
4url = "https://api.letsdeel.com/rest/v2/time_tracking/shifts"
5
6headers = {
7 "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}"
8}
9
10params = {
11 "limit": 10,
12 "offset": 0
13}
14
15response = requests.get(url, headers=headers, params=params)
16print(response.json())

Query parameters

NameTypeDescriptionDefault
limitnumberMaximum number of shifts to return per request100
offsetnumberNumber of shifts to skip before returning results0

Retrieve a single shift

Retrieve detailed information about a specific shift using its external ID.

Make a GET request to the Retrieve a single time tracking shift endpoint:

1import requests
2import os
3
4shift_external_id = "shift_123456"
5url = f"https://api.letsdeel.com/rest/v2/time_tracking/shifts/{shift_external_id}"
6
7headers = {
8 "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}"
9}
10
11response = requests.get(url, headers=headers)
12print(response.json())

Update a shift

Modify shift details for shifts that have not been processed for payroll. Once a shift is processed at the payroll cutoff date, it cannot be updated.

You can only update shifts that have not been processed for payroll. Shifts are processed for payroll at the cutoff date. Configure the cutoff date in the Deel app under payroll settings.

Make a PATCH request to the Update a time tracking shift endpoint:

1import requests
2import os
3
4shift_external_id = "shift_123456"
5url = f"https://api.letsdeel.com/rest/v2/time_tracking/shifts/{shift_external_id}"
6
7headers = {
8 "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
9 "Content-Type": "application/json"
10}
11
12payload = {
13 "data": {
14 "description": "Updated shift description",
15 "summary": {
16 "total_payable_hours": 8.0
17 }
18 }
19}
20
21response = requests.patch(url, json=payload, headers=headers)
22print(response.json())

Delete a shift

Remove a shift that has not been processed for payroll. Deletion is permanent and cannot be undone.

You can only delete shifts that have not been processed for payroll. Once processed at the payroll cutoff date, shifts cannot be deleted.

Make a DELETE request to the Delete a time tracking shift endpoint:

1import requests
2import os
3
4shift_external_id = "shift_123456"
5url = f"https://api.letsdeel.com/rest/v2/time_tracking/shifts/{shift_external_id}"
6
7headers = {
8 "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}"
9}
10
11response = requests.delete(url, headers=headers)
12print(f"Status code: {response.status_code}")

A successful deletion returns HTTP 204 with an empty response body.

Manage shift rates

Shift rates define compensation rules for different types of work. They specify how to calculate payment based on hours worked and employee base salary.

Shift rate types

The time tracking API supports two calculation methods:

MULTIPLIER_PERCENTAGE: Multiplies the base hourly salary by a percentage factor.

Formula: Total amount = (MULTIPLIER_PERCENTAGE/100) × Base hourly salary × Total payable hours

Example: An employee with a base salary of $10/hour works a 5-hour shift with a 200% multiplier:

  • Calculation: 2.0 × $10 × 5 = $100
  • Total compensation: $100

Use cases:

  • Overtime shifts (150% or 200% multiplier)
  • Weekend work premiums
  • Holiday compensation

Create a shift rate

Define shift rates before creating shifts. Each shift rate needs a unique external ID that you reference when creating shifts.

1

Choose rate type

Decide whether to use a multiplier percentage or a flat hourly rate based on your compensation policy.

2

Submit the request

Make a POST request to the Create time tracking shift rate endpoint.

3

Save the external ID

Store the external ID to reference when creating shifts.

1import requests
2import os
3
4url = "https://api.letsdeel.com/rest/v2/time_tracking/shift_rates"
5
6headers = {
7 "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
8 "Content-Type": "application/json"
9}
10
11payload = {
12 "data": {
13 "external_id": "overtime_rate_150",
14 "name": "Overtime - 150%",
15 "type": "MULTIPLIER_PERCENTAGE",
16 "value": 150
17 }
18}
19
20response = requests.post(url, json=payload, headers=headers)
21print(response.json())

Request parameters

NameRequiredTypeDescription
external_idYesstringUnique identifier for the shift rate
nameYesstringHuman-readable name describing the shift rate purpose
typeYesenumRate calculation method: MULTIPLIER_PERCENTAGE or PER_HOUR_FLAT_RATE
valueYesnumberRate value (percentage for multiplier, dollar amount for flat rate)

List shift rates

Retrieve all shift rates in your organization:

1import requests
2import os
3
4url = "https://api.letsdeel.com/rest/v2/time_tracking/shift_rates"
5
6headers = {
7 "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}"
8}
9
10params = {
11 "limit": 10,
12 "offset": 0
13}
14
15response = requests.get(url, headers=headers, params=params)
16print(response.json())

Retrieve a single shift rate

Get details for a specific shift rate using its external ID:

1import requests
2import os
3
4rate_external_id = "overtime_rate_150"
5url = f"https://api.letsdeel.com/rest/v2/time_tracking/shift_rates/{rate_external_id}"
6
7headers = {
8 "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}"
9}
10
11response = requests.get(url, headers=headers)
12print(response.json())

Update a shift rate

Modify shift rate details if the rate is not currently used in any shifts:

You can only update shift rates that are not assigned to any shifts. Once a shift rate is used, you must create a new rate with different values.

1import requests
2import os
3
4rate_external_id = "overtime_rate_150"
5url = f"https://api.letsdeel.com/rest/v2/time_tracking/shift_rates/{rate_external_id}"
6
7headers = {
8 "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
9 "Content-Type": "application/json"
10}
11
12payload = {
13 "data": {
14 "name": "Overtime - 175%",
15 "value": 175
16 }
17}
18
19response = requests.patch(url, json=payload, headers=headers)
20print(response.json())

Delete a shift rate

Remove a shift rate that is not assigned to any shifts:

You can only delete shift rates that are not used in any shifts. Deletion is permanent and cannot be undone.

1import requests
2import os
3
4rate_external_id = "overtime_rate_150"
5url = f"https://api.letsdeel.com/rest/v2/time_tracking/shift_rates/{rate_external_id}"
6
7headers = {
8 "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}"
9}
10
11response = requests.delete(url, headers=headers)
12print(f"Status code: {response.status_code}")

Best practices

Follow these guidelines to ensure accurate time tracking and payroll processing:

  • Use consistent naming conventions for external IDs across your system
  • Include identifiable prefixes (e.g., shift_, rate_) to distinguish resource types
  • Store external IDs in your database to maintain mapping between systems
  • Never reuse external IDs for different resources
  • Verify all times are in the correct timezone for the employee location
  • Use 24-hour format (HH:MM) for all time fields
  • Ensure break periods fall within the shift start and end times
  • Distinguish between paid and unpaid breaks based on local labor laws
  • Submit shifts before the payroll cutoff date for the current cycle
  • Verify shifts have been created successfully before the cutoff
  • Contact support if you need to modify processed shifts
  • Configure payroll cutoff dates to align with your business processes
  • Test shift rate calculations in sandbox before production use
  • Verify multiplier percentages produce expected compensation amounts
  • Document which shift rates apply to different scenarios
  • Review calculated payroll amounts before finalizing
  • Implement retry logic for temporary network failures
  • Validate shift data client-side before API submission
  • Log all API responses for troubleshooting
  • Monitor for failed shift creations and resubmit as needed

Next steps