Allocate a user to a task
Creates an allocation of one user on a task, with planned hours, hourly rate and optional period, and returns it.
Plans a user on a task: AllocatedHours, the contract hourly rate (omit HourlyRateID to let TimeLog resolve the user's default rate) and, when the account feature 'Enable start and end date on allocations' is on, a period (a missing date defaults to the task's). One allocation per user and task: a second one is refused with 409, pointing to the existing allocation ID. The user is added to the project's resource group when missing. Answers 200 with the new allocation as AllocationRead. Requires project privileges on the task's project. Not idempotent and not atomic.
Use it to:
- Staff a project from a resource-planning or PSA tool: allocate people with planned hours
- Give a user access to register time on a task, with an explicit hourly rate
Not for:
- A dry run →
POST /v1/task/{taskID}/allocation/validate-new-allocation
Rules across fields:
- R1: Effective period: StartDate (or the task's StartDate) must not be after EndDate (or the task's EndDate) (400).
- R2: At most one allocation per (user, task), any status (409).
Behaviour:
- Not idempotent.
- Safe to retry only under a condition. A retry after success answers 409 with the existing allocation ID — treat that 409 as 'already done'.
- Duplicates: Refused with 409 (pre-check on employee + task).
- Side effect: Inserts the allocation (status NotCompleted; recurring tasks get a recurring allocation without period) with an audit-log + e-mail event, and sets the task's HasAllocations flag.
- Side effect: Adds the user's employee to the project's resource group when missing (resource hourly rate 0).
- Side effect: Notifies the allocation search table, so the task appears in the user's caller-scoped task / project searches.
- Side effect: Re-saves the project.
- Side effect: Dates sent, no task date sync, not recurring: sends the 'allocation exceeds task dates' notification when the period goes beyond the task.
- Changes are visible to the next read immediately.
Call first: GET /v1/task, GET /v1/user
Next: GET /v1/task/{taskID}/allocation, POST /v1/time-registration
See also: POST /v1/task/{taskID}/allocation/validate-new-allocation
Not yet documented: behaviour → consistency; behaviour → duplicate handling; behaviour → side effects; examples.
curl -X POST "https://app3.timelog.com/your-account/api/v1/task/123/allocation" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"UserID": 123,
"AllocatedHours": 3.14,
"HourlyRateID": "null",
"StartDate": "null",
"EndDate": "null"
}'
import requests
import json
url = "https://app3.timelog.com/your-account/api/v1/task/123/allocation"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
data = {
"UserID": 123,
"AllocatedHours": 3.14,
"HourlyRateID": "null",
"StartDate": "null",
"EndDate": "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/123/allocation", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
},
body: JSON.stringify({
"UserID": 123,
"AllocatedHours": 3.14,
"HourlyRateID": "null",
"StartDate": "null",
"EndDate": "null"
})
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
"bytes"
"encoding/json"
)
func main() {
data := []byte(`{
"UserID": 123,
"AllocatedHours": 3.14,
"HourlyRateID": "null",
"StartDate": "null",
"EndDate": "null"
}`)
req, err := http.NewRequest("POST", "https://app3.timelog.com/your-account/api/v1/task/123/allocation", 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/123/allocation')
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 = '{
"UserID": 123,
"AllocatedHours": 3.14,
"HourlyRateID": "null",
"StartDate": "null",
"EndDate": "null"
}'
response = http.request(request)
puts response.body
{
"Properties": {
"AllocationID": 123,
"TaskID": 123,
"UserID": 123,
"AllocatedHours": 3.14,
"RegisteredHours": 3.14,
"HourlyRateID": 123,
"HourlyRateName": "null",
"HourlyRateAmount": 3.14,
"HourlyRateCurrencyAbb": "example_string",
"StartDate": "null",
"EndDate": "null",
"IsRecurring": true,
"IsCompleted": true
},
"Links": [
{
"Href": "example_string",
"Rel": "example_string"
}
],
"Actions": [
{}
]
}
{
"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
}
{
"error": "Forbidden",
"message": "You don't have permission to access this resource",
"code": 403
}
{
"error": "Not Found",
"message": "The requested resource was not found",
"code": 404
}
{
"error": "Conflict",
"message": "The request conflicts with the current state of the resource",
"code": 409,
"details": "Resource already exists"
}
{
"error": "Internal Server Error",
"message": "An unexpected error occurred on the server",
"code": 500,
"requestId": "req_1234567890"
}
/v1/task/{taskID}/allocationTarget 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 task to allocate on. Must be a leaf task that is not Completed / Cancelled. Route constraint :int.
The media type of the request body
The USER to allocate (a user ID, not an employee ID). Must be an existing, active user without an allocation on the task.
Refers to UserRead.UserID. Look up valid values with GET /v1/user.
Contains personal data.
- 0 or omitted — 400 Code 200 (NotEmpty).
- Unknown user — 400 'No user found with ID: {0}'.
- Deactivated user — 400 'User with ID {0} is not active'.
- The user already has an allocation on the task (any status) — 409 Code 102 '… already has an allocation on task … Update it with PUT v1/allocation/{allocationID} instead.'
Read back as AllocationRead.UserID.
Valid values come from GET /v1/user.
Hours the user is planned to work on the task. Unit: hours - Null / omitted — 400 Code 200 (NotNull). - Negative — 400 Code 200 (>= 0). 0 is accepted (an allocation that only lets the user register).
The contract hourly rate of the allocation. Omit it to let TimeLog resolve the default rate for the user.
Refers to ContractHourlyRateID.
- 0 or negative — 400 Code 200 (> 0 when present).
- > 0 and not a rate of the task's contract, or inactive — 400 with the violation message (37944 text) or 'The contract hourly rate with ID {0} is not active'.
- Omitted — Resolved on save: fixed-price task on a Calculated project → the system hourly rate; time & material / Allocated → the task's rate, else the employee's resource-group rate, else the project's standard rate, else the employee's standard rate.
- Omitted and no default can be resolved — 400 'No default hourly rate could be resolved for task with ID {0}. Supply HourlyRateID or add a rate to the contract.' — predicted by the adapter before the save, and mapped from the save's pre-write violations (HourlyRateIsNotvalidOnTask, TimeRegistrationCannotBeCreatedWithInvalidAllocationHourlyRateID) after it.
> Not yet documented: values → lookup op.
> Not yet documented: behaviour.
First day of the allocation period. Conditional. Only allowed when the account feature 'Enable start and end date on allocations' (AllocationDates) is active, and never on a recurring task. Optional then; omitted = the task's StartDate. Time zone: none — the time part is dropped (.Date) - Feature AllocationDates off — 400 Code 200 'StartDate cannot be set because the feature … is not active'. - Recurring task — 400 'StartDate and EndDate cannot be set on task with ID {0}; allocations on recurring tasks have no period'. - Omitted (only EndDate sent) — The task's StartDate; the effective span is checked (400 when EndDate is then before it). - Outside the task's dates — Accepted. With feature ChangeTaskDateBasedAllocation the task's dates follow the allocations; otherwise an 'allocation exceeds task dates' notification is sent.
Last day of the allocation period. Conditional. Same as StartDate. Omitted = the task's EndDate. Time zone: none — the time part is dropped (.Date) - Both dates sent and EndDate < StartDate — 400 Code 200 (GreaterThanOrEqualTo). - Effective span (missing date taken from the task) ends before it starts — 400 'EndDate (yyyy-MM-dd) cannot be before StartDate (yyyy-MM-dd)'.
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.
Path Parameters
The task to allocate on. Must be a leaf task that is not Completed / Cancelled. Route constraint :int.
Body
The USER to allocate (a user ID, not an employee ID). Must be an existing, active user without an allocation on the task.
Refers to UserRead.UserID. Look up valid values with GET /v1/user.
Contains personal data.
-
0 or omitted — 400 Code 200 (NotEmpty).
-
Unknown user — 400 'No user found with ID: {0}'.
-
Deactivated user — 400 'User with ID {0} is not active'.
-
The user already has an allocation on the task (any status) — 409 Code 102 '… already has an allocation on task … Update it with PUT v1/allocation/{allocationID} instead.'
Read back as AllocationRead.UserID.
Valid values come from GET /v1/user.
Hours the user is planned to work on the task.
Unit: hours
-
Null / omitted — 400 Code 200 (NotNull).
-
Negative — 400 Code 200 (>= 0).
0 is accepted (an allocation that only lets the user register).
The contract hourly rate of the allocation. Omit it to let TimeLog resolve the default rate for the user.
Refers to ContractHourlyRateID.
-
0 or negative — 400 Code 200 (> 0 when present).
-
0 and not a rate of the task's contract, or inactive — 400 with the violation message (37944 text) or 'The contract hourly rate with ID {0} is not active'.
-
Omitted — Resolved on save: fixed-price task on a Calculated project → the system hourly rate; time & material / Allocated → the task's rate, else the employee's resource-group rate, else the project's standard rate, else the employee's standard rate.
-
Omitted and no default can be resolved — 400 'No default hourly rate could be resolved for task with ID {0}. Supply HourlyRateID or add a rate to the contract.' — predicted by the adapter before the save, and mapped from the save's pre-write violations (HourlyRateIsNotvalidOnTask, TimeRegistrationCannotBeCreatedWithInvalidAllocationHourlyRateID) after it.
Not yet documented: values → lookup op.
Not yet documented: behaviour.
First day of the allocation period.
Conditional. Only allowed when the account feature 'Enable start and end date on allocations' (AllocationDates) is active, and never on a recurring task. Optional then; omitted = the task's StartDate.
Time zone: none — the time part is dropped (.Date)
-
Feature AllocationDates off — 400 Code 200 'StartDate cannot be set because the feature … is not active'.
-
Recurring task — 400 'StartDate and EndDate cannot be set on task with ID {0}; allocations on recurring tasks have no period'.
-
Omitted (only EndDate sent) — The task's StartDate; the effective span is checked (400 when EndDate is then before it).
-
Outside the task's dates — Accepted. With feature ChangeTaskDateBasedAllocation the task's dates follow the allocations; otherwise an 'allocation exceeds task dates' notification is sent.
Last day of the allocation period.
Conditional. Same as StartDate. Omitted = the task's EndDate.
Time zone: none — the time part is dropped (.Date)
-
Both dates sent and EndDate < StartDate — 400 Code 200 (GreaterThanOrEqualTo).
-
Effective span (missing date taken from the task) ends before it starts — 400 'EndDate (yyyy-MM-dd) cannot be before StartDate (yyyy-MM-dd)'.
Responses
One user's allocation on a task: planned (allocated) hours, hours already registered, the contract hourly rate with its name, amount and currency, the allocation period, and whether it is recurring or completed.