> This page is for API, version 2026-01-01 (Stable) (default).
> For other versions, use one of these documentation indexes:
> - 2026-01-01 (Stable) (default): https://developer.deel.com/api/stable/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.deel.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.deel.com/_mcp/server.

# Time tracking

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

> **Note**
>
> The time tracking API only works for [Global Payroll](https://help.letsdeel.com/hc/en-gb/articles/9080297174033-Global-Payroll-Overview).
>
> Independent contractors use timesheets to track their time. For more information, see [Timesheets](/api/contractors/timesheets).

## Before you begin

Before managing shifts, note the following:

#### Retrieve your contract ID

Shifts are linked to contracts. Retrieve the contract ID from the [GET list of contracts](https://developer.deel.com/reference/listofcontracts) endpoint.

#### Create your shift rates first

Categorized shifts reference a shift rate by its `external_id`. [Create the shift rates](#manage-shift-rates) you need before you submit any categorized shifts.

#### Understand payroll cycles

Shifts are processed and compensated at the end of each payroll cycle. For more information, see [Shifts and payroll cycles](#shifts-and-payroll-cycles).

#### Late shift behavior

If a shift's payroll cycle reaches its cutoff date, the shift is not rejected by default; it automatically moves to the next payroll cycle and is compensated then. To detect this instead of relying on automatic rollover, see [Preventing late submissions with `payroll_cycle_ref.date`](#preventing-late-submissions-with-payroll_cycle_refdate).

#### Compensation calculation

How a shift is compensated depends on the type of the shift rate it references:

* `PER_HOUR_FLAT_RATE` and `PER_UNIT_FLAT_RATE`: the rate value is applied directly to `time_amount`. The worker's base salary is not used.
* `MULTIPLIER_PERCENTAGE`: the rate value is applied as a percentage of the worker's hourly base salary (or equivalent hourly rate, for non-hourly contracts).

See [Manage shift rates](#manage-shift-rates) for the formula used by each rate type.

#### Common errors

If a request fails, check [Common errors](#common-errors) before troubleshooting further. Several error messages, particularly those related to payroll cycles and corrections, are easy to misread.

### How the pieces fit together

At a high level, setting up and running time tracking for an employee follows this sequence:

#### Create shift rates

[Create one shift rate](#create-a-shift-rate) for each type of compensation, for example, a flat hourly rate, an overtime multiplier, or a per-unit rate for piecework. Each rate gets an `external_id` that you reference later.

#### Submit shifts

Submit [categorized shifts](#categorized-shifts) that reference a shift rate's `external_id`, or submit [uncategorized (raw) shifts](#uncategorized-raw-shifts) that only capture start time, end time, and breaks. Raw shifts do not reference a shift rate.

#### Shift is matched to a payroll cycle

The shift is associated with a payroll cycle based on `date_of_work`, or explicitly via [`payroll_cycle_ref.date`](#preventing-late-submissions-with-payroll_cycle_refdate) if you want to guard against late submissions.

#### Submission must land before cutoff

For shifts created through this API, the submission timestamp is what counts; there is no separate manager approval step. The shift must be submitted before the payroll cycle's cutoff date to be processed in that cycle. See [Shifts and payroll cycles](#shifts-and-payroll-cycles) for what happens if it lands after the cutoff.

#### Cycle is compensated

At the cutoff date, all shifts submitted for the cycle are compensated according to their shift rate.

#### Adjust with corrections, if needed

Once a shift has been processed for payroll, you can no longer update or delete it directly. Use [correction shifts](#correction-shifts) to adjust the payable amount without touching historical payroll data.

## Shift types

Shifts can be submitted in two formats:

#### [Categorized shifts](#categorized-shifts)

Shifts with known pay codes and rates defined in Deel.

#### [Uncategorized (raw) shifts](#uncategorized-raw-shifts)

Granular shifts capturing start time, end time, and break information.

### Categorized shifts

Categorized shifts are suited for when shifts are categorized outside Deel and the shift rates (pay codes/category) are known. To use this API, you must first [create shift rates](#create-a-shift-rate) in Deel and then reference those shift rates when submitting categorized shifts.

Following is an example of a categorized shift:

```json
{
  "external_id": "shift_123",
  "date_of_work": "2024-04-01",
  "summary": {
    "shift_rate_external_id": "rate123",
    "time_unit": "HOUR",
    "time_amount": 15.50
  }
}
```

### Uncategorized (raw) shifts

Uncategorized shifts are used to capture the shift information in a more granular way. Unlike categorized shifts, which capture summary information such as total hours worked, uncategorized shifts capture granular start time, end time, and break information. There is no need to set up a shift rate for uncategorized shifts.

```json
{
  "external_id": "shift_456",
  "date_of_work": "2024-04-01",
  "meta": {
    "start": {
      "date": "2024-04-01",
      "time": "09:00",
      "is_rest_day": false,
      "is_public_holiday": false
    },
    "end": {
      "date": "2024-04-01",
      "time": "17:00",
      "is_rest_day": false,
      "is_public_holiday": false
    },
    "breaks": [
      {
        "end": {
          "date": "2024-04-01",
          "time": "12:00"
        },
        "start": {
          "date": "2024-04-01",
          "time": "11:00"
        },
        "is_paid": true
      }
    ],
    "approval_date": "2024-04-03"
  }
}
```

### Shifts and payroll cycles

When you submit a shift, it's automatically associated with the relevant payroll cycle based on the submission timestamp and the cycle's cutoff date. For shifts created through this API, only the submission timestamp counts; there is no separate manager approval step. The cutoff date marks the last day you can submit a shift for processing within the current payroll cycle.

* If the shift is submitted before the cutoff date, it is processed within the current cycle
* If the shift is submitted after the cutoff date, it is processed in the next cycle

The payroll calendar is configured per entity by your Deel representative when you onboard. That is when you decide the cutoff dates. If you have questions about your cutoff dates, contact your Deel representative. For more information, see [Understanding the Deel Global Payroll Calendar](https://help.letsdeel.com/hc/en-gb/articles/34654118966801-Understanding-the-Deel-Global-Payroll-Calendar).

![Shifts cutoff date](/_fern-img/509c27e376d5f643d22b72640533887a6b2d7a95adea79a1489d4a298dce4298.webp)

> **Warning**
>
> Contact support for late shift submissions
>
> If the payroll cutoff date has already passed and waiting for the next cycle for the shift to be processed is not an option for you, you can contact support to process the shift in a special off-cycle payroll run. Off-cycle payroll runs have an additional fee that depends on your contract agreement.
>
> Make sure to provide the shift details via a CSV file through the support channel.

### Preventing late submissions with `payroll_cycle_ref.date`

You can include the optional `payroll_cycle_ref.date` parameter when submitting a shift to declare your intended payroll cycle. The system compares the request's timestamp against that cycle's cutoff date, so the shift is only accepted into the specified cycle if the submission is timely; otherwise, the request is rejected instead of silently rolling over.

The following table compares this to omitting the parameter:

| Approach                         | If the cycle's cutoff has already passed                                                                                            | Use when                                                                                                     |
| :------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------- |
| Omit `payroll_cycle_ref`         | The shift is silently moved to the next payroll cycle. The request still succeeds.                                                  | Automatic rollover is acceptable, and your integration does not need to be notified when it happens.         |
| Include `payroll_cycle_ref.date` | The request is rejected, so your integration can detect the miss immediately instead of finding out only when compensation is late. | You want to guarantee a shift lands in a specific cycle, or want to catch late submissions programmatically. |

> **Note**
>
> A shift that rolls over to the next payroll cycle is compensated with that cycle, not the one its `date_of_work` originally fell in. In practice, this means payment lands roughly one full payroll cycle later than expected.

The following example shows how to use the `payroll_cycle_ref.date` parameter:

> **Format of the payroll\_cycle\_ref**
>
> The `payroll_cycle_ref.date` follows the [ISO 8601 date and time format](https://en.wikipedia.org/wiki/ISO_8601). You can use any date within the cycle's start and end dates, but we recommend using the cycle's end date for clarity.

```json
{
  "data": {
    "contract_id": "123456",
    "shifts": [
      {
        "external_id": "shift_123456",
        "description": "This is a sample shift description.",
        "date_of_work": "2023-10-01",
        "payroll_cycle_ref": {
          "date": "2023-10-31T00:00:00.000Z"
        },
        "summary": {
          "shift_rate_external_id": "rate1234",
          "time_unit": "HOUR",
          "time_amount": 15.50
        }
      }
    ]
  }
}
```

## Manage shifts

This section covers how to add, update, and delete shifts for an employee.

### Add shifts

This section covers how to add shifts for an employee. There are different endpoints available for adding shifts based on the shift type:

#### [Add categorized shifts](#add-categorized-shifts)

Add shifts with known pay codes and rates.

#### [Add categorized shifts - Legacy](#add-categorized-shifts-legacy)

Legacy method (not recommended).

#### [Add uncategorized (raw) shifts](#add-uncategorized-raw-shifts)

Add granular shifts with detailed time information.

#### Add categorized shifts

You can add multiple categorized shifts for a single contract by providing an array of shifts. You can add a shift using any of the following time units: `HOUR` , `DAY` , `WEEK` , and `MONTH`.

#### Create a shift rate

Before submitting a categorized shift, [create a shift rate](#create-a-shift-rate) and note its `external_id`.

#### Submit the shift

Make a POST request to the [Create a time tracking shift](https://developer.deel.com/reference/createshifts) endpoint.

**`Python`**

```python Python
import requests
import os

url = "https://api.letsdeel.com/rest/time_tracking/shifts"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
    "Content-Type": "application/json"
}

payload = {
    "data": {
        "contract_id": "123456",
        "shifts": [
            {
                "external_id": "shift_123456",
                "description": "This is a sample shift description.",
                "date_of_work": "2023-10-01",
                "payroll_cycle_ref": {
                    "date": "2023-10-31T00:00:00.000Z"
                },
                "summary": {
                    "shift_rate_external_id": "rate1234",
                    "time_unit": "HOUR",
                    "time_amount": 15.50
                }
            }
        ]
    }
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const url = 'https://api.letsdeel.com/rest/time_tracking/shifts';

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
  'Content-Type': 'application/json'
};

const payload = {
  data: {
    contract_id: '123456',
    shifts: [
      {
        external_id: 'shift_123456',
        description: 'This is a sample shift description.',
        date_of_work: '2023-10-01',
        payroll_cycle_ref: {
          date: '2023-10-31T00:00:00.000Z'
        },
        summary: {
          shift_rate_external_id: 'rate1234',
          time_unit: 'HOUR',
          time_amount: 15.50
        }
      }
    ]
  }
};

axios.post(url, payload, { headers })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));
```

**`cURL`**

```bash cURL
curl --request POST 'https://api.letsdeel.com/rest/time_tracking/shifts' \
--header 'Authorization: Bearer $DEEL_API_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",
                "payroll_cycle_ref": {
                    "date": "2023-10-31T00:00:00.000Z"
                },
                "summary": {
                    "shift_rate_external_id": "rate1234",
                    "time_unit": "HOUR",
                    "time_amount": 15.50
                }
            }
        ]
    }
}'
```

In the body:

| Name                              | Required | Type   | Format               | Description                                                                                                                                             | Example                               |
| --------------------------------- | -------- | ------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| contract\_id                      | true     | string | -                    | Unique identifier of the contract for which shifts are being submitted                                                                                  | `123456`                              |
| description                       | true     | string | -                    | Description of shift. Use it to describe what kind of work is done during the shift.                                                                    | `This is a sample shift description.` |
| external\_id                      | true     | string | -                    | User-defined ID of the shift                                                                                                                            | `shift_123456`                        |
| date\_of\_work                    | true     | string | date                 | Date on which shift is performed. It is used to identify the payroll cycle of the shift                                                                 | `2023-10-01`                          |
| payroll\_cycle\_ref.date          | false    | string | date-time (ISO 8601) | Reference date of the payroll cycle in which shift should be processed (We recommend to send the payroll cycle end date as the payroll cycle reference) | `2023-10-31T00:00:00.000Z`            |
| summary                           | true     | object | -                    | 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\_id | true     | string | -                    | ID of the shift rate. Use it to link the shift to [a shift rate you created](#create-a-shift-rate).                                                     | `rate1234`                            |
| summary.time\_unit                | true     | string | -                    | Time unit for the shift. Possible values: `HOUR`, `DAY`, `WEEK`, `MONTH`.                                                                               | `HOUR`                                |
| summary.time\_amount              | false    | number | -                    | Length of the shift, expressed in the selected time unit                                                                                                | `15.50`                               |

#### Verify the response

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

```json
{
  "data": [
    {
      "external_id": "95c35493-41aa-44f8-9154-5a25cbbc1865",
      "organization_id": 0,
      "description": "string",
      "date_of_work": "2019-08-24T14:15:22Z",
      "contract_id": "string",
      "payroll_cycle_ref": {
        "date": "2023-10-31T00:00:00.000Z"
      },
      "summary": {
        "shift_rate_external_id": "rate1234",
        "time_unit": "HOUR",
        "time_amount": 15.50,
        "total_payable_hours": 15.50
      },
      "created_at": "2022-05-24T09:38:46.235Z",
      "updated_at": "2022-05-24T09:38:46.235Z"
    }
  ]
}
```

Where:

| Name                     | Required | Type   | Format               | Description                                                                                                           | Example                               |
| ------------------------ | -------- | ------ | -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| external\_id             | true     | string | -                    | User-defined ID of the shift                                                                                          | `shift_123456`                        |
| organization\_id         | true     | number | -                    | The ID of your organization                                                                                           | `123456`                              |
| description              | true     | string | -                    | Description of shift                                                                                                  | `This is a sample shift description.` |
| date\_of\_work           | true     | string | date-time            | Date of the shift                                                                                                     | `2019-08-24T14:15:22Z`                |
| payroll\_cycle\_ref.date | false    | string | date-time (ISO 8601) | Reference date of the payroll cycle in which shift will be processed                                                  | `2023-10-31T00:00:00.000Z`            |
| contract\_id             | true     | string | -                    | Unique identifier of the contract that shifts were submitted for                                                      | `123456`                              |
| summary                  | true     | object | -                    | Object containing numerical data about the shift. This data is used to calculate the amount to be paid for the shift. | -                                     |
| created\_at              | true     | string | date-time            | Date on which the shift is created                                                                                    | `2022-05-24T09:38:46.235Z`            |
| updated\_at              | true     | string | date-time            | Date on which the shift is updated                                                                                    | `2022-05-24T09:38:46.235Z`            |

> **Note**
>
> The `total_payable_hours` is set by default by the API when `time_unit` is `HOUR`.

#### Add categorized shifts (Legacy)

This method is still supported, but it is recommended to use [Add categorized shifts](https://developer.deel.com/docs/time-tracking#add-categorized-shifts) instead.

This shift type adds a shift without requiring `time_amount` and `time_unit` in the request body. By default, `time_amount` is set to `summary.total_payable_hours` and `time_unit` is set to `HOUR`. You can perform the same operations on these shifts as you would with [categorized shifts](https://developer.deel.com/docs/time-tracking#add-categorized-shifts) by using the same payload.

#### Create a shift rate

Before submitting a categorized shift, [create a shift rate](#create-a-shift-rate) and note its `external_id`.

#### Submit the shift

Make a POST request to the [Create a time tracking shift](https://developer.deel.com/reference/createshifts) endpoint.

**`Python`**

```python Python
import requests
import os

url = "https://api.letsdeel.com/rest/time_tracking/shifts"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
    "Content-Type": "application/json"
}

payload = {
    "data": {
        "contract_id": "123456",
        "shifts": [
            {
                "external_id": "shift_123456",
                "description": "This is a sample shift description.",
                "date_of_work": "2023-10-01",
                "payroll_cycle_ref": {
                    "date": "2023-10-31T00:00:00.000Z"
                },
                "summary": {
                    "shift_rate_external_id": "rate1234",
                    "shift_duration_hours": 8,
                    "total_break_hours": 1,
                    "payable_break_hours": 0.5,
                    "total_payable_hours": 7.5
                }
            }
        ]
    }
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const url = 'https://api.letsdeel.com/rest/time_tracking/shifts';

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
  'Content-Type': 'application/json'
};

const payload = {
  data: {
    contract_id: '123456',
    shifts: [
      {
        external_id: 'shift_123456',
        description: 'This is a sample shift description.',
        date_of_work: '2023-10-01',
        payroll_cycle_ref: {
          date: '2023-10-31T00:00:00.000Z'
        },
        summary: {
          shift_rate_external_id: 'rate1234',
          shift_duration_hours: 8,
          total_break_hours: 1,
          payable_break_hours: 0.5,
          total_payable_hours: 7.5
        }
      }
    ]
  }
};

axios.post(url, payload, { headers })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));
```

**`cURL`**

```bash cURL
curl --request POST 'https://api.letsdeel.com/rest/time_tracking/shifts' \
--header 'Authorization: Bearer $DEEL_API_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",
                "payroll_cycle_ref": {
                    "date": "2023-10-31T00:00:00.000Z"
                },
                "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:

| Name                              | Required | Type   | Format               | Description                                                                                                                                             | Example                               |
| --------------------------------- | -------- | ------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| contract\_id                      | true     | string | -                    | Unique identifier of the contract for which shifts are being submitted                                                                                  | `123456`                              |
| description                       | true     | string | -                    | Description of shift. Use it to describe what kind of work is done during the shift.                                                                    | `This is a sample shift description.` |
| external\_id                      | true     | string | -                    | User-defined ID of the shift                                                                                                                            | `shift_123456`                        |
| date\_of\_work                    | true     | string | date                 | Date on which shift is performed. It is used to identify the payroll cycle of the shift                                                                 | `2023-10-01`                          |
| payroll\_cycle\_ref.date          | false    | string | date-time (ISO 8601) | Reference date of the payroll cycle in which shift should be processed (We recommend to send the payroll cycle end date as the payroll cycle reference) | `2023-10-31T00:00:00.000Z`            |
| summary                           | true     | object | -                    | 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\_id | true     | string | -                    | ID of the shift rate. Use it to link the shift to [a shift rate you created](#create-a-shift-rate).                                                     | `rate1234`                            |
| summary.shift\_duration\_hours    | false    | number | -                    | Total time of the shift in hours                                                                                                                        | `8`                                   |
| summary.total\_break\_hours       | false    | number | -                    | Total break time in hours                                                                                                                               | `1`                                   |
| summary.payable\_break\_hours     | false    | number | -                    | Total breaks hours that must be paid                                                                                                                    | `0.5`                                 |
| summary.total\_payable\_hours     | true     | number | -                    | Total hours that need to be paid using the shift rate provided above                                                                                    | `7.5`                                 |

#### Verify the response

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

```json
{
  "data": [
    {
      "external_id": "95c35493-41aa-44f8-9154-5a25cbbc1865",
      "organization_id": 0,
      "description": "string",
      "date_of_work": "2019-08-24T14:15:22Z",
      "contract_id": "string",
      "payroll_cycle_ref": {
        "date": "2023-10-31T00:00:00.000Z"
      },
      "summary": {
        "shift_rate_external_id": "rate1234",
        "time_unit": "HOUR",
        "time_amount": 7.5,
        "shift_duration_hours": 8,
        "total_break_hours": 1,
        "payable_break_hours": 0.5,
        "total_payable_hours": 7.5
      },
      "created_at": "2022-05-24T09:38:46.235Z",
      "updated_at": "2022-05-24T09:38:46.235Z"
    }
  ]
}
```

Where:

| Name                     | Required | Type   | Format               | Description                                                                                                           | Example                               |
| ------------------------ | -------- | ------ | -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| external\_id             | true     | string | -                    | User-defined ID of the shift                                                                                          | `shift_123456`                        |
| organization\_id         | true     | number | -                    | The ID of your organization                                                                                           | `123456`                              |
| description              | true     | string | -                    | Description of shift                                                                                                  | `This is a sample shift description.` |
| date\_of\_work           | true     | string | date-time            | Date of the shift                                                                                                     | `2019-08-24T14:15:22Z`                |
| payroll\_cycle\_ref.date | false    | string | date-time (ISO 8601) | Reference date of the payroll cycle in which shift will be processed                                                  | `2023-10-31T00:00:00.000Z`            |
| contract\_id             | true     | string | -                    | Unique identifier of the contract that shifts were submitted for                                                      | `123456`                              |
| summary                  | true     | object | -                    | Object containing numerical data about the shift. This data is used to calculate the amount to be paid for the shift. | -                                     |
| created\_at              | true     | string | date-time            | Date on which the shift is created                                                                                    | `2022-05-24T09:38:46.235Z`            |
| updated\_at              | true     | string | date-time            | Date on which the shift is updated                                                                                    | `2022-05-24T09:38:46.235Z`            |

#### Add uncategorized (raw) shifts

You can add multiple uncategorized shifts for a single contract by providing an array of shifts.

#### Submit the shift

Make a POST request to the [Create uncategorized (raw) shift](https://developer.deel.com/reference/createrawshifts) endpoint.

**`Python`**

```python Python
import requests
import os

url = "https://api.letsdeel.com/rest/time_tracking/shifts/raw"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
    "Content-Type": "application/json"
}

payload = {
    "data": {
        "contract_id": "123456",
        "shifts": [
            {
                "external_id": "shift_123456",
                "description": "This is a sample shift description.",
                "date_of_work": "2024-04-01",
                "meta": {
                    "start": {
                        "date": "2024-04-01",
                        "time": "09:00",
                        "is_rest_day": False,
                        "is_public_holiday": False
                    },
                    "end": {
                        "date": "2024-04-01",
                        "time": "17:00",
                        "is_rest_day": False,
                        "is_public_holiday": False
                    },
                    "breaks": [
                        {
                            "end": {
                                "date": "2024-04-01",
                                "time": "12:00"
                            },
                            "start": {
                                "date": "2024-04-01",
                                "time": "11:00"
                            },
                            "is_paid": True
                        }
                    ],
                    "approval_date": "2024-04-03"
                }
            }
        ]
    }
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const url = 'https://api.letsdeel.com/rest/time_tracking/shifts/raw';

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
  'Content-Type': 'application/json'
};

const payload = {
  data: {
    contract_id: '123456',
    shifts: [
      {
        external_id: 'shift_123456',
        description: 'This is a sample shift description.',
        date_of_work: '2024-04-01',
        meta: {
          start: {
            date: '2024-04-01',
            time: '09:00',
            is_rest_day: false,
            is_public_holiday: false
          },
          end: {
            date: '2024-04-01',
            time: '17:00',
            is_rest_day: false,
            is_public_holiday: false
          },
          breaks: [
            {
              end: {
                date: '2024-04-01',
                time: '12:00'
              },
              start: {
                date: '2024-04-01',
                time: '11:00'
              },
              is_paid: true
            }
          ],
          approval_date: '2024-04-03'
        }
      }
    ]
  }
};

axios.post(url, payload, { headers })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));
```

**`cURL`**

```bash cURL
curl --request POST 'https://api.letsdeel.com/rest/time_tracking/shifts/raw' \
--header 'Authorization: Bearer $DEEL_API_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": "2024-04-01",
                "meta": {
                  "start": {
                    "date": "2024-04-01",
                    "time": "09:00",
                    "is_rest_day": false,
                    "is_public_holiday": false
                  },
                  "end": {
                    "date": "2024-04-01",
                    "time": "17:00",
                    "is_rest_day": false,
                    "is_public_holiday": false
                  },
                  "breaks": [
                    {
                      "end": {
                        "date": "2024-04-01",
                        "time": "12:00"
                      },
                      "start": {
                        "date": "2024-04-01",
                        "time": "11:00"
                      },
                      "is_paid": true
                    }
                  ],
                  "approval_date": "2024-04-03"
                }
            }
        ]
    }
}'
```

In the body:

| Name                           | Required | Type    | Format       | Description                                                                             | Example                               |
| ------------------------------ | -------- | ------- | ------------ | --------------------------------------------------------------------------------------- | ------------------------------------- |
| contract\_id                   | true     | string  | -            | Unique identifier of the contract for which shifts are being submitted                  | `123456`                              |
| external\_id                   | true     | string  | -            | User-defined ID of the shift                                                            | `shift_123456`                        |
| description                    | true     | string  | -            | Description of shift. Use it to describe what kind of work is done during the shift.    | `This is a sample shift description.` |
| date\_of\_work                 | true     | string  | date         | Date on which shift is performed. It is used to identify the payroll cycle of the shift | `2024-04-01`                          |
| meta                           | true     | object  | -            | Object containing detailed start/end times, breaks, and approval metadata               | -                                     |
| meta.start.date                | true     | string  | date         | Date when the shift starts                                                              | `2024-04-01`                          |
| meta.start.time                | true     | string  | time (HH:mm) | Start time of the shift                                                                 | `09:00`                               |
| meta.start.is\_rest\_day       | true     | boolean | -            | Indicates if the shift start is on a rest day                                           | `false`                               |
| meta.start.is\_public\_holiday | true     | boolean | -            | Indicates if the shift start is on a public holiday                                     | `false`                               |
| meta.end.date                  | true     | string  | date         | Date when the shift ends                                                                | `2024-04-01`                          |
| meta.end.time                  | true     | string  | time (HH:mm) | End time of the shift                                                                   | `17:00`                               |
| meta.end.is\_rest\_day         | true     | boolean | -            | Indicates if the shift end is on a rest day                                             | `false`                               |
| meta.end.is\_public\_holiday   | true     | boolean | -            | Indicates if the shift end is on a public holiday                                       | `false`                               |
| meta.breaks                    | false    | array   | -            | List of breaks taken during the shift                                                   | -                                     |
| meta.breaks\[].start.date      | true     | string  | date         | Break start date                                                                        | `2024-04-01`                          |
| meta.breaks\[].start.time      | true     | string  | time (HH:mm) | Break start time                                                                        | `11:00`                               |
| meta.breaks\[].end.date        | true     | string  | date         | Break end date                                                                          | `2024-04-01`                          |
| meta.breaks\[].end.time        | true     | string  | time (HH:mm) | Break end time                                                                          | `12:00`                               |
| meta.breaks\[].is\_paid        | false    | boolean | -            | Indicates whether the break is paid                                                     | `true`                                |
| meta.approval\_date            | false    | string  | date         | Date when the shift was approved by a manager                                           | `2024-04-03`                          |

#### Verify the response

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

```json
{
  "data": [
    {
      "external_id": "shift_example05",
      "description": "This is a sample shift description 5",
      "date_of_work": "2025-06-01",
      "created_at": "2025-06-30T19:31:54.402Z",
      "updated_at": "2025-06-30T19:31:54.402Z",
      "contract_id": "mjgd99e",
      "meta": {
        "start": {
          "date": "2024-02-12",
          "time": "08:00",
          "is_rest_day": false,
          "is_public_holiday": false
        },
        "end": {
          "date": "2024-02-12",
          "time": "16:00",
          "is_rest_day": false,
          "is_public_holiday": false
        },
        "approval_date": "2024-12-11"
      }
    }
  ]
}
```

Where:

| Name                           | Required | Type    | Format       | Description                                                      | Example                                |
| ------------------------------ | -------- | ------- | ------------ | ---------------------------------------------------------------- | -------------------------------------- |
| external\_id                   | true     | string  | -            | User-defined ID of the shift                                     | `shift_example05`                      |
| description                    | true     | string  | -            | Description of shift                                             | `This is a sample shift description 5` |
| date\_of\_work                 | true     | string  | date         | Date of the shift                                                | `2025-06-01`                           |
| contract\_id                   | true     | string  | -            | Unique identifier of the contract that shifts were submitted for | `mjgd99e`                              |
| created\_at                    | true     | string  | date-time    | Date on which the shift is created                               | `2025-06-30T19:31:54.402Z`             |
| updated\_at                    | true     | string  | date-time    | Date on which the shift is updated                               | `2025-06-30T19:31:54.402Z`             |
| meta                           | true     | object  | -            | Object containing detailed start/end times and approval date     | -                                      |
| meta.start.date                | true     | string  | date         | Date when the shift starts                                       | `2024-02-12`                           |
| meta.start.time                | true     | string  | time (HH:mm) | Start time of the shift                                          | `08:00`                                |
| meta.start.is\_rest\_day       | true     | boolean | -            | Indicates if the shift start is on a rest day                    | `false`                                |
| meta.start.is\_public\_holiday | true     | boolean | -            | Indicates if the shift start is on a public holiday              | `false`                                |
| meta.end.date                  | true     | string  | date         | Date when the shift ends                                         | `2024-02-12`                           |
| meta.end.time                  | true     | string  | time (HH:mm) | End time of the shift                                            | `16:00`                                |
| meta.end.is\_rest\_day         | true     | boolean | -            | Indicates if the shift end is on a rest day                      | `false`                                |
| meta.end.is\_public\_holiday   | true     | boolean | -            | Indicates if the shift end is on a public holiday                | `false`                                |
| meta.approval\_date            | false    | string  | date         | Date when the shift was approved by a manager                    | `2024-12-11`                           |

### List shifts in your organization

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

#### Make the request

Make a GET request to the [List time tracking shifts](https://developer.deel.com/reference/listofshifts) endpoint.

**`Python`**

```python Python
import requests
import os

url = "https://api.letsdeel.com/rest/time_tracking/shifts"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}"
}

params = {
    "limit": 10,
    "offset": 20,
    "contract_id[]": ["abcd", "abcd2"],
    "from_date": "2023-10-01",
    "to_date": "2023-10-02"
}

response = requests.get(url, headers=headers, params=params)
print(response.json())
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const url = 'https://api.letsdeel.com/rest/time_tracking/shifts';

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`
};

const params = {
  limit: 10,
  offset: 20,
  'contract_id[]': ['abcd', 'abcd2'],
  from_date: '2023-10-01',
  to_date: '2023-10-02'
};

axios.get(url, { headers, params })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));
```

**`cURL`**

```bash cURL
curl --request GET 'https://api.letsdeel.com/rest/time_tracking/shifts?limit=10&offset=20&contract_id[]=abcd&contract_id[]=abcd2&from_date=2023-10-01&to_date=2023-10-02' \
--header 'Authorization: Bearer $DEEL_API_TOKEN'
```

In the query:

| Name            | Required | Type      | Format      | Description                                                    | Example                                    |
| --------------- | -------- | --------- | ----------- | -------------------------------------------------------------- | ------------------------------------------ |
| limit           | false    | number    |             | Number of rows that must be returned in one API call           | 100                                        |
| offset          | false    | number    |             | Number of rows that must be skipped when returning the results | 10                                         |
| contract\_id\[] | false    | string\[] | array param | Filter shifts by one or more contract IDs                      | contract\_id\[]=abcd\&contract\_id\[]=efgh |
| from\_date      | false    | string    | YYYY-MM-DD  | Filter shifts from this date (inclusive)                       | 2023-10-01                                 |
| to\_date        | false    | string    | YYYY-MM-DD  | Filter shifts until this date (inclusive)                      | 2023-10-02                                 |

> **Note**
>
> Use the array syntax `contract_id[]=value` to filter for contract IDs. For multiple contract IDs, you can use `contract_id[]=value1&contract_id[]=value2`.

#### Review the response

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

```json
{
  "data": [
    {
      "external_id": "d3m0d3m0-d3m0-d3m0-d3m0-d3m0d3m0d3m0",
      "organization_id": 0,
      "description": "string",
      "date_of_work": "2019-08-24T14:15:22Z",
      "contract_id": "string",
      "summary": {
        "shift_rate_external_id": "rate1234",
        "time_unit": "HOUR",
        "time_amount": 15.50,
        "total_payable_hours": 15.50
      },
      "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:

| Name | Required | Type   | Format | Description                                                                | Example |
| ---- | -------- | ------ | ------ | -------------------------------------------------------------------------- | ------- |
| data | true     | array  | -      | The list of shifts available                                               | -       |
| page | true     | object | -      | 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 using the `external_id` of the shift.

#### Make the request

Make a GET request to the [Retrieve a single time tracking shift](https://developer.deel.com/reference/gettimetrackingshiftbyexternalid) endpoint.

**`Python`**

```python Python
import requests
import os

external_id = "shift_123456"
url = f"https://api.letsdeel.com/rest/time_tracking/shifts/{external_id}"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}"
}

response = requests.get(url, headers=headers)
print(response.json())
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const externalId = 'shift_123456';
const url = `https://api.letsdeel.com/rest/time_tracking/shifts/${externalId}`;

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`
};

axios.get(url, { headers })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));
```

**`cURL`**

```bash cURL
curl --request GET 'https://api.letsdeel.com/rest/time_tracking/shifts/{{external_id}}' \
--header 'Authorization: Bearer $DEEL_API_TOKEN'
```

#### Review the response

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

```json
{
  "external_id": "d3m0d3m0-d3m0-d3m0-d3m0-d3m0d3m0d3m0",
  "organization_id": 0,
  "description": "string",
  "date_of_work": "2019-08-24T14:15:22Z",
  "contract_id": "string",
  "summary": {
    "shift_rate_external_id": "rate1234",
    "time_unit": "HOUR",
    "time_amount": 15.50,
    "total_payable_hours": 15.50
  },
  "created_at": "2022-05-24T09:38:46.235Z",
  "updated_at": "2022-05-24T09:38:46.235Z"
}
```

### Update shifts

You can update a shift before it is processed for payroll. After a shift is processed, your ability to amend it depends on the shift type:

* You can amend a categorized shift using [correction shifts](#correction-shifts)
* You cannot amend uncategorized (raw) shifts

This section explains how to update shifts that have not been processed for payroll:

* [Update categorized shift for an employee](#update-categorized-shift-for-an-employee)
* [Update uncategorized (raw) shift for an employee](#update-uncategorized-raw-shift-for-an-employee)

### Update categorized shift for an employee

You can update the information of a categorized shift.

> **Note**
>
> You can only update shifts that have not been processed for payroll. Shifts are processed for payroll at the cutoff date.

#### Make the request

Make a PATCH request to the [Update a time tracking shift](https://developer.deel.com/reference/updateashift) endpoint.

**`Python`**

```python Python
import requests
import os

external_id = "shift_123456"
url = f"https://api.letsdeel.com/rest/time_tracking/shifts/{external_id}"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
    "Content-Type": "application/json"
}

payload = {
    "data": {
        "description": "This is a sample shift description.",
        "date_of_work": "2023-10-01",
        "payroll_cycle_ref": {
            "date": "2023-10-31T00:00:00.000Z"
        },
        "summary": {
            "time_amount": 15.50
        }
    }
}

response = requests.patch(url, json=payload, headers=headers)
print(response.json())
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const externalId = 'shift_123456';
const url = `https://api.letsdeel.com/rest/time_tracking/shifts/${externalId}`;

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
  'Content-Type': 'application/json'
};

const payload = {
  data: {
    description: 'This is a sample shift description.',
    date_of_work: '2023-10-01',
    payroll_cycle_ref: {
      date: '2023-10-31T00:00:00.000Z'
    },
    summary: {
      time_amount: 15.50
    }
  }
};

axios.patch(url, payload, { headers })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));
```

**`cURL`**

```bash cURL
curl --request PATCH 'https://api.letsdeel.com/rest/time_tracking/shifts/{{external_id}}' \
--header 'Authorization: Bearer $DEEL_API_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
    "data": {
        "description": "This is a sample shift description.",
        "date_of_work": "2023-10-01",
        "payroll_cycle_ref": {
          "date": "2023-10-31T00:00:00.000Z"
        },
        "summary": {
          "time_amount": 15.50
        }
    }
}'
```

In the path:

| Name         | Required | Type   | Format | Description                  | Example        |
| ------------ | -------- | ------ | ------ | ---------------------------- | -------------- |
| external\_id | true     | string | -      | User-defined ID of the shift | `shift_123456` |

In the body:

| Name | Required | Type   | Format | Description                                                 | Example |
| ---- | -------- | ------ | ------ | ----------------------------------------------------------- | ------- |
| data | true     | object | -      | Contains the information of the shift that must be updated. | -       |

#### Review the response

A successful response (`200`) returns the updated shift.

```json
{
  "external_id": "95c35493-41aa-44f8-9154-5a25cbbc1865",
  "organization_id": 0,
  "description": "string",
  "date_of_work": "2019-08-24T14:15:22Z",
  "contract_id": "string",
  "payroll_cycle_ref": {
    "date": "2023-10-31T00:00:00.000Z"
  },
  "summary": {
    "shift_rate_external_id": "rate1234",
    "time_unit": "HOUR",
    "time_amount": 15.50,
    "total_payable_hours": 15.50
  },
  "created_at": "2022-05-24T09:38:46.235Z",
  "updated_at": "2022-05-24T09:38:46.235Z"
}
```

### Update uncategorized (raw) shift for an employee

You can update the information of an uncategorized shift.

> **Note**
>
> You can only update shifts that have not been processed for payroll. Shifts are processed for payroll at the cutoff date.

#### Make the request

Make a PATCH request to the [Update a raw time tracking shift](https://developer.deel.com/reference/updaterawshift) endpoint.

**`Python`**

```python Python
import requests
import os

external_id = "shift_123456"
url = f"https://api.letsdeel.com/rest/time_tracking/shifts/raw/{external_id}"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
    "Content-Type": "application/json"
}

payload = {
    "data": {
        "description": "This is a sample shift updated now again.",
        "date_of_work": "2023-10-01",
        "meta": {
            "start": {
                "date": "2024-02-12",
                "time": "08:00",
                "is_rest_day": False,
                "is_public_holiday": False
            },
            "end": {
                "date": "2024-02-12",
                "time": "16:00",
                "is_rest_day": False,
                "is_public_holiday": False
            },
            "approval_date": "2024-12-11"
        }
    }
}

response = requests.patch(url, json=payload, headers=headers)
print(response.json())
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const externalId = 'shift_123456';
const url = `https://api.letsdeel.com/rest/time_tracking/shifts/raw/${externalId}`;

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
  'Content-Type': 'application/json'
};

const payload = {
  data: {
    description: 'This is a sample shift updated now again.',
    date_of_work: '2023-10-01',
    meta: {
      start: {
        date: '2024-02-12',
        time: '08:00',
        is_rest_day: false,
        is_public_holiday: false
      },
      end: {
        date: '2024-02-12',
        time: '16:00',
        is_rest_day: false,
        is_public_holiday: false
      },
      approval_date: '2024-12-11'
    }
  }
};

axios.patch(url, payload, { headers })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));
```

**`cURL`**

```bash cURL
curl --request PATCH 'https://api.letsdeel.com/rest/time_tracking/shifts/raw/{{external_id}}' \
--header 'Authorization: Bearer $DEEL_API_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
    "data": {
        "description": "This is a sample shift updated now again.",
        "date_of_work": "2023-10-01",
        "meta": {
            "start": {
                "date": "2024-02-12",
                "time": "08:00",
                "is_rest_day": false,
                "is_public_holiday": false
            },
            "end": {
                "date": "2024-02-12",
                "time": "16:00",
                "is_rest_day": false,
                "is_public_holiday": false
            },
            "approval_date": "2024-12-11"
        }
    }
}'
```

In the path:

| Name         | Required | Type   | Format | Description                  | Example        |
| ------------ | -------- | ------ | ------ | ---------------------------- | -------------- |
| external\_id | true     | string | -      | User-defined ID of the shift | `shift_123456` |

In the body:

| Name | Required | Type   | Format | Description                                                 | Example |
| ---- | -------- | ------ | ------ | ----------------------------------------------------------- | ------- |
| data | true     | object | -      | Contains the information of the shift that must be updated. | -       |

#### Review the response

A successful response (`200`) returns the updated shift.

```json
{
  "external_id": "95c35493-41aa-44f8-9154-5a25cbbc1865",
  "description": "string",
  "date_of_work": "2019-08-24T14:15:22Z",
  "contract_id": "string",
  "payroll_cycle_ref": {
    "date": "2023-10-31T00:00:00.000Z"
  },
  "meta": {
    "start": {
      "date": "2024-02-12",
      "time": "08:00",
      "is_rest_day": false,
      "is_public_holiday": false
    },
    "end": {
      "date": "2024-02-12",
      "time": "16:00",
      "is_rest_day": false,
      "is_public_holiday": false
    },
    "approval_date": "2024-12-11"
  },
  "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.

> **Note**
>
> You can only delete shifts that have not been processed for payroll. Shifts are processed for payroll at the cutoff date.

#### Make the request

Make a DELETE request to the [Delete a time tracking shift](https://developer.deel.com/reference/deletetimetrackingshift) endpoint.

**`Python`**

```python Python
import requests
import os

external_id = "shift_123456"
url = f"https://api.letsdeel.com/rest/time_tracking/shifts/{external_id}"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}"
}

response = requests.delete(url, headers=headers)
print(response.status_code)
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const externalId = 'shift_123456';
const url = `https://api.letsdeel.com/rest/time_tracking/shifts/${externalId}`;

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`
};

axios.delete(url, { headers })
  .then(response => console.log(response.status))
  .catch(error => console.error(error));
```

**`cURL`**

```bash cURL
curl --request DELETE 'https://api.letsdeel.com/rest/time_tracking/shifts/{{external_id}}' \
--header 'Authorization: Bearer $DEEL_API_TOKEN'
```

Where:

| Name         | Required | Type   | Format | Description                  | Example        |
| ------------ | -------- | ------ | ------ | ---------------------------- | -------------- |
| external\_id | true     | string | -      | User-defined ID of the shift | `shift_123456` |

#### Verify the response

A successful response (`204`) returns an empty body.

## Correction shifts

When you need to adjust hours for shifts that have already been processed for payroll, you can use correction shifts. Corrections create a new shift entry that adjusts the payable hours of the original shift. The correction will be processed in the next payroll cycle, ensuring accurate compensation without modifying historical payroll data.

The sequence below covers when a correction is accepted or rejected. See [Common errors](#common-errors) for the exact rejection messages.

```mermaid
sequenceDiagram
    participant Client
    participant Deel API

    Client->>Deel API: POST /shifts (create shift)
    Deel API-->>Client: Shift created, editable

    Client->>Deel API: PATCH /shifts/{external_id}
    Deel API-->>Client: Shift updated

    Note over Deel API: Payroll cycle cutoff passes — shift is now processed for payroll

    Client->>Deel API: PATCH /shifts/{external_id}
    Deel API-->>Client: 403 Payroll cycle already closed

    Client->>Deel API: POST /shifts (correction, shift_type: CORRECTION_DELTA)
    Deel API-->>Client: Correction applied

    Note over Client,Deel API: A correction in the opposite direction, or one that pushes the total below zero, is rejected — see Common errors
```

Two additional rules to keep in mind:

* Correction shifts cannot be submitted for another correction shift.
* Correction shifts cannot be updated but can be deleted.

To submit a correction, use the same [Create a time tracking shift](https://developer.deel.com/reference/createshifts) endpoint, by specifying `CORRECTION_DELTA` as the `shift_type` and including a `corrections` array with additional fields to specify the correction details.

#### Identify the shift to correct

For example, you may have previously created a shift with 10 total payable hours and this shift has been already processed for payroll. If you later discover that the actual hours worked were 7.5, which requires a reduction of 2.5 hours, note the original shift's `external_id`.

#### Submit the correction

Make a POST request to the [Create a time tracking shift](https://developer.deel.com/reference/createshifts) endpoint with the correction details.

**`Python`**

```python Python
import requests
import os

url = "https://api.letsdeel.com/rest/time_tracking/shifts"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
    "Content-Type": "application/json"
}

payload = {
    "data": {
        "contract_id": "3j5z2e6",
        "shifts": [
            {
                "external_id": "shift_example47",
                "description": "Correction shift",
                "shift_type": "CORRECTION_DELTA",
                "shift_reference_id": "shift_example45",
                "corrections": [
                    {
                        "type": "SUBTRACTION",
                        "time_amount": 2.5
                    }
                ]
            }
        ]
    }
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const url = 'https://api.letsdeel.com/rest/time_tracking/shifts';

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
  'Content-Type': 'application/json'
};

const payload = {
  data: {
    contract_id: '3j5z2e6',
    shifts: [
      {
        external_id: 'shift_example47',
        description: 'Correction shift',
        shift_type: 'CORRECTION_DELTA',
        shift_reference_id: 'shift_example45',
        corrections: [
          {
            type: 'SUBTRACTION',
            time_amount: 2.5
          }
        ]
      }
    ]
  }
};

axios.post(url, payload, { headers })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));
```

**`cURL`**

```bash cURL
curl --request POST 'https://api.letsdeel.com/rest/time_tracking/shifts' \
--header 'Authorization: Bearer $DEEL_API_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
    "data": {
        "contract_id": "3j5z2e6",
        "shifts": [
            {
                "external_id": "shift_example47",
                "description": "Correction shift",
                "shift_type": "CORRECTION_DELTA",
                "shift_reference_id": "shift_example45",
                "corrections": [
                    {
                        "type": "SUBTRACTION",
                        "time_amount": 2.5
                    }
                ]
            }
        ]
    }
}'
```

In the body:

| Name                        | Required | Type   | Format | Description                                                                      | Example            |
| --------------------------- | -------- | ------ | ------ | -------------------------------------------------------------------------------- | ------------------ |
| contract\_id                | true     | string | -      | Unique identifier of the contract for which the correction is being submitted    | `3j5z2e6`          |
| external\_id                | true     | string | -      | User-defined ID for the correction shift                                         | `shift_example47`  |
| description                 | true     | string | -      | Description of the correction. Use it to describe the reason for the correction. | `Correction shift` |
| shift\_type                 | true     | string | -      | Type of shift being submitted. Use `CORRECTION_DELTA` for corrections            | `CORRECTION_DELTA` |
| shift\_reference\_id        | true     | string | -      | External ID of the original shift that is being corrected                        | `shift_example45`  |
| corrections                 | true     | array  | -      | Array containing correction details                                              | -                  |
| corrections\[].type         | true     | string | -      | Type of correction. Use `SUBTRACTION` to reduce hours or `ADDITION` to add hours | `SUBTRACTION`      |
| corrections\[].time\_amount | true     | number | -      | Amount of time to add or subtract from the original shift                        | `2.5`              |

#### Verify the response

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

```json
{
  "data": [
    {
      "external_id": "shift_example47",
      "description": "Correction shift",
      "date_of_work": "2019-08-24T14:15:22Z",
      "contract_id": "3j5z2e6",
      "summary": {
        "time_amount": -2.5,
        "total_payable_hours": -2.5
      },
      "shift_type": "CORRECTION_DELTA",
      "shift_reference_id": "shift_example45",
      "created_at": "2022-05-24T09:38:46.235Z",
      "updated_at": "2022-05-24T09:38:46.235Z"
    }
  ]
}
```

Where:

| Name                 | Required | Type   | Format    | Description                                                                                                                     | Example                    |
| -------------------- | -------- | ------ | --------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| external\_id         | true     | string | -         | User-defined ID of the correction shift                                                                                         | `shift_example47`          |
| description          | true     | string | -         | Description of the correction                                                                                                   | `Correction shift`         |
| date\_of\_work       | true     | string | date-time | Date of the original shift being corrected                                                                                      | `2019-08-24T14:15:22Z`     |
| contract\_id         | true     | string | -         | Unique identifier of the contract                                                                                               | `3j5z2e6`                  |
| summary              | true     | object | -         | Object containing the delta values for the correction. Negative values indicate reductions, positive values indicate additions. | -                          |
| summary.time\_amount | true     | number | -         | Adjustment amount, negative for reduction and positive for increase                                                             | `-2.5`                     |
| shift\_type          | true     | string | -         | Type of shift, will be `CORRECTION_DELTA` for corrections                                                                       | `CORRECTION_DELTA`         |
| shift\_reference\_id | true     | string | -         | External ID of the original shift being corrected                                                                               | `shift_example45`          |
| created\_at          | true     | string | date-time | Date on which the correction is created                                                                                         | `2022-05-24T09:38:46.235Z` |
| updated\_at          | true     | string | date-time | Date on which the correction is updated                                                                                         | `2022-05-24T09:38:46.235Z` |

## Manage shift rates

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

| Name                    | Description                                                                                                                                                                                           | Formula                                                                                        | Example                                                                                                                                                                                                                                           |
| :---------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `MULTIPLIER_PERCENTAGE` | Defines the rate of a shift as a percentage of the salary, using the employee's hourly salary (if it's an hourly contract) or equivalent hourly salary (for non-hourly contracts).                    | `Total amount for shift = (MULTIPLIER_PERCENTAGE/100) * Per_hour_salary * Total_payable_hours` | 10$/hour is the base salary; the user submitted a shift with a total of 5 payable hours, and according to the shift rate attached to the shift, `MULTIPLIER_PERCENTAGE` is set to 200%, so `Total amount paid for the shift = 2 * 10 * 5 = 100$\` |
| `PER_HOUR_FLAT_RATE`    | Defines the rate of a shift as a flat rate per hour.                                                                                                                                                  | `Total amount for shift = PER_HOUR_FLAT_RATE * Total_payable_hours`                            | `PER_HOUR_FLAT_RATE` is set to `100$` and `total_payable_hours` for the shift are `5 hours`. `Total amount paid for the shift = 100 * 5 = 500$`                                                                                                   |
| `PER_UNIT_FLAT_RATE`    | Defines the rate of a shift as a flat rate per unit of work, instead of per hour. Use this for work compensated by output (for example, deliveries, tickets, or items processed) rather than by time. | `Total amount for shift = PER_UNIT_FLAT_RATE * summary.time_amount`                            | `PER_UNIT_FLAT_RATE` is set to `2.50$` and `time_amount` for the shift is `40 (units)`. `Total amount paid for the shift = 2.50 * 40 = 100$`                                                                                                      |

> **Note**
>
> `PER_HOUR_FLAT_RATE` and `PER_UNIT_FLAT_RATE` apply their value directly to `summary.time_amount` and do not use `summary.time_unit` in the calculation.`time_amount` can represent hours, units, or any other count you choose to submit. `MULTIPLIER_PERCENTAGE` is the only rate type that reads the worker's base salary, and it only produces an amount when `summary.time_unit` is `HOUR`. Submitting a `MULTIPLIER_PERCENTAGE` shift with `time_unit` set to `DAY`, `WEEK`, or `MONTH` will not be compensated, since there is no base-salary conversion for those units.

### Create a shift rate

You can create shift rates for your organization, which you can then map to individual shifts [when adding them](#add-shifts).

#### Make the request

Make a POST request to the [time\_tracking/shift\_rates](https://developer.deel.com/reference/createtimetrackingshiftrate) endpoint.

**`Python`**

```python Python
import requests
import os

url = "https://api.letsdeel.com/rest/time_tracking/shift_rates"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
    "Content-Type": "application/json"
}

payload = {
    "data": {
        "external_id": "regular_rate_1",
        "name": "Regular Shift rate 1",
        "type": "PER_HOUR_FLAT_RATE",
        "value": 150
    }
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const url = 'https://api.letsdeel.com/rest/time_tracking/shift_rates';

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
  'Content-Type': 'application/json'
};

const payload = {
  data: {
    external_id: 'regular_rate_1',
    name: 'Regular Shift rate 1',
    type: 'PER_HOUR_FLAT_RATE',
    value: 150
  }
};

axios.post(url, payload, { headers })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));
```

**`cURL`**

```bash cURL
curl --request POST 'https://api.letsdeel.com/rest/time_tracking/shift_rates' \
--header 'Authorization: Bearer $DEEL_API_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
    }
}'
```

In the body:

| Name         | Required | Type   | Format | Description                                                                                                   | Example                |
| ------------ | -------- | ------ | ------ | ------------------------------------------------------------------------------------------------------------- | ---------------------- |
| external\_id | true     | string | -      | User defined unique identifier for the shift rate                                                             | `regular_rate_1`       |
| name         | true     | string | -      | A human readable string to identify the purpose of the shift rate                                             | `Regular Shift rate 1` |
| type         | false    | string | ENUM   | Defines the type of rate that must be used. Use any of the [available shift rate types](#manage-shift-rates). | `PER_HOUR_FLAT_RATE`   |
| value        | false    | number | -      | Value of the shift rate, to use in combination with the `type` parameter                                      | 150                    |

For example, to create a per-unit rate for piecework, such as \$2.50 per delivery, use `PER_UNIT_FLAT_RATE` as the `type`:

```json
{
  "data": {
    "external_id": "delivery_rate_1",
    "name": "Delivery rate",
    "type": "PER_UNIT_FLAT_RATE",
    "value": 2.50
  }
}
```

Reference this rate's `external_id` from a categorized shift's `summary.shift_rate_external_id`, and set `summary.time_amount` to the number of units completed (for example, `40` for 40 deliveries). See [Add categorized shifts](#add-categorized-shifts).

### Retrieve a shift rate

You can retrieve a shift rate using the `external_id` of the shift rate.

#### Make the request

Make a GET request to the [Retrieve a single time tracking shift rate](https://developer.deel.com/reference/gettimetrackingshiftratebyexternalid) endpoint.

**`Python`**

```python Python
import requests
import os

external_id = "regular_rate_1"
url = f"https://api.letsdeel.com/rest/time_tracking/shift_rates/{external_id}"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
    "Content-Type": "application/json"
}

response = requests.get(url, headers=headers)
print(response.json())
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const externalId = 'regular_rate_1';
const url = `https://api.letsdeel.com/rest/time_tracking/shift_rates/${externalId}`;

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
  'Content-Type': 'application/json'
};

axios.get(url, { headers })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));
```

**`cURL`**

```bash cURL
curl --request GET 'https://api.letsdeel.com/rest/time_tracking/shift_rates/{{external_id}}' \
--header 'Authorization: Bearer $DEEL_API_TOKEN' \
--header 'Content-Type: application/json'
```

Where:

| Name         | Required | Type   | Format | Description                                       | Example        |
| ------------ | -------- | ------ | ------ | ------------------------------------------------- | -------------- |
| external\_id | true     | string | -      | User-defined unique identifier for the shift rate | `shift_123456` |

#### Review the response

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

```json
{
  "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"
  }
}
```

### List shift rates

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

#### Make the request

Make a GET request to the [List time tracking shift rates](https://developer.deel.com/reference/gettimetrackingshiftrates) endpoint.

**`Python`**

```python Python
import requests
import os

url = "https://api.letsdeel.com/rest/time_tracking/shift_rates"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
    "Content-Type": "application/json"
}

params = {
    "limit": 10,
    "offset": 5
}

response = requests.get(url, headers=headers, params=params)
print(response.json())
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const url = 'https://api.letsdeel.com/rest/time_tracking/shift_rates';

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
  'Content-Type': 'application/json'
};

const params = {
  limit: 10,
  offset: 5
};

axios.get(url, { headers, params })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));
```

**`cURL`**

```bash cURL
curl --request GET 'https://api.letsdeel.com/rest/time_tracking/shift_rates?limit=10&offset=5' \
--header 'Authorization: Bearer $DEEL_API_TOKEN' \
--header 'Content-Type: application/json'
```

| Name   | Required | Type   | Format | Description                                                    | Example |
| ------ | -------- | ------ | ------ | -------------------------------------------------------------- | ------- |
| limit  | false    | number |        | Number of rows that must be returned in one API call           | 100     |
| offset | false    | number |        | Number of rows that must be skipped when returning the results | 10      |

#### Review the response

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

```json
{
  "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:

| Name             | Required | Type   | Format    | Description                                                                                             | Example                                      |
| ---------------- | -------- | ------ | --------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| data             | true     | array  | -         | An array of shift rates                                                                                 | `[shift_rate_1, shift_rate_2, shift_rate_3]` |
| organization\_id | true     | number | -         | The ID of your organization                                                                             | `123456`                                     |
| external\_id     | true     | string | -         | User defined unique identifier for the shift rate                                                       | `regular_rate_1`                             |
| name             | true     | string | -         | A human readable string to identify the purpose of the shift rate                                       | `Regular Shift rate 1`                       |
| rate\_type       | false    | string | ENUM      | Defines the type of rate that must be used. Use any of the [available shift rates](#manage-shift-rates) | `PER_HOUR_FLAT_RATE`                         |
| value            | false    | number | -         | Value of the shift rate, to use in combination with the `type` parameter                                | 150                                          |
| created\_at      | true     | string | date-time | Date on which the shift rate is created                                                                 | `2022-05-24T09:38:46.235Z`                   |
| updated\_at      | true     | string | date-time | Date on which the shift rate is updated                                                                 | `2022-05-24T09:38:46.235Z`                   |
| page             | true     | object | -         | An object containing pagination information. Use it to navigate through sets of                         | -                                            |

### Update a shift rate

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

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

#### Make the request

Make a PATCH request to the [Update a time tracking shift rate](https://developer.deel.com/reference/updatetimetrackingshiftrate) endpoint.

**`Python`**

```python Python
import requests
import os

external_id = "regular_rate_1"
url = f"https://api.letsdeel.com/rest/time_tracking/shift_rates/{external_id}"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
    "Content-Type": "application/json"
}

payload = {
    "data": {
        "name": "On-call shift rate",
        "type": "PER_HOUR_FLAT_RATE",
        "value": 150
    }
}

response = requests.patch(url, json=payload, headers=headers)
print(response.json())
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const externalId = 'regular_rate_1';
const url = `https://api.letsdeel.com/rest/time_tracking/shift_rates/${externalId}`;

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
  'Content-Type': 'application/json'
};

const payload = {
  data: {
    name: 'On-call shift rate',
    type: 'PER_HOUR_FLAT_RATE',
    value: 150
  }
};

axios.patch(url, payload, { headers })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));
```

**`cURL`**

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

In the path:

| Name         | Required | Type   | Format | Description                                       | Example          |
| ------------ | -------- | ------ | ------ | ------------------------------------------------- | ---------------- |
| external\_id | true     | string | -      | User defined unique identifier for the shift rate | `regular_rate_1` |

In the body:

| Name  | Required | Type   | Format | Description                                                                                             | Example                |
| ----- | -------- | ------ | ------ | ------------------------------------------------------------------------------------------------------- | ---------------------- |
| name  | true     | string | -      | A human readable string to identify the purpose of the shift rate                                       | `Regular Shift rate 1` |
| type  | true     | string | ENUM   | Defines the type of rate that must be used. Use any of the [available shift rates](#manage-shift-rates) | `PER_HOUR_FLAT_RATE`   |
| value | true     | number | -      | Value of the shift rate, to use in combination with the `type` parameter                                | 150                    |

#### Review the response

A successful response (`200`) returns the updated shift rate.

```json
{
  "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 delete a shift rate if it's not being used in any shift, by using the `external_id` of the shift rate.

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

#### Make the request

Make a DELETE request to the [Delete a time tracking shift rate](https://developer.deel.com/reference/deletetimetrackingshiftrate) endpoint.

**`Python`**

```python Python
import requests
import os

external_id = "regular_rate_1"
url = f"https://api.letsdeel.com/rest/time_tracking/shift_rates/{external_id}"

headers = {
    "Authorization": f"Bearer {os.getenv('DEEL_API_TOKEN')}",
    "Content-Type": "application/json"
}

response = requests.delete(url, headers=headers)
print(response.status_code)
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const externalId = 'regular_rate_1';
const url = `https://api.letsdeel.com/rest/time_tracking/shift_rates/${externalId}`;

const headers = {
  'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
  'Content-Type': 'application/json'
};

axios.delete(url, { headers })
  .then(response => console.log(response.status))
  .catch(error => console.error(error));
```

**`cURL`**

```bash cURL
curl --request DELETE 'https://api.letsdeel.com/rest/time_tracking/shift_rates/{{external_id}}' \
--header 'Authorization: Bearer $DEEL_API_TOKEN' \
--header 'Content-Type: application/json'
```

#### Verify the response

A successful response (`204`) returns an empty body.

## Common errors

Most errors return a `400`, `404`, or `422` response with a self-explanatory message, for example, a missing organization ID or a shift that does not exist. This section covers the errors whose cause is not obvious from the message alone.

### Payroll cycle already closed

A message about the payroll cycle being closed can mean two different things, depending on when you see it:

| When it happens                                                                            | Status | Message                                                                     |
| :----------------------------------------------------------------------------------------- | :----- | :-------------------------------------------------------------------------- |
| Creating a shift with an explicit `payroll_cycle_ref.date` whose cutoff has already passed | `400`  | `Delayed submission is not allowed. Payroll cycle is already closed.`       |
| Updating or deleting a shift whose payroll cycle has already been processed                | `403`  | `You cannot update or delete this shift as payroll cycle is already closed` |

The first only happens if you explicitly set `payroll_cycle_ref.date` — see [Preventing late submissions](#preventing-late-submissions-with-payroll_cycle_refdate). If you omit `payroll_cycle_ref`, the shift is never rejected for being late; it rolls over silently to the next cycle instead. The second happens any time you try to modify a shift after its cycle has already been compensated. Use a [correction shift](#correction-shifts) instead.

### Shift rate type mismatch on update

`400` — `Cannot update rate associated with the shift: the type of the new rate must match the type of the original rate.`

A shift can be repointed to a different shift rate of the *same* type, for example, swapping one `PER_HOUR_FLAT_RATE` for another, but not to a shift rate of a different type, such as switching from `PER_HOUR_FLAT_RATE` to `MULTIPLIER_PERCENTAGE`.

### Shift rate is already in use

`409` — `The shiftRate with id: '{id}' is currently in use and cannot be updated/deleted`

A shift rate becomes locked as soon as any shift references it. To change its value or delete it, first confirm no shift still references it, or create a new shift rate instead.

### Correction shift errors

Corrections carry a few rules that are not obvious from the request shape alone:

* `Cannot apply correction to shift {id}. Correction can only be applied to a regular shift that has been exported.` (`400`): A correction can only target a shift that has already been processed for payroll. If the original shift has not been processed yet, [update it directly](#update-shifts) instead of submitting a correction.
* `Cannot apply correction to shift {id}. Only summary shifts can be corrected.` (`400`): Corrections only work on [categorized shifts](#categorized-shifts). Uncategorized (raw) shifts cannot be corrected.
* `Cannot apply {type} correction to shift {id}. {otherType} corrections have already been applied to this shift.` (`400`): Once an `ADDITION` correction has been applied to a shift, you cannot later apply a `SUBTRACTION` correction to the same shift, and vice versa. Corrections to a given shift must stay in one direction.
* `Cannot apply correction to shift {id}. Subtraction correction would result in an overall negative value` (`400`): A `SUBTRACTION` correction cannot reduce the shift's payable amount below zero.
* `Cannot submit multiple corrections for the same shift reference ID: {ids}` (`400`): A single request cannot contain two corrections for the same original shift. Submit them one at a time, or combine the adjustment into a single correction.

## Next steps

#### [Adjustments](/docs/adjustments)

Submit payroll adjustments for Global Payroll employees.

#### [Global Payroll introduction](/docs/gp-introduction)

Overview of the Global Payroll API.