GuidesAPI ReferenceChangelog
Guides

Time tracking

Learn how to use the time tracking API to keep the time sheets of your Global Payroll employees under control

With the time tracking API, you can manage the time worked by employees, and add, update, retrieve and delete their shifts.

📘

The time tracking API only works for Global Payroll

Independent contractors use timesheets to track their time. For more information, see Timesheets.

Manage shifts

Here's a few things to keep in mind before starting to manage shifts:

  • Shifts are linked contracts. To manage shifts, you need the contract ID that you can retrieve from the GET list of contracts endpoint.
  • Shifts are processed and compensated at the end of each payroll cycle.
  • If you submit a shift for a past payroll cycle, it will be compensated at the end of the current cycle without validating if the date of the shift falls within the current cycle.
  • (Remove if redundant?) Shifts will be compensated according to the shift_rate attached to each shift and combination of per hour base salary of the employee.

Add shifts for an employee

You can add multiple shifts for a single contract by providing an array of shifts. Before creating a shift, you need to create a shift rate that you will link to the shift.

To add shifts, make a POST request to the Create a time tracking shift endpoint.

curl --location --request POST 'https://api.letsdeel.com/rest/v2/time_tracking/shifts' \
--header 'Authorization: Bearer {{token}}' \
--header 'Content-Type: application/json' \
--data '{
    "data": {
        "contract_id": "123456",
        "shifts": [
            {
                "external_id": "shift_123456",
                "description": "This is a sample shift description.",
                "date_of_work": "2023-10-01",
                "meta": {
                    "start": {
                        "date": "2023-10-01",
                        "time": "08:00",
                        "is_rest_day": false,
                        "is_public_holiday": false
                    },
                    "end": {
                        "date": "2023-10-01",
                        "time": "17:00",
                        "is_rest_day": false,
                        "is_public_holiday": false
                    },
                    "breaks": [
                        {
                            "start": {
                                "date": "2023-10-01",
                                "time": "12:00"
                            },
                            "end": {
                                "date": "2023-10-01",
                                "time": "12:30"
                            },
                            "is_paid": true
                        }
                    ],
                    "approval_date": "2023-10-02"
                },
                "summary": {
                    "shift_rate_external_id": "rate1234",
                    "shift_duration_hours": 8,
                    "total_break_hours": 1,
                    "payable_break_hours": 0.5,
                    "total_payable_hours": 7.5
                }
            }
        ]
    }
}'

In the body:

NameRequiredTypeFormatDescriptionExample
contract_idtruestring-Unique identifier of the contract for which shifts are being submitted123456
descriptiontruestring-Description of shift. Use it to describe what kind of work is done during the shift.This is a sample shift description.
external_idtruestring-User-defined ID of the shiftshift_123456
date_of_worktruestringdateDate on which shift is performed. It is used to identify the payroll cycle of the shift2023-10-01
metafalseobject-Object containing metadata about the shift. This data is used for descriptive purposes and doesn't affect how the payroll is calculated.-
meta.start.datetruestringdateStart date of the shift2023-10-01
meta.start.timetruestring-Start time of the shift08:00
meta.start.is_rest_daytrueboolean-Is there a rest day or weekend on the start date of the shiftfalse
meta.start.is_public_holidaytrueboolean-Is public holiday on the start of the shiftfalse
meta.end.datetruestringdateEnd date of the shift2023-10-01
meta.end.timetruestring-End time of the shift. The format is HH:MM.17:00
meta.end.is_rest_daytrueboolean-Is there a rest day or weekend on the end date of the shiftfalse
meta.end.is_public_holidaytrueboolean-Is public holiday on the end of the shiftfalse
meta.breaksfalsearray-List of breaks during the shift. Multiple breaks are allowed.-
meta.breaks.start.datetruestringdateStart date of the break2023-10-01
meta.breaks.start.timetruestring-Start time of the break. The format is HH:MM.12:00
meta.breaks.[0].end.datetruestringdateEnd date of the break2023-10-01
meta.breaks.[0].end.timetruestring-End time of the break. The format is HH:MM.12:30
meta.breaks.[0].is_paidtrueboolean-Defines if break is paid or nottrue
meta.approval_datetruestringdateDate on which shift is approved2023-10-02
summarytrueobject-Object containing numerical data about the shift. This data is used to calculate the amount to be paid for the shift.-
summary.shift_rate_external_idtruestring-ID of the shift rate. Use it to link the shift to a shift rate you created.rate1234
summary.shift_duration_hoursfalsenumber-Total time of the shift in hours8
summary.total_break_hoursfalsenumber-Total break time in hours1
summary.payable_break_hoursfalsenumber-Total breaks hours that must be paid0.5
summary.total_payable_hourstruenumber-Total hours that need be paid using the shift rate provided above7.5

A successful response (200) returns the details of the shift created.

{
  "data": [
    {
      "external_id": "95c35493-41aa-44f8-9154-5a25cbbc1865",
      "organization_id": 0,
      "description": "string",
      "date_of_work": "2019-08-24T14:15:22Z",
      "contract_id": "string",
      "meta": {
        "start": {
          "date": "^\\dddd-\\dd-\\dd$",
          "time": "^\\dd:\\dd$",
          "is_rest_day": true,
          "is_public_holiday": true
        },
        "end": {
          "date": "^\\dddd-\\dd-\\dd$",
          "time": "^\\dd:\\dd$",
          "is_rest_day": true,
          "is_public_holiday": true
        },
        "breaks": [
          {
            "start": {
              "date": "^\\dddd-\\dd-\\dd$",
              "time": "^\\dd:\\dd:$"
            },
            "end": {
              "date": "^\\dddd-\\dd-\\dd$",
              "time": "^\\dd:\\dd$"
            },
            "is_paid": true
          }
        ],
        "approval_date": "^\\dddd-\\dd-\\dd$"
      },
      "summary": {
        "shift_duration_hours": 0,
        "total_break_hours": 0,
        "payable_break_hours": 0,
        "total_payable_hours": 0
      },
      "created_at": "2022-05-24T09:38:46.235Z",
      "updated_at": "2022-05-24T09:38:46.235Z"
    }
  ]
}

Where:

NameRequiredTypeFormatDescriptionExample
external_idtruestring-User-defined ID of the shiftshift_123456
organization_idtruenumber-The ID of your organization123456
descriptionfalsestring-Description of shiftThis is a sample shift description.
date_of_worktruestringdate-timeDate of the shift2019-08-24T14:15:22Z
contract_idfalsestring-Unique identifier of the contract that shifts were submitted for123456
metafalseobject-Object containing metadata about the shift. This data is used for descriptive purposes and doesn't affect how the payroll is calculated.-
summarytrueobject-Object containing numerical data about the shift. This data is used to calculate the amount to be paid for the shift.-
created_attruestringdate-timeDate on which the shift is created2022-05-24T09:38:46.235Z
updated_attruestringdate-timeDate on which the shift is updated2022-05-24T09:38:46.235Z

List shifts in your organization

You can list the shifts in your organization and sort them by the time of creation.

To list shifts, make a GET request to the List time tracking shifts endpoint.

curl --location --request GET 'https://api.letsdeel.com/rest/v2/time_tracking/shifts?limit=10&offset=20' \
--header 'Authorization: Bearer {{token}}'

In the query:

NameRequiredTypeFormatDescriptionExample
limitfalsenumberNumber of rows that must be returned in one API call100
offsetfalsenumberNumber of rows that must be skipped when returning the results10

A successful response (200) returns the list of shifts available in your organization and matching any filters applied. For example:

{
  "data": [
    {
      "external_id": "d3m0d3m0-d3m0-d3m0-d3m0-d3m0d3m0d3m0",
      "organization_id": 0,
      "description": "string",
      "date_of_work": "2019-08-24T14:15:22Z",
      "contract_id": "string",
      "meta": {
        "start": {
          "date": "^\\dddd-\\dd-\\dd$",
          "time": "^\\dd:\\dd$",
          "is_rest_day": true,
          "is_public_holiday": true
        },
        "end": {
          "date": "^\\dddd-\\dd-\\dd$",
          "time": "^\\dd:\\dd$",
          "is_rest_day": true,
          "is_public_holiday": true
        },
        "breaks": [
          {
            "start": {
              "date": "^\\dddd-\\dd-\\dd$",
              "time": "^\\dd:\\dd:$"
            },
            "end": {
              "date": "^\\dddd-\\dd-\\dd$",
              "time": "^\\dd:\\dd$"
            },
            "is_paid": true
          }
        ],
        "approval_date": "^\\dddd-\\dd-\\dd$"
      },
      "summary": {
        "shift_duration_hours": 0,
        "total_break_hours": 0,
        "payable_break_hours": 0,
        "total_payable_hours": 0
      },
      "created_at": "2022-05-24T09:38:46.235Z",
      "updated_at": "2022-05-24T09:38:46.235Z"
    }
  ],
  "page": {
    "total_rows": 0,
    "items_per_page": 1,
    "offset": 999999999
  }
}

Where:

NameRequiredTypeFormatDescriptionExample
datatruearray-The list of shifts available-
pagetrueobject-Contains information to navigate to the next set of results, if applicable-

Retrieve a single shift

You can retrieve the information of a single shift starting from the external_id of the shift.

To retrieve a shift, make a GET request to the Retrieve a single time tracking shift endpoint.

curl --location --request GET 'https://api.letsdeel.com/rest/v2/time_tracking/shifts/{{external_id}}' \
--header 'Authorization: Bearer {{token}}'

A successful response (200) returns the information of the requested shift.

{
  "external_id": "d3m0d3m0-d3m0-d3m0-d3m0-d3m0d3m0d3m0",
  "organization_id": 0,
  "description": "string",
  "date_of_work": "2019-08-24T14:15:22Z",
  "contract_id": "string",
  "meta": {
    "start": {
      "date": "^\\dddd-\\dd-\\dd$",
      "time": "^\\dd:\\dd$",
      "is_rest_day": true,
      "is_public_holiday": true
    },
    "end": {
      "date": "^\\dddd-\\dd-\\dd$",
      "time": "^\\dd:\\dd$",
      "is_rest_day": true,
      "is_public_holiday": true
    },
    "breaks": [
      {
        "start": {
          "date": "^\\dddd-\\dd-\\dd$",
          "time": "^\\dd:\\dd:$"
        },
        "end": {
          "date": "^\\dddd-\\dd-\\dd$",
          "time": "^\\dd:\\dd$"
        },
        "is_paid": true
      }
    ],
    "approval_date": "^\\dddd-\\dd-\\dd$"
  },
  "summary": {
    "shift_duration_hours": 0,
    "total_break_hours": 0,
    "payable_break_hours": 0,
    "total_payable_hours": 0
  },
  "created_at": "2022-05-24T09:38:46.235Z",
  "updated_at": "2022-05-24T09:38:46.235Z"
}

Update shifts for an employee

If you need it, you can update the information of a shift.

📘

You can only update shifts that haven't been processed for payroll

Shifts are processed for payroll at the cutoff date. You can configure the cutoff date in the Deel app.

To update a shift, make a PATCH request to the Update a time tracking shift endpoint.

curl --location --request PATCH 'https://api.letsdeel.com/rest/v2/time_tracking/shifts/{{external_id}}' \
--header 'Authorization: Bearer {{token}}' \
--header 'Content-Type: application/json' \
--data '{
    "data": {
        "description": "This is a sample shift description.",
        "date_of_work": "2023-10-01",
        "meta": {
            "start": {
                "date": "2023-10-01",
                "time": "08:00",
                "is_rest_day": false,
                "is_public_holiday": false
            },
            "end": {
                "date": "2023-10-01",
                "time": "17:00",
                "is_rest_day": false,
                "is_public_holiday": false
            },
            "breaks": [
                {
                    "start": {
                        "date": "2023-10-01",
                        "time": "12:00"
                    },
                    "end": {
                        "date": "2023-10-01",
                        "time": "13:00"
                    },
                    "is_paid": false
                }
            ],
            "approval_date": "2023-10-01"
        },
        "summary": {
            "shift_duration_hours": 8,
            "total_break_hours": 1,
            "payable_break_hours": 0.5,
            "total_payable_hours": 7.5
        }
    }
}'

In the path:

NameRequiredTypeFormatDescriptionExample
external_idtruestring-User-defined ID of the shiftshift_123456

In the body:

NameRequiredTypeFormatDescriptionExample
datatrueobject-Contains the information of the shift that must be updated.-

A successful response (200) returns the updated shift. For example:

{
  "external_id": "95c35493-41aa-44f8-9154-5a25cbbc1865",
  "organization_id": 0,
  "description": "string",
  "date_of_work": "2019-08-24T14:15:22Z",
  "contract_id": "string",
  "meta": {
    "start": {
      "date": "^\\dddd-\\dd-\\dd$",
      "time": "^\\dd:\\dd$",
      "is_rest_day": true,
      "is_public_holiday": true
    },
    "end": {
      "date": "^\\dddd-\\dd-\\dd$",
      "time": "^\\dd:\\dd$",
      "is_rest_day": true,
      "is_public_holiday": true
    },
    "breaks": [
      {
        "start": {
          "date": "^\\dddd-\\dd-\\dd$",
          "time": "^\\dd:\\dd:$"
        },
        "end": {
          "date": "^\\dddd-\\dd-\\dd$",
          "time": "^\\dd:\\dd$"
        },
        "is_paid": true
      }
    ],
    "approval_date": "^\\dddd-\\dd-\\dd$"
  },
  "summary": {
    "shift_duration_hours": 0,
    "total_break_hours": 0,
    "payable_break_hours": 0,
    "total_payable_hours": 0
  },
  "created_at": "2022-05-24T09:38:46.235Z",
  "updated_at": "2022-05-24T09:38:46.235Z"
}

Delete shift for a contract

You can delete a shift for a contract by using the external_id of the shift.

To delete a shift, make a DELETE request to the Delete a time tracking shift endpoint.

📘

You can only delete shifts that haven't been processed for payroll

Shifts are processed for payroll at the cutoff date. You can configure the cutoff date in the Deel app.

curl --location --request DELETE 'https://api.letsdeel.com/rest/v2/time_tracking/shifts/{{external_id}}' \
--header 'Authorization: Bearer {{token}}'

Where:

NameRequiredTypeFormatDescriptionExample
external_idtruestring-User-defined ID of the shiftshift_123456

A successful response (204) returns an empty body.

Manage shift rates

Shift rates are used in payroll calculations to define the amount of the salary to be paid for a specific shift. There shift rate types are:

NameDescriptionFormulaExample
MULTIPLIER_PERCENTAGEDefines the rate of a shift as a percentage of the salary, using the employee's hourly salary (if it's a hourly contract) or equivalent hourly salary (for non-hourly contracts).Total amount for shift = (MULTIPLIER_PERCENTAGE/100) * Per_hour_salary * Total_payable_hours10$/hour is the base salary, user submitted shift with total of 5 payable hours and according to the shift rate attached to the shift MULTIPLIERPERCENTAGE is set to 200% so Total amount paid for the shift = 2 10 _ 5 = 100$
PER_HOUR_FLAT_RATEDefine the rate of a shift as a flat rate per hour.Total amount for shift = PER_HOUR_FLAT_RATE * Total_payable_hoursPER_HOUR_FLAT_RATE set to 100$ and total_payable_hours for the shift are 5 hours Total amount paid for the shift = 100 * 5 = 500$

Create a shift rate

You can create shift rates for your organization, which you can then map to individual shifts when creating them.

To create a shift rate, make a POST request to the time_tracking/shift_rates endpoint.

curl --location --request POST 'https://api.letsdeel.com/rest/v2/time_tracking/shift_rates' \
--header 'Authorization: Bearer {{token}}' \
--header 'Content-Type: application/json' \
--data '{
    "data": {
        "external_id": "regular_rate_1",
        "name": "Regular Shift rate 1",
        "type": "PER_HOUR_FLAT_RATE",
        "value": 150
    }
}'

Where:

NameRequiredTypeFormatDescriptionExample
 external_idtruestring-User defined unique identifier for the shift rateregular_rate_1

Step 1: Fill the below details related to the shift rate

NameRequiredTypeFormatDescriptionExample
external_idtruestring-User defined unique identifier for the shift rateregular_rate_1
nametruestring-A human readable string to identify the purpose of the shift rateRegular Shift rate 1
typetruestringENUMDefines the type of rate that must be used. Use any of the available shift rate typesPER_HOUR_FLAT_RATE
valuetruenumber-Value of the shift rate, to use in combination with the type parameter150

Retrieve the shift rate

You can also retrieve the shift rates starting from the external_id of the shift rate.

To retrieve a shift rate, make a GET request to the Retrieve a single time tracking shift rate endpoint.

curl --location --request GET 'https://api.letsdeel.com/rest/v2/time_tracking/shift_rates/{{external_id}}' \
--header 'Authorization: Bearer {{token}}' \
--header 'Content-Type: application/json'

Where:

NameRequiredTypeFormatDescriptionExample
external_idtruestring-User-defined unique identifier for the shift rateshift_123456

A successful response (200) returns the shift rate of the requested shift. For example:

{
  "data": {
    "organization_id": "string",
    "external_id": "string",
    "name": "string",
    "rate_type": "MULTIPLIER_PERCENTAGE",
    "value": 0,
    "created_at": "2022-05-24T09:38:46.235Z",
    "updated_at": "2022-05-24T09:38:46.235Z"
  }
}

Retrieve shift rates

You can also retrieve the list of shift rates for your organization.

To retrieve a list of shift rates, make a GET request to the List time tracking shift rates endpoint.

curl --location --request GET 'https://api.letsdeel.com/rest/v2/time_tracking/shift_rates?limit=10&offset=5' \
--header 'Authorization: Bearer {{token}}' \
--header 'Content-Type: application/json'
NameRequiredTypeFormatDescriptionExample
limitfalsenumberNumber of rows that must be returned in one API call100
offsetfalsenumberNumber of rows that must be skipped when returning the results10

A successful response (200) returns the list of shift rates available in your organization and matching any filters applied. For example:

{
  "data": [
    {
      "organization_id": "string",
      "external_id": "string",
      "name": "string",
      "rate_type": "MULTIPLIER_PERCENTAGE",
      "value": 0,
      "created_at": "2022-05-24T09:38:46.235Z",
      "updated_at": "2022-05-24T09:38:46.235Z"
    }
  ],
  "page": {
    "total_rows": 0,
    "items_per_page": 1,
    "offset": 999999999
  }
}

Where:

NameRequiredTypeFormatDescriptionExample
datatruearray-An array of shift rates[shift_rate_1, shift_rate_2, shift_rate_3]
organization_idtruenumber-The ID of your organization123456
external_idtruestring-User defined unique identifier for the shift rateregular_rate_1
nametruestring-A human readable string to identify the purpose of the shift rateRegular Shift rate 1
rate_typetruestringENUMDefines the type of rate that must be used. Use any of the available shift rate typesPER_HOUR_FLAT_RATE
valuetruenumber-Value of the shift rate, to use in combination with the type parameter150
created_attruestringdate-timeDate on which the shift rate is created2022-05-24T09:38:46.235Z
updated_attruestringdate-timeDate on which the shift rate is updated2022-05-24T09:38:46.235Z
pagetrueobject-An object containing pagination information. Use it to navigate through sets of-

Update a shift rate

You can also update a shift rate if it's not being used in any shift, by using the external_id of the shift rate.

To update a shift rate, make a PATCH request to the Update a time tracking shift rate endpoint.

📘

Only shift rates that are not used in any shift can be updated.

curl --location --request PATCH 'https://api.letsdeel.com/rest/v2/time_tracking/shift_rates/{{external_id}}' \
--header 'Authorization: Bearer {{token}}' \
--header 'Content-Type: application/json' \
--data '{
    "data": {
         "name": "On-call shift rate",
        "type": "PER_HOUR_FLAT_RATE",
        "value": 150
    }
}'

In the path:

NameRequiredTypeFormatDescriptionExample
external_idtruestring-User defined unique identifier for the shift rateregular_rate_1

In the body:

NameRequiredTypeFormatDescriptionExample
nametruestring-A human readable string to identify the purpose of the shift rateRegular Shift rate 1
typetruestringENUMDefines the type of rate that must be used. Use any of the available shift rate typesPER_HOUR_FLAT_RATE
valuetruenumber-Value of the shift rate, to use in combination with the type parameter150

A successful response (200) returns the updated shift rate. For example:

{
  "data": {
    "organization_id": "string",
    "external_id": "string",
    "name": "string",
    "rate_type": "MULTIPLIER_PERCENTAGE",
    "value": 0,
    "created_at": "2022-05-24T09:38:46.235Z",
    "updated_at": "2022-05-24T09:38:46.235Z"
  }
}

Delete a shift rate

You can also delete a shift rate if it's not being used in any shift, by using the external_id of the shift rate.

To delete a shift rate, make a DELETE request to the Delete a time tracking shift rate endpoint.

📘

Only shift rates that are not used in any shift can be deleted.

curl --location --request DELETE 'https://api.letsdeel.com/rest/v2/time_tracking/shift_rates/{{external_id}}' \
--header 'Authorization: Bearer {{token}}' \
--header 'Content-Type: application/json'

A successful response (204) returns an empty body.