> 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.

# Update a shift

PATCH https://api.letsdeel.com/rest/time-tracking/shifts/{shift_id}
Content-Type: application/json

Corrects a shift that was recorded wrongly, before payroll closes its period. Editing an approved shift sends it back for approval unless the organization sends it as approved again.

 **Token scopes**: `time-tracking:write`, `worker:write`

 **Beta**: this version requires the `X-Beta: true` request header and its contract may change before it is promoted to stable. It is scheduled to become stable on 2027-02-13.

Reference: https://developer.deel.com/api/reference/endpoints/time-tracking-shifts/update-shift-v-2026-11-13

## Authentication

- `Authorization` header (bearer token, required) — ## Authentication The Deel API uses bearer tokens to authenticate requests. All API calls must be made over HTTPS — calls over plain HTTP or without authentication will fail. ```curl curl -X GET 'https://api.letsdeel.com/rest/v2/contracts' \ -H 'Authorization: Bearer YOUR-TOKEN-HERE' ``` [Learn more about authentication](/api/authentication)
- `Authorization` header (bearer token, required) — Standard OAuth2 security scheme based on https://swagger.io/docs/specification/authentication/

## Servers

- `https://api.letsdeel.com/rest` (Production, default)
- `https://api-staging.letsdeel.com/rest` (Demo)

## Request

### Path parameters

- `shift_id` (string, required) — Deel ID of the shift, as returned in the shift's id field.

### Headers

- `Idempotency-Key` (string, optional) — Unique key for this write, up to 64 characters, kept for 24 hours. Repeating a successful request with the same key returns the first response instead of writing again; a failed request is not kept and can be retried with the same key. A request whose key is still in progress, or was used for a different request, gets 409.
- `X-Beta` (enum, required, default: true) — Opts in to the beta version of this endpoint. Must be `true`.
  - Allowed values: `true`

### Body (application/json)

This endpoint expects an object.

- `data` (TimeTrackingShiftsShiftIdPatchRequestBodyContentApplicationJsonSchemaData, required) — Fields to change. Fields not sent keep their value; breaks and call_outs, when sent, replace the whole list.

## Response

### 200

Shift updated.

- `data` (TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaData, required) — A shift recorded on a contract.

## Errors

### 400 Bad Request Error

MALFORMED_REQUEST. The body is not valid JSON or lacks the data wrapper.

- `errors` (list of TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaErrorsItems, required) — Every problem found, not only the first.

### 401 Unauthorized Error

The token is missing, invalid, inactive or revoked (INVALID_TOKEN, TOKEN_INACTIVE, TOKEN_REVOKED or UNAUTHORIZED).

- `errors` (list of TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaErrorsItems, required) — Every problem found, not only the first.

### 403 Forbidden Error

INSUFFICIENT_SCOPES, APPROVED_STATUS_NOT_ALLOWED (as on create) or ON_BEHALF_SUBMISSION_NOT_ALLOWED, as on create.

- `errors` (list of TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaErrorsItems, required) — Every problem found, not only the first.

### 404 Not Found Error

SHIFT_NOT_FOUND. No such shift in the caller's organization, it was deleted, or a worker token asked for another worker's shift.

- `errors` (list of TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaErrorsItems, required) — Every problem found, not only the first.

### 409 Conflict Error

SHIFT_LOCKED (the shift cannot change; details.reason is PAYROLL_LOCKED or FILE_UPLOAD_SHIFT, and there is no details.reason when the policy refuses the change or the lock could not be checked), or IDEMPOTENCY_CONFLICT / IDEMPOTENCY_KEY_REUSED as on create.

- `errors` (list of TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaErrorsItems, required) — Every problem found, not only the first.

### 422 Unprocessable Entity Error

Every create validation applies to the shift as it would be after the change, OVERTIME_ACKNOWLEDGEMENT_REQUIRED included, plus FIELD_NOT_UPDATABLE when contract_id is sent or when the change would record the time another way (a shift with start and end, a duration-only shift and a DAY_TRACKING shift cannot become one another).

- `errors` (list of TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaErrorsItems, required) — Every problem found, not only the first.

### 429 Too Many Requests Error

RATE_LIMITED. Over 5 requests per second for the organization.

- `errors` (list of TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaErrorsItems, required) — Every problem found, not only the first.

### 500 Internal Server Error

INTERNAL_ERROR. Unexpected failure; safe to retry with the same Idempotency-Key.

- `errors` (list of TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaErrorsItems, required) — Every problem found, not only the first.

## Types

### TimeTrackingShiftsShiftIdPatchRequestBodyContentApplicationJsonSchemaData

Fields to change. Fields not sent keep their value; breaks and call_outs, when sent, replace the whole list.

- `end` (string, optional, nullable) — Shift end as the local clock time where the work happened, without a UTC offset. Send with start; after start.
- `date` (string, optional) — Shift day, the local date the shift belongs to. Required for duration-only and DAY_TRACKING shifts; taken from start when omitted otherwise.
- `type` (enum, optional) — Kind of time the shift records. Which types a contract accepts depends on its time tracking policy.
  - Allowed values: `REGULAR`, `OVERTIME`, `ON_CALL`, `KEEP_IN_TOUCH`, `DAY_TRACKING`
- `start` (string, optional, nullable) — Shift start as the local clock time where the work happened, without a UTC offset (not UTC). Send with end; null or omitted for duration-only and DAY_TRACKING shifts. Stored as sent.
- `breaks` (list of TimeTrackingShiftsShiftIdPatchRequestBodyContentApplicationJsonSchemaDataBreaksItems, optional) — Breaks inside the shift, without overlaps. Not accepted on duration-only shifts.
- `status` (enum, optional) — Approval status to set. PENDING sends the shift to approval. APPROVED is accepted from organization tokens only; worker tokens get 403.
  - Allowed values: `PENDING`, `APPROVED`
- `timezone` (string, optional, nullable) — IANA time zone the shift was worked in, stored as sent and not checked. When omitted or null, it is filled from the requester's profile, as in the app. Start and end are never converted to it.
- `call_outs` (list of TimeTrackingShiftsShiftIdPatchRequestBodyContentApplicationJsonSchemaDataCallOutsItems, optional) — Call-outs inside an ON_CALL shift, without overlaps. Accepted only on ON_CALL shifts.
- `project_id` (string, optional, nullable) — Project the time was spent on. Must belong to the contract.
- `description` (string, optional, nullable) — What the worker did. Required when the time tracking policy requires a work description.
- `is_half_day` (boolean, optional) — Whether a DAY_TRACKING shift counts as half a day. Accepted only on DAY_TRACKING shifts.
- `worked_hours` (double, optional) — Hours worked. Required for duration-only shifts (no start or end); not accepted when start and end are sent, as Deel calculates it.
- `is_meal_break_waived` (boolean, optional) — Whether the worker waived the meal break. Accepted only when the time tracking policy allows waiving.

### TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaData

A shift recorded on a contract.

- `id` (string, required) — Deel ID of the shift; the shift_id path parameter.
- `end` (string, required, nullable) — Shift end as local clock time, without a UTC offset; null for duration-only and DAY_TRACKING shifts.
- `date` (string, required) — Shift day, the local date the shift belongs to.
- `type` (enum, required) — Kind of time the shift records. Which types a contract accepts depends on its time tracking policy.
  - Allowed values: `REGULAR`, `OVERTIME`, `ON_CALL`, `KEEP_IN_TOUCH`, `DAY_TRACKING`
- `start` (string, required, nullable) — Shift start as local clock time, without a UTC offset; null for duration-only and DAY_TRACKING shifts.
- `breaks` (list of TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaDataBreaksItems, required) — Breaks inside the shift; empty when there are none.
- `origin` (enum, required) — Channel the shift was entered through. PLATFORM is the Deel web app, ADMIN is Deel staff, AUTO_SUBMIT is automatic submission, INTERNAL_INTEGRATION is another Deel product, EXTERNAL_INTEGRATION is this API; null when the channel was not recorded.
  - Allowed values: `PLATFORM`, `MOBILE`, `ADMIN`, `AUTO_SUBMIT`, `INTERNAL_INTEGRATION`, `EXTERNAL_INTEGRATION`, `SLACK`
- `status` (enum, required) — Approval status of the shift. On a pay-as-you-go contract it follows the hourly report: APPROVED also covers hourly reports being processed, paid or marked not payable.
  - Allowed values: `PENDING`, `APPROVED`, `REJECTED`
- `timezone` (string, required, nullable) — IANA time zone the shift was worked in: as sent, or filled from the requester's profile when it was not sent; null when neither had one.
- `call_outs` (list of TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaDataCallOutsItems, required) — Call-outs inside an ON_CALL shift; empty for other types.
- `created_at` (string, required) — When the shift was created, in UTC.
- `project_id` (string, required, nullable) — Project the time was spent on; null when none.
- `updated_at` (string, required) — When the shift last changed, in UTC.
- `break_hours` (double, required) — Hours spent on breaks.
- `contract_id` (string, required) — Deel contract the shift is recorded on.
- `description` (string, required, nullable) — What the worker did; null when none.
- `total_hours` (double, required) — Hours from start to end, breaks included.
- `worked_hours` (double, required) — Hours worked, total hours minus break hours.
- `is_meal_break_waived` (boolean, required) — Whether the worker waived the meal break.
- `is_half_day` (boolean, optional) — Whether a DAY_TRACKING shift counts as half a day. Present only on DAY_TRACKING shifts.

### TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaErrorsItems

One problem with the request.

- `code` (string, required) — Stable machine-readable error code.
- `message` (string, required) — Human-readable explanation. May change; do not parse it.
- `field` (string, optional) — Request field the error refers to, when there is one.
- `details` (map from string to any, optional) — Extra data for the error code, such as the reason a shift is locked.

### TimeTrackingShiftsShiftIdPatchRequestBodyContentApplicationJsonSchemaDataBreaksItems

A break taken inside the shift.

- `end` (string, required) — Break end, local time (no UTC offset). After start and not after the shift end.
- `type` (enum, required) — Kind of break. BREAK is a generic break; use MEAL_BREAK or REST_BREAK when known, and LACTATION_BREAK where the time tracking policy offers it.
  - Allowed values: `BREAK`, `MEAL_BREAK`, `REST_BREAK`, `LACTATION_BREAK`
- `start` (string, required) — Break start, local time where the work happened (no UTC offset).

### TimeTrackingShiftsShiftIdPatchRequestBodyContentApplicationJsonSchemaDataCallOutsItems

A period inside an on-call shift when the worker was called out to work.

- `end` (string, required) — Call-out end, local time (no UTC offset). After start and not after the shift end.
- `start` (string, required) — Call-out start, local time where the work happened (no UTC offset).

### TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaDataBreaksItems

A break taken inside the shift.

- `end` (string, required) — Break end, local time (no UTC offset). After start and not after the shift end.
- `type` (enum, required) — Kind of break. BREAK is a generic break; use MEAL_BREAK or REST_BREAK when known, and LACTATION_BREAK where the time tracking policy offers it.
  - Allowed values: `BREAK`, `MEAL_BREAK`, `REST_BREAK`, `LACTATION_BREAK`
- `start` (string, required) — Break start, local time where the work happened (no UTC offset).

### TimeTrackingShiftsShiftIdPatchResponsesContentApplicationJsonSchemaDataCallOutsItems

A period inside an on-call shift when the worker was called out to work.

- `end` (string, required) — Call-out end, local time (no UTC offset). After start and not after the shift end.
- `start` (string, required) — Call-out start, local time where the work happened (no UTC offset).

## Examples

**Request**

```json
{
  "data": {
    "end": "2026-11-03T19:00:00"
  }
}
```

**Response**

```json
{
  "data": {
    "id": "9d2e4f6a-1b3c-4d5e-8f7a-2b4c6d8e0f1a",
    "end": "2026-11-03T18:00:00",
    "date": "2026-11-03",
    "type": "REGULAR",
    "start": "2026-11-03T09:00:00",
    "breaks": [
      {
        "end": "2026-11-03T13:00:00",
        "type": "MEAL_BREAK",
        "start": "2026-11-03T12:00:00"
      }
    ],
    "origin": "EXTERNAL_INTEGRATION",
    "status": "PENDING",
    "timezone": "America/Sao_Paulo",
    "call_outs": [
      {
        "end": "2026-11-03T16:30:00",
        "start": "2026-11-03T15:00:00"
      }
    ],
    "created_at": "2026-11-03T21:05:12Z",
    "project_id": "0b6f3c1e-2d4a-4f8b-9e7c-1a2b3c4d5e6f",
    "updated_at": "2026-11-03T21:05:12Z",
    "break_hours": 1,
    "contract_id": "m3x9k2p",
    "description": "Warehouse night support",
    "total_hours": 9,
    "worked_hours": 8,
    "is_meal_break_waived": false,
    "is_half_day": false
  }
}
```

**SDK Code**

```python
import requests

url = "https://api.letsdeel.com/rest/time-tracking/shifts/9d2e4f6a-1b3c-4d5e-8f7a-2b4c6d8e0f1a"

payload = { "data": { "end": "2026-11-03T19:00:00" } }
headers = {
    "Idempotency-Key": "3f1c2b9e-7a4d-4e1b-9c2a-5d8e6f7a1b2c",
    "X-Beta": "true",
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

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

print(response.json())
```

```javascript
const url = 'https://api.letsdeel.com/rest/time-tracking/shifts/9d2e4f6a-1b3c-4d5e-8f7a-2b4c6d8e0f1a';
const options = {
  method: 'PATCH',
  headers: {
    'Idempotency-Key': '3f1c2b9e-7a4d-4e1b-9c2a-5d8e6f7a1b2c',
    'X-Beta': 'true',
    Authorization: 'Bearer <token>',
    'Content-Type': 'application/json'
  },
  body: '{"data":{"end":"2026-11-03T19:00:00"}}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.letsdeel.com/rest/time-tracking/shifts/9d2e4f6a-1b3c-4d5e-8f7a-2b4c6d8e0f1a"

	payload := strings.NewReader("{\n  \"data\": {\n    \"end\": \"2026-11-03T19:00:00\"\n  }\n}")

	req, _ := http.NewRequest("PATCH", url, payload)

	req.Header.Add("Idempotency-Key", "3f1c2b9e-7a4d-4e1b-9c2a-5d8e6f7a1b2c")
	req.Header.Add("X-Beta", "true")
	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.letsdeel.com/rest/time-tracking/shifts/9d2e4f6a-1b3c-4d5e-8f7a-2b4c6d8e0f1a")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Patch.new(url)
request["Idempotency-Key"] = '3f1c2b9e-7a4d-4e1b-9c2a-5d8e6f7a1b2c'
request["X-Beta"] = 'true'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"data\": {\n    \"end\": \"2026-11-03T19:00:00\"\n  }\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.patch("https://api.letsdeel.com/rest/time-tracking/shifts/9d2e4f6a-1b3c-4d5e-8f7a-2b4c6d8e0f1a")
  .header("Idempotency-Key", "3f1c2b9e-7a4d-4e1b-9c2a-5d8e6f7a1b2c")
  .header("X-Beta", "true")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"data\": {\n    \"end\": \"2026-11-03T19:00:00\"\n  }\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('PATCH', 'https://api.letsdeel.com/rest/time-tracking/shifts/9d2e4f6a-1b3c-4d5e-8f7a-2b4c6d8e0f1a', [
  'body' => '{
  "data": {
    "end": "2026-11-03T19:00:00"
  }
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
    'Idempotency-Key' => '3f1c2b9e-7a4d-4e1b-9c2a-5d8e6f7a1b2c',
    'X-Beta' => 'true',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.letsdeel.com/rest/time-tracking/shifts/9d2e4f6a-1b3c-4d5e-8f7a-2b4c6d8e0f1a");
var request = new RestRequest(Method.PATCH);
request.AddHeader("Idempotency-Key", "3f1c2b9e-7a4d-4e1b-9c2a-5d8e6f7a1b2c");
request.AddHeader("X-Beta", "true");
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"data\": {\n    \"end\": \"2026-11-03T19:00:00\"\n  }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Idempotency-Key": "3f1c2b9e-7a4d-4e1b-9c2a-5d8e6f7a1b2c",
  "X-Beta": "true",
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = ["data": ["end": "2026-11-03T19:00:00"]] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.letsdeel.com/rest/time-tracking/shifts/9d2e4f6a-1b3c-4d5e-8f7a-2b4c6d8e0f1a")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```