ProjectsUpdate a task

Update a task

Changes the fields, status or plan position of an existing task and returns it.

A partial update (TaskUpdate): only the fields you send change. It can rename the task, change its dates, budget, hourly rate, task type and additional-text flag, change its status (Completed / Cancelled close it for registration), and move it under another task of the same project (ParentTaskID, 0 = root). The task's contract cannot be changed. All field, move and up-front status rules are checked on a detached copy before anything is saved, but the call is not atomic: a move is saved on its own first, and a status change does its own writes before the final project save. Answers 200 with the task read back as TaskRead — which has no dates, budget or status, so the response cannot confirm what changed.

Use it to:

  • Keep task names, dates and budgets in sync from a PSA / ERP project plan
  • Close a task for registration when the deliverable is done (Status 3) or cancelled (Status 4)
  • Restructure a plan: move a task under another task, or back to the root

Not for:

  • Changing the task's contract — not supported; a move into a contract sub-group is rejected
  • A dry run → POST /v1/task/{taskID}/validate-update-task

Rules across fields:

  • R1: When both dates are sent, EndDate must be on or after StartDate (400 Code 200).
  • R2: BudgetHours and BudgetAmount may not both be sent when an hourly rate drives the task's budget (400).

Behaviour:

  • Not idempotent.
  • Re-sending the same field values is a no-op for those fields and the same ParentTaskID is skipped; a status already reached is skipped. The project is still re-saved.
  • Safe to retry only under a condition. Safe after a 4xx from the dry-run stage (nothing written). After a 500 or a timeout, GET the task (and list the project's tasks) before retrying: the move and status writes may have happened.
  • Side effect: ParentTaskID: the task (with its sub-tree) is moved and the project saved immediately; WBS numbers are renumbered; the old parent's payment is rebuilt when it lost its last child. The task keeps its contract.
  • Side effect: Status: closing / reopening runs TimeLog's task status processing — allocations set completed / not completed, the contract reopened when a closed task is reopened, vouchers reopened when leaving Completed, completion processing ('causes invoicing') when completing, the status pushed to sub-tasks of a parent; recurring tasks notify hard bookings.
  • On failure: A rejected status change (400) or a later 500 leaves an earlier move saved; status-change writes done before a failure stay.
  • Changes are visible to the next read immediately.

Call first: GET /v1/task, GET /v1/task/{taskID}

Next: GET /v1/task/{taskID}

See also: POST /v1/task/{taskID}/validate-update-task, POST /v1/task, DELETE /v1/task/{taskID}

Not yet documented: behaviour → events emitted; behaviour → side effects; examples.

curl -X PUT "https://app3.timelog.com/your-account/api/v1/task/123" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
  "TaskName": "null",
  "TaskNo": "null",
  "Description": "null",
  "StartDate": "null",
  "EndDate": "null",
  "AdditionalTextIsRequired": "null",
  "BudgetHours": "null",
  "BudgetAmount": "null",
  "HourlyRateID": "null",
  "TaskTypeID": "null",
  "Status": "null",
  "ParentTaskID": "null"
}'
{
  "Properties": {
    "TaskID": 123,
    "ID": "123e4567-e89b-12d3-a456-426614174000",
    "Name": "John Doe",
    "No": "example_string",
    "IsFixedPrice": true,
    "IsDefaultBillable": true,
    "AdditionalTextIsRequired": true,
    "ParentFullName": "John Doe",
    "ProjectID": 123,
    "Project": {
      "ProjectID": 123,
      "ID": "123e4567-e89b-12d3-a456-426614174000",
      "Name": "John Doe",
      "No": "example_string",
      "ExpenseIsBillable": "null",
      "CustomerID": 123,
      "Customer": {
        "CustomerID": 123,
        "ID": "123e4567-e89b-12d3-a456-426614174000",
        "Name": "John Doe",
        "No": "example_string",
        "DefaultMileageDistance": 25,
        "CustomerStatusID": 123,
        "VATNo": "example_string",
        "OrganizationNo": "example_string"
      },
      "MainContractId": 123
    },
    "ParentTaskID": "null",
    "IsTravelTimeTask": true,
    "IsFavourite": "null"
  },
  "Links": [
    {
      "Href": "example_string",
      "Rel": "example_string"
    }
  ],
  "Actions": [
    {}
  ]
}
PUT
/v1/task/{taskID}
PUT
Base URLstring

Target server for requests. Edit to use your own host.

Bearer Token
Bearer Tokenstring
Required

OAuth 2.0 bearer token, sent as Authorization: Bearer <token>. JWT and reference tokens are both accepted. The token must carry the required scope (default tlp). The token's sub claim is the acting user. A token WITHOUT sub (client-credentials) acts as the system service user. If the sub user cannot log in (inactive, locked, deleted), the request is rejected with 401 — it is no longer let through without a user. > Not yet documented: how to obtain a token. > Not yet documented: token lifetime.

OAuth 2.0 bearer token, sent as Authorization: Bearer <token>. JWT and reference tokens are both accepted. The token must carry the required scope (default tlp). The token's sub claim is the acting user. A token WITHOUT sub (client-credentials) acts as the system service user. If the sub user cannot log in (inactive, locked, deleted), the request is rejected with 401 — it is no longer let through without a user. > Not yet documented: how to obtain a token. > Not yet documented: token lifetime.
path
taskIDinteger
Required

The task to update. Route constraint :int.

Format: int32
Content-Typestring
Required

The media type of the request body

Options: application/json
TaskNamestringnull

New display name of the task. Null: null / omitted = unchanged; there is no way to clear the name. - Sent as empty string — 400 'TaskName cannot be empty when supplied.' > Not yet documented: constraints → max length.

Min length: 1
TaskNostringnull

New task number. Null: null / omitted = unchanged. - The account's task-number setting auto-generates or hides task numbers — Passed to Task.ChangeNo with the ShowTaskNo setting; per the model's comment the value is then ignored. > Not yet documented: behaviour.

Descriptionstringnull

New free-text description. Null: null / omitted = unchanged. An empty string clears it.

StartDatestringnull

New planned start of the task. Null: null / omitted = unchanged. Time zone: not yet documented. - Not a valid date (e.g. 0001-01-01) — 400 'StartDate is not a valid date.' - After the stored EndDate (EndDate not sent) — Not checked by the body validator; left to the task rules (task.Validate / save rules) — 400 from the controller. > Not yet documented: constraints → timezone. > Not yet documented: behaviour.

Format: date-time
EndDatestringnull

New planned end of the task. Null: null / omitted = unchanged. Time zone: not yet documented. - Not a valid date — 400 'EndDate is not a valid date.'. - Both dates sent and EndDate < StartDate — 400 'EndDate must be equal to or greater than StartDate.' > Not yet documented: constraints → timezone.

Format: date-time
AdditionalTextIsRequiredbooleannull

When true, time registrations on this task must carry an additional comment. Null: null / omitted = unchanged.

BudgetHoursnumbernull

New budgeted hours of the task. Null: null / omitted = unchanged. Unit: hours - Negative — 400 (>= 0). - BudgetAmount also sent and an hourly rate drives the budget (time & material or Allocated rate model with HourlyRateID > 0; fixed price Calculated with a task hourly rate > 0) — 400 'BudgetHours and BudgetAmount cannot both be supplied when an hourly rate applies to the task…'. > Not yet documented: behaviour.

Format: double
BudgetAmountnumbernull

New budgeted revenue of the task. Null: null / omitted = unchanged. Unit: project currency - Negative — 400 (>= 0). - BudgetHours also sent and an hourly rate drives the budget — 400 (see BudgetHours). > Not yet documented: behaviour.

Format: double
HourlyRateIDintegernull

New contract hourly rate of the task; 0 = the default hourly rate. Refers to ContractHourlyRateID. Null: null / omitted = unchanged. - Negative — 400 (>= 0). - Changed, fixed-price contract with the Calculated rate model — ChangeHourlyRateId does nothing for this case, yet the call answers 200 (an existence check still runs for > 0). - Applied BEFORE the budget fields — The adapter applies HourlyRateID first, so BudgetHours / BudgetAmount in the same request are judged against the NEW rate. > Not yet documented: values → lookup op.

Format: int32
TaskTypeIDintegernull

New task type (classification). Refers to TaskTypeRead.TaskTypeID. Null: null / omitted = unchanged. 0 is rejected, so a task type cannot be removed through this body. - 0 or negative — 400 (> 0). > Not yet documented: values → lookup op. > Not yet documented: behaviour.

Format: int32
Statusintegernull

New status of the task. Completed and Cancelled close it for time registration. Null: null / omitted = unchanged. - Not a member — 400. - Planning task and a status other than NotStarted / Completed — Violation 'A planning task can only be "not started" or "completed"'. - Reopening (from Completed or Cancelled) — Reopens the task's contract when it is closed, sets allocations back to not completed; from Completed also re-opens the task's vouchers. - Parent task (not a contract-sub-group primary) — The status is pushed down to all sub-tasks. Allowed values: | Value | Name | Meaning | |---|---|---| | 0 | NotStarted | Not started. Rejected when the task already has time registrations (Task_ChangeState violation). | | 1 | InProgress | Open and in progress. | | 2 | OnHold | Open, paused. | | 3 | Completed | Closed. | > Not yet documented: values → members → meaning. > Not yet documented: values → members → meaning.

Format: int32 • Options: 0, 1, 2, 3, 4
ParentTaskIDintegernull

Moves the task in the project plan — under this task, or to the root with 0. Refers to TaskRead.TaskID. Look up valid values with GET /v1/task. Null: null / omitted = placement unchanged. 0 = move to the end of the plan's root level. - Negative — 400 (>= 0). - Same as the current parent — No move. - A task of another project — MovedTaskIsUnderSameProject violation (400). - The task itself or one of its descendants — TaskCannotMoveToOwnSubTask violation (400). - A contract sub-group task — 400 'ParentTaskID points at a contract sub group task…' — that move would change the task's contract and payments. - Recurring / ordinary mix, a receiver that cannot become a parent, WBS depth exceeded, contract-sub-group WBS rule, project-level rules — 400 with the violation messages. - Accepted — The move is SAVED on its own (MoveTaskUnderParentTask saves the project) before the other fields; the task keeps its contract (changeToParentContract false) and the old parent's payment is rebuilt. Valid values come from GET /v1/task.

Format: int32
Request Preview
Response

Response will appear here after sending the request

Authentication

header
Authorizationstring
Required

Bearer token. OAuth 2.0 bearer token, sent as Authorization: Bearer <token>. JWT and reference tokens are both accepted. The token must carry the required scope (default tlp).

The token's sub claim is the acting user. A token WITHOUT sub (client-credentials) acts as the system service user. If the sub user cannot log in (inactive, locked, deleted), the request is rejected with 401 — it is no longer let through without a user.

Not yet documented: how to obtain a token.

Not yet documented: token lifetime.

Path Parameters

taskIDinteger
Required

The task to update. Route constraint :int.

Body

application/json
TaskNamestring,null

New display name of the task.

Null: null / omitted = unchanged; there is no way to clear the name.

  • Sent as empty string — 400 'TaskName cannot be empty when supplied.'

Not yet documented: constraints → max length.

TaskNostring,null

New task number.

Null: null / omitted = unchanged.

  • The account's task-number setting auto-generates or hides task numbers — Passed to Task.ChangeNo with the ShowTaskNo setting; per the model's comment the value is then ignored.

Not yet documented: behaviour.

Descriptionstring,null

New free-text description.

Null: null / omitted = unchanged. An empty string clears it.

StartDatestring,null

New planned start of the task.

Null: null / omitted = unchanged.

Time zone: not yet documented.

  • Not a valid date (e.g. 0001-01-01) — 400 'StartDate is not a valid date.'

  • After the stored EndDate (EndDate not sent) — Not checked by the body validator; left to the task rules (task.Validate / save rules) — 400 from the controller.

Not yet documented: constraints → timezone.

Not yet documented: behaviour.

EndDatestring,null

New planned end of the task.

Null: null / omitted = unchanged.

Time zone: not yet documented.

  • Not a valid date — 400 'EndDate is not a valid date.'.

  • Both dates sent and EndDate < StartDate — 400 'EndDate must be equal to or greater than StartDate.'

Not yet documented: constraints → timezone.

AdditionalTextIsRequiredboolean,null

When true, time registrations on this task must carry an additional comment.

Null: null / omitted = unchanged.

BudgetHoursnumber,null

New budgeted hours of the task.

Null: null / omitted = unchanged.

Unit: hours

  • Negative — 400 (>= 0).

  • BudgetAmount also sent and an hourly rate drives the budget (time & material or Allocated rate model with HourlyRateID > 0; fixed price Calculated with a task hourly rate > 0) — 400 'BudgetHours and BudgetAmount cannot both be supplied when an hourly rate applies to the task…'.

Not yet documented: behaviour.

BudgetAmountnumber,null

New budgeted revenue of the task.

Null: null / omitted = unchanged.

Unit: project currency

  • Negative — 400 (>= 0).

  • BudgetHours also sent and an hourly rate drives the budget — 400 (see BudgetHours).

Not yet documented: behaviour.

HourlyRateIDinteger,null

New contract hourly rate of the task; 0 = the default hourly rate.

Refers to ContractHourlyRateID.

Null: null / omitted = unchanged.

  • Negative — 400 (>= 0).

  • Changed, fixed-price contract with the Calculated rate model — ChangeHourlyRateId does nothing for this case, yet the call answers 200 (an existence check still runs for > 0).

  • Applied BEFORE the budget fields — The adapter applies HourlyRateID first, so BudgetHours / BudgetAmount in the same request are judged against the NEW rate.

Not yet documented: values → lookup op.

TaskTypeIDinteger,null

New task type (classification).

Refers to TaskTypeRead.TaskTypeID.

Null: null / omitted = unchanged. 0 is rejected, so a task type cannot be removed through this body.

  • 0 or negative — 400 (> 0).

Not yet documented: values → lookup op.

Not yet documented: behaviour.

Statusinteger,null

New status of the task. Completed and Cancelled close it for time registration.

Null: null / omitted = unchanged.

  • Not a member — 400.

  • Planning task and a status other than NotStarted / Completed — Violation 'A planning task can only be "not started" or "completed"'.

  • Reopening (from Completed or Cancelled) — Reopens the task's contract when it is closed, sets allocations back to not completed; from Completed also re-opens the task's vouchers.

  • Parent task (not a contract-sub-group primary) — The status is pushed down to all sub-tasks.

Allowed values:

ValueNameMeaning
0NotStartedNot started. Rejected when the task already has time registrations (Task_ChangeState violation).
1InProgressOpen and in progress.
2OnHoldOpen, paused.
3CompletedClosed.

Not yet documented: values → members → meaning.

Not yet documented: values → members → meaning.

Allowed values:01234
ParentTaskIDinteger,null

Moves the task in the project plan — under this task, or to the root with 0.

Refers to TaskRead.TaskID. Look up valid values with GET /v1/task.

Null: null / omitted = placement unchanged. 0 = move to the end of the plan's root level.

  • Negative — 400 (>= 0).

  • Same as the current parent — No move.

  • A task of another project — MovedTaskIsUnderSameProject violation (400).

  • The task itself or one of its descendants — TaskCannotMoveToOwnSubTask violation (400).

  • A contract sub-group task — 400 'ParentTaskID points at a contract sub group task…' — that move would change the task's contract and payments.

  • Recurring / ordinary mix, a receiver that cannot become a parent, WBS depth exceeded, contract-sub-group WBS rule, project-level rules — 400 with the violation messages.

  • Accepted — The move is SAVED on its own (MoveTaskUnderParentTask saves the project) before the other fields; the task keeps its contract (changeToParentContract false) and the old parent's payment is rebuilt.

Valid values come from GET /v1/task.

Responses

Propertiesobject

A short task representation for time-tracking pickers: the task's IDs, name and number, its parent, its project, and three flags that change how a registration on it behaves (fixed price, default billable, additional text required). It is not the full task (no dates, budget, status or contract). The TAF self link points to GET /v1/task/{TaskID}; entities also carry TAF actions (add-registration, start-time-tracker and, depending on the operation, create-task / create-sub-task / validate-new-task) describing other POST operations.

Linksarray
Actionsobject[]