Validate a new task (dry run)
Checks whether a root task with this body could be created on the project, without creating it.
Same body as POST /v1/task. Runs the body rules, the project / contract / hourly-rate / TDR product-number checks and the task save rules (contract valid for the project, hourly rate on the contract, parent may take sub-tasks), then answers 200 with null. It writes nothing.
Use it to:
- Pre-check a task import row before calling
POST /v1/task
Not for:
- Validating a sub-task placement — not supported
Rules across fields:
- R1: Time & material contract: BudgetHours, BudgetAmount and HourlyRateID may not ALL be non-zero (400).
- R2: TDR contract: BudgetHours, BudgetAmount and TaskHourlyRate may not ALL be non-zero (400).
- R3: TDR contract: TaskHourlyRate must be 0 when HourlyRateID is not 0 (400).
- R4: TDR contract: PaymentRecognitionModel must be 1 or 2; with 2, PaymentProductNo, PaymentName and PaymentInvoiceDate are required (400).
- R5: EndDate must be on or after StartDate (400).
Behaviour:
- Idempotent.
- Safe to retry.
- Changes are visible to the next read immediately.
Call first: GET /v1/project/get-all, GET /v1/contract
Next: POST /v1/task
See also: POST /v1/task/create-sub-task
Not yet documented: examples.
curl -X POST "https://app3.timelog.com/your-account/api/v1/task/validate-new-task" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"ProjectID": 123,
"ParentTaskID": "null",
"TaskName": "John Doe",
"TaskNo": "null",
"Description": "null",
"StartDate": "2024-12-25T10:00:00Z",
"EndDate": "2024-12-25T10:00:00Z",
"AdditionalTextIsRequired": true,
"IsBillable": true,
"IsReadyForInvoicing": true,
"ProjectSubContractID": "null",
"HourlyRateID": 123,
"BudgetHours": 3.14,
"BudgetAmount": 3.14,
"TaskTypeID": "null",
"PaymentRecognitionModel": 42,
"PaymentAmount": 3.14,
"TaskHourlyRate": 3.14,
"PaymentProductNo": "null",
"PaymentName": "null",
"PaymentInvoiceDate": "null"
}'
import requests
import json
url = "https://app3.timelog.com/your-account/api/v1/task/validate-new-task"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
data = {
"ProjectID": 123,
"ParentTaskID": "null",
"TaskName": "John Doe",
"TaskNo": "null",
"Description": "null",
"StartDate": "2024-12-25T10:00:00Z",
"EndDate": "2024-12-25T10:00:00Z",
"AdditionalTextIsRequired": true,
"IsBillable": true,
"IsReadyForInvoicing": true,
"ProjectSubContractID": "null",
"HourlyRateID": 123,
"BudgetHours": 3.14,
"BudgetAmount": 3.14,
"TaskTypeID": "null",
"PaymentRecognitionModel": 42,
"PaymentAmount": 3.14,
"TaskHourlyRate": 3.14,
"PaymentProductNo": "null",
"PaymentName": "null",
"PaymentInvoiceDate": "null"
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const response = await fetch("https://app3.timelog.com/your-account/api/v1/task/validate-new-task", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
},
body: JSON.stringify({
"ProjectID": 123,
"ParentTaskID": "null",
"TaskName": "John Doe",
"TaskNo": "null",
"Description": "null",
"StartDate": "2024-12-25T10:00:00Z",
"EndDate": "2024-12-25T10:00:00Z",
"AdditionalTextIsRequired": true,
"IsBillable": true,
"IsReadyForInvoicing": true,
"ProjectSubContractID": "null",
"HourlyRateID": 123,
"BudgetHours": 3.14,
"BudgetAmount": 3.14,
"TaskTypeID": "null",
"PaymentRecognitionModel": 42,
"PaymentAmount": 3.14,
"TaskHourlyRate": 3.14,
"PaymentProductNo": "null",
"PaymentName": "null",
"PaymentInvoiceDate": "null"
})
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
"bytes"
"encoding/json"
)
func main() {
data := []byte(`{
"ProjectID": 123,
"ParentTaskID": "null",
"TaskName": "John Doe",
"TaskNo": "null",
"Description": "null",
"StartDate": "2024-12-25T10:00:00Z",
"EndDate": "2024-12-25T10:00:00Z",
"AdditionalTextIsRequired": true,
"IsBillable": true,
"IsReadyForInvoicing": true,
"ProjectSubContractID": "null",
"HourlyRateID": 123,
"BudgetHours": 3.14,
"BudgetAmount": 3.14,
"TaskTypeID": "null",
"PaymentRecognitionModel": 42,
"PaymentAmount": 3.14,
"TaskHourlyRate": 3.14,
"PaymentProductNo": "null",
"PaymentName": "null",
"PaymentInvoiceDate": "null"
}`)
req, err := http.NewRequest("POST", "https://app3.timelog.com/your-account/api/v1/task/validate-new-task", bytes.NewBuffer(data))
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
}
require 'net/http'
require 'json'
uri = URI('https://app3.timelog.com/your-account/api/v1/task/validate-new-task')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request.body = '{
"ProjectID": 123,
"ParentTaskID": "null",
"TaskName": "John Doe",
"TaskNo": "null",
"Description": "null",
"StartDate": "2024-12-25T10:00:00Z",
"EndDate": "2024-12-25T10:00:00Z",
"AdditionalTextIsRequired": true,
"IsBillable": true,
"IsReadyForInvoicing": true,
"ProjectSubContractID": "null",
"HourlyRateID": 123,
"BudgetHours": 3.14,
"BudgetAmount": 3.14,
"TaskTypeID": "null",
"PaymentRecognitionModel": 42,
"PaymentAmount": 3.14,
"TaskHourlyRate": 3.14,
"PaymentProductNo": "null",
"PaymentName": "null",
"PaymentInvoiceDate": "null"
}'
response = http.request(request)
puts response.body
"null"
{
"error": "Bad Request",
"message": "The request contains invalid parameters or malformed data",
"code": 400,
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
{
"error": "Unauthorized",
"message": "Authentication required. Please provide a valid API token",
"code": 401
}
{
"Code": 100,
"Details": [],
"Message": "Unknown error occur in system, please contact system administrator.",
"Parameters": "taskModel: {...,"ProjectID":0,...}",
"Url": "https://app{server-number}.timelog.com/{account}/api/v1/task/validate-new-task"
}
/v1/task/validate-new-task
Target server for requests. Edit to use your own host.
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.
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.The media type of the request body
The project the task is created on.
Can be set on create only.
Refers to ProjectRead.ProjectID. Look up valid values with GET /v1/project/get-all.
- 0 or omitted — Should be 400 'Project ID must be greater than 0', but the validator's contract rules crash first: 500 Code 100.
- Unknown ID — 500 Code 102 / 37014 (rest.projectmanagement.project-not-found) — or 500 Code 100 when the validator's contract lookup runs first.
Valid values come from GET /v1/project/get-all.
The existing task the new task is placed under (create-sub-task only).
Can be set on create only.
Refers to TaskRead.TaskID. Look up valid values with GET /v1/task.
- Sent as 0 or negative — 400 (must be > 0 when present).
- create-sub-task: the task is on ANOTHER project — The parent is re-resolved by its WBS number inside ProjectID: the sub-task lands under the task of ProjectID that has the same WBS number, or the call fails with 500.
- create-sub-task: null — Treated as task 0: 500 rest.projectmanagement.task-not-found (37001).
Valid values come from GET /v1/task.
Display name of the task. > Not yet documented: constraints → max length.
The task number shown next to the name (searchable by the time-tracking searches). Copied as sent. > Not yet documented: notes.
Free-text description of the task.
Planned start of the task. Time zone: not yet documented. - Omitted (0001-01-01) — 400 'StartDate is not a valid date.' > Not yet documented: constraints → timezone.
Planned end of the task. Time zone: not yet documented. - Omitted (0001-01-01) — 400 with the message 'StartDate is not a valid date.'. - Before StartDate — 400 'EndDate must be equal to or greater than StartDate.' - Time-and-material basic contract — Becomes the invoice date of the task's instalment. > Not yet documented: constraints → timezone.
When true, time registrations on this task must carry an additional comment.
Default billable flag of time registered on the task. Omitted = false (non-billable task). Read back as TaskRead.IsDefaultBillable.
Marks the task (and, for TDR fixed-price-payment / time-and-material-basic tasks, its instalment) ready for invoicing.
The contract of the project the task is invoiced on.
Refers to ContractRead.ContractID. Look up valid values with GET /v1/contract.
- Omitted / null on POST /v1/task and validate-new-task — The project's main contract.
- Omitted / null on create-sub-task — The parent task's contract.
- 0 or negative — 400 (must be > 0 when present).
- Not a contract of ProjectID — 500 Code 102 / 37042 (rest.projectmanagement.task-contract-not-on-project).
Valid values come from GET /v1/contract.
The contract hourly rate used for the task; 0 = the employee's default hourly rate.
Refers to ContractHourlyRateID.
- Null / omitted — Required (NotNull) — but an empty body fails earlier with 500 Code 100.
- > 0 and not a rate of the contract — 500 Code 102 / 37944 or 37043 (rest.projectmanagement.task-hourly-rate-not-on-contract).
- > 0, fixed-price contract on a project with hourly-rate model Calculated — Set to 0; the task's hourly rate is derived instead (TDR: TaskHourlyRate; other fixed price: the contract's target hourly rate).
- TDR contract and HourlyRateID != 0 — TaskHourlyRate must then be 0 (400 'Hourly rate must be 0 when HourlyRateID is selected.').
> Not yet documented: values → lookup op.
Budgeted hours of the task. Unit: hours - Null / omitted — Required (NotNull, >= 0) — an empty body fails earlier with 500 Code 100. - Time & material contract, BudgetAmount and HourlyRateID also non-zero — 400: only two of BudgetHours, BudgetAmount, HourlyRateID may be non-zero. - HourlyRateID > 0 and BudgetHours = 0 — Derived: BudgetAmount / the rate's amount, rounded to InvoiceSetting_NumberOfDecimals. - TDR on a Calculated-rate project and BudgetHours = 0 — Derived: BudgetAmount / TaskHourlyRate (1 when the rate is 0).
Budgeted revenue of the task. Unit: project currency - Null / omitted — Required (NotNull, >= 0) — an empty body fails earlier with 500 Code 100. - HourlyRateID > 0 and BudgetAmount = 0 — Derived: BudgetHours x the rate's amount. - TDR contract — Stored as the budget; the task's allocated revenue is PaymentAmount instead. - Otherwise — Also becomes the task's allocated revenue amount.
The task type (classification) of the task.
Refers to TaskTypeRead.TaskTypeID.
- 0 or negative — 400 (must be > 0 when present).
- Unknown ID (create only) — 500 Code 102 / 37010 (rest.projectmanagement.task-type-not-found). validate-new-task does not check it.
- Null — Stored as 0 (no task type). The account's 'task type required' setting is deliberately not enforced by the save rules.
> Not yet documented: values → lookup op.
How a task-driven-revenue (TDR) task recognises revenue.
Conditional. Required (1 or 2) when the task's contract is TDR; ignored and reset to 0 otherwise.
Allowed values:
| Value | Name | Meaning |
|---|---|---|
| 0 | Undefined | Not a TDR task. Rejected (400) on a TDR contract. |
| 1 | OverallPaymentPlan | Revenue follows the contract's overall payment plan; no instalment of its own. |
| 2 | FixedPricePayment | The task has its own fixed-price instalment: PaymentAmount, PaymentProductNo, PaymentName and PaymentInvoiceDate become required and are written to it. |
TDR only — the task's allocated revenue, and with FixedPricePayment the amount of its instalment. Conditional. TDR contract: >= 0 (0 when omitted). Ignored otherwise. Can be set on create only. Unit: project currency - TDR contract — Becomes the task's allocated revenue amount (replaces BudgetAmount for that). - TDR + FixedPricePayment and the task has exactly one instalment after the save — Written as that instalment's Rate and Amount.
TDR only — the task's own hourly rate on a project whose hourly-rate model is Calculated. Conditional. TDR + FixedPricePayment: >= 0. Must be 0 when HourlyRateID != 0, and not all three of BudgetHours, BudgetAmount, TaskHourlyRate may be non-zero. Ignored on other contracts. Can be set on create only. Unit: project currency per hour - TDR, Calculated hourly-rate model, TaskHourlyRate = 0 — Derived: BudgetAmount / BudgetHours (1 when hours are 0). - TDR, Calculated hourly-rate model, TaskHourlyRate > 0 — Stored as the task's hourly rate; used to derive BudgetHours or BudgetAmount when one of them is 0.
TDR fixed-price-payment only — product number of the task's instalment.
Conditional. Required (non-empty) when the contract is TDR and PaymentRecognitionModel = 2; must exist in the project's legal entity whenever sent on a TDR contract.
Can be set on create only.
Refers to ProductNumber.
- TDR contract, unknown number — 500 Code 102 / 37939 (rest.projectmanagement.task-payment-product-number-not-found).
> Not yet documented: values → lookup op.
TDR fixed-price-payment only — name (invoice text) of the task's instalment. Conditional. Required (non-empty) when the contract is TDR and PaymentRecognitionModel = 2. Ignored otherwise. Can be set on create only.
TDR fixed-price-payment only — invoice date of the task's instalment. Conditional. Required when the contract is TDR and PaymentRecognitionModel = 2. Ignored otherwise. Can be set on create only. Time zone: not yet documented. - Feature LockPaymentDateToFPTTaskDate on — On later task saves the instalment's invoice date is re-synced to the task's EndDate. > Not yet documented: constraints → timezone.
Request Preview
Response
Response will appear here after sending the request
Authentication
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.
Body
The project the task is created on.
Can be set on create only.
Refers to ProjectRead.ProjectID. Look up valid values with GET /v1/project/get-all.
-
0 or omitted — Should be 400 'Project ID must be greater than 0', but the validator's contract rules crash first: 500 Code 100.
-
Unknown ID — 500 Code 102 / 37014 (rest.projectmanagement.project-not-found) — or 500 Code 100 when the validator's contract lookup runs first.
Valid values come from GET /v1/project/get-all.
The existing task the new task is placed under (create-sub-task only).
Can be set on create only.
Refers to TaskRead.TaskID. Look up valid values with GET /v1/task.
-
Sent as 0 or negative — 400 (must be > 0 when present).
-
create-sub-task: the task is on ANOTHER project — The parent is re-resolved by its WBS number inside ProjectID: the sub-task lands under the task of ProjectID that has the same WBS number, or the call fails with 500.
-
create-sub-task: null — Treated as task 0: 500 rest.projectmanagement.task-not-found (37001).
Valid values come from GET /v1/task.
Display name of the task.
Not yet documented: constraints → max length.
The task number shown next to the name (searchable by the time-tracking searches).
Copied as sent.
Not yet documented: notes.
Free-text description of the task.
Planned start of the task.
Time zone: not yet documented.
- Omitted (0001-01-01) — 400 'StartDate is not a valid date.'
Not yet documented: constraints → timezone.
Planned end of the task.
Time zone: not yet documented.
-
Omitted (0001-01-01) — 400 with the message 'StartDate is not a valid date.'.
-
Before StartDate — 400 'EndDate must be equal to or greater than StartDate.'
-
Time-and-material basic contract — Becomes the invoice date of the task's instalment.
Not yet documented: constraints → timezone.
When true, time registrations on this task must carry an additional comment.
Default billable flag of time registered on the task.
Omitted = false (non-billable task). Read back as TaskRead.IsDefaultBillable.
Marks the task (and, for TDR fixed-price-payment / time-and-material-basic tasks, its instalment) ready for invoicing.
The contract of the project the task is invoiced on.
Refers to ContractRead.ContractID. Look up valid values with GET /v1/contract.
-
Omitted / null on POST /v1/task and validate-new-task — The project's main contract.
-
Omitted / null on create-sub-task — The parent task's contract.
-
0 or negative — 400 (must be > 0 when present).
-
Not a contract of ProjectID — 500 Code 102 / 37042 (rest.projectmanagement.task-contract-not-on-project).
Valid values come from GET /v1/contract.
The contract hourly rate used for the task; 0 = the employee's default hourly rate.
Refers to ContractHourlyRateID.
-
Null / omitted — Required (NotNull) — but an empty body fails earlier with 500 Code 100.
-
0 and not a rate of the contract — 500 Code 102 / 37944 or 37043 (rest.projectmanagement.task-hourly-rate-not-on-contract).
-
0, fixed-price contract on a project with hourly-rate model Calculated — Set to 0; the task's hourly rate is derived instead (TDR: TaskHourlyRate; other fixed price: the contract's target hourly rate).
-
TDR contract and HourlyRateID != 0 — TaskHourlyRate must then be 0 (400 'Hourly rate must be 0 when HourlyRateID is selected.').
Not yet documented: values → lookup op.
Budgeted hours of the task.
Unit: hours
-
Null / omitted — Required (NotNull, >= 0) — an empty body fails earlier with 500 Code 100.
-
Time & material contract, BudgetAmount and HourlyRateID also non-zero — 400: only two of BudgetHours, BudgetAmount, HourlyRateID may be non-zero.
-
HourlyRateID > 0 and BudgetHours = 0 — Derived: BudgetAmount / the rate's amount, rounded to InvoiceSetting_NumberOfDecimals.
-
TDR on a Calculated-rate project and BudgetHours = 0 — Derived: BudgetAmount / TaskHourlyRate (1 when the rate is 0).
Budgeted revenue of the task.
Unit: project currency
-
Null / omitted — Required (NotNull, >= 0) — an empty body fails earlier with 500 Code 100.
-
HourlyRateID > 0 and BudgetAmount = 0 — Derived: BudgetHours x the rate's amount.
-
TDR contract — Stored as the budget; the task's allocated revenue is PaymentAmount instead.
-
Otherwise — Also becomes the task's allocated revenue amount.
The task type (classification) of the task.
Refers to TaskTypeRead.TaskTypeID.
-
0 or negative — 400 (must be > 0 when present).
-
Unknown ID (create only) — 500 Code 102 / 37010 (rest.projectmanagement.task-type-not-found). validate-new-task does not check it.
-
Null — Stored as 0 (no task type). The account's 'task type required' setting is deliberately not enforced by the save rules.
Not yet documented: values → lookup op.
How a task-driven-revenue (TDR) task recognises revenue.
Conditional. Required (1 or 2) when the task's contract is TDR; ignored and reset to 0 otherwise.
Allowed values:
| Value | Name | Meaning |
|---|---|---|
0 | Undefined | Not a TDR task. Rejected (400) on a TDR contract. |
1 | OverallPaymentPlan | Revenue follows the contract's overall payment plan; no instalment of its own. |
2 | FixedPricePayment | The task has its own fixed-price instalment: PaymentAmount, PaymentProductNo, PaymentName and PaymentInvoiceDate become required and are written to it. |
012TDR only — the task's allocated revenue, and with FixedPricePayment the amount of its instalment.
Conditional. TDR contract: >= 0 (0 when omitted). Ignored otherwise.
Can be set on create only.
Unit: project currency
-
TDR contract — Becomes the task's allocated revenue amount (replaces BudgetAmount for that).
-
TDR + FixedPricePayment and the task has exactly one instalment after the save — Written as that instalment's Rate and Amount.
TDR only — the task's own hourly rate on a project whose hourly-rate model is Calculated.
Conditional. TDR + FixedPricePayment: >= 0. Must be 0 when HourlyRateID != 0, and not all three of BudgetHours, BudgetAmount, TaskHourlyRate may be non-zero. Ignored on other contracts.
Can be set on create only.
Unit: project currency per hour
-
TDR, Calculated hourly-rate model, TaskHourlyRate = 0 — Derived: BudgetAmount / BudgetHours (1 when hours are 0).
-
TDR, Calculated hourly-rate model, TaskHourlyRate > 0 — Stored as the task's hourly rate; used to derive BudgetHours or BudgetAmount when one of them is 0.
TDR fixed-price-payment only — product number of the task's instalment.
Conditional. Required (non-empty) when the contract is TDR and PaymentRecognitionModel = 2; must exist in the project's legal entity whenever sent on a TDR contract.
Can be set on create only.
Refers to ProductNumber.
- TDR contract, unknown number — 500 Code 102 / 37939 (rest.projectmanagement.task-payment-product-number-not-found).
Not yet documented: values → lookup op.
TDR fixed-price-payment only — name (invoice text) of the task's instalment.
Conditional. Required (non-empty) when the contract is TDR and PaymentRecognitionModel = 2. Ignored otherwise.
Can be set on create only.
TDR fixed-price-payment only — invoice date of the task's instalment.
Conditional. Required when the contract is TDR and PaymentRecognitionModel = 2. Ignored otherwise.
Can be set on create only.
Time zone: not yet documented.
- Feature LockPaymentDateToFPTTaskDate on — On later task saves the instalment's invoice date is re-synced to the task's EndDate.
Not yet documented: constraints → timezone.
Responses
Success without content: HTTP 200 with the literal JSON body null. The literal null (TafSuccessResult): the task could be built and passed the save rules.