Create a time registration
Creates a time or absence registration for an employee on a date.
Registers project time, travel time or absence. You supply the registration's GUID. The response is the created registration, including the integer TimeRegistrationID used by GET and DELETE.
Use it to:
- Import hours from an external time-tracking or calendar tool
- Register absence from an HR system
Not for:
- Import with project/contract and an hourly-rate override →
POST /v1/time-registration/import
Rules across fields:
- R1: GroupType Project (1) or TravelTime (6) → TaskID and non-zero Hours are required; EntityID is ignored.
- R2: GroupType Absence (3) → EntityID (absence code) is required; TaskID is not used.
- R3: TimeTrackingFormat Hours (0) → Hours required and non-zero, for any GroupType.
Behaviour:
- Not idempotent.
- Safe to retry only under a condition. Retry with the SAME GUID: a 422 with 38030 means the first attempt succeeded.
- Duplicates: Not deduplicated beyond the GUID. Same work sent with a new GUID creates a second registration.
- Side effect: If the employee is not allocated to the task: adds the employee to the project's resource group and contract team and creates a 0-hour allocation. This happens after the existence checks but BEFORE business-rule validation, outside the save transaction — it persists even if the registration is then rejected.
- Side effect: Creates the work unit; updates invoicing potential, salary posting and mileage links (same transaction).
- Side effect: Recalculates financial data after commit.
- Read-after-write consistency: not yet documented.
Next: GET /v1/time-registration/{timeRegistrationID}, PUT /v1/time-registration/{timeRegistrationID}/external-keys
See also: PUT /v1/time-registration
Not yet documented: behaviour → consistency; examples (success).
curl -X POST "https://app3.timelog.com/your-account/api/v1/time-registration" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"ID": "123e4567-e89b-12d3-a456-426614174000",
"TaskID": 123,
"UserID": 123,
"Date": "2024-12-25T10:00:00Z",
"Hours": 3.14,
"Minutes": 42,
"GroupType": 42,
"TimeTrackingFormat": 42,
"EntityID": 123,
"Billable": true,
"BillableHours": 3.14,
"BillableMinutes": 42,
"Factor": 3.14,
"Comment": "null",
"AdditionalComment": "example_string",
"MonthlyPeriod": "example_string",
"StartTime": "2024-12-25T10:00:00Z",
"IsCalendarEvent": true
}'
import requests
import json
url = "https://app3.timelog.com/your-account/api/v1/time-registration"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
data = {
"ID": "123e4567-e89b-12d3-a456-426614174000",
"TaskID": 123,
"UserID": 123,
"Date": "2024-12-25T10:00:00Z",
"Hours": 3.14,
"Minutes": 42,
"GroupType": 42,
"TimeTrackingFormat": 42,
"EntityID": 123,
"Billable": true,
"BillableHours": 3.14,
"BillableMinutes": 42,
"Factor": 3.14,
"Comment": "null",
"AdditionalComment": "example_string",
"MonthlyPeriod": "example_string",
"StartTime": "2024-12-25T10:00:00Z",
"IsCalendarEvent": true
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const response = await fetch("https://app3.timelog.com/your-account/api/v1/time-registration", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
},
body: JSON.stringify({
"ID": "123e4567-e89b-12d3-a456-426614174000",
"TaskID": 123,
"UserID": 123,
"Date": "2024-12-25T10:00:00Z",
"Hours": 3.14,
"Minutes": 42,
"GroupType": 42,
"TimeTrackingFormat": 42,
"EntityID": 123,
"Billable": true,
"BillableHours": 3.14,
"BillableMinutes": 42,
"Factor": 3.14,
"Comment": "null",
"AdditionalComment": "example_string",
"MonthlyPeriod": "example_string",
"StartTime": "2024-12-25T10:00:00Z",
"IsCalendarEvent": true
})
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
"bytes"
"encoding/json"
)
func main() {
data := []byte(`{
"ID": "123e4567-e89b-12d3-a456-426614174000",
"TaskID": 123,
"UserID": 123,
"Date": "2024-12-25T10:00:00Z",
"Hours": 3.14,
"Minutes": 42,
"GroupType": 42,
"TimeTrackingFormat": 42,
"EntityID": 123,
"Billable": true,
"BillableHours": 3.14,
"BillableMinutes": 42,
"Factor": 3.14,
"Comment": "null",
"AdditionalComment": "example_string",
"MonthlyPeriod": "example_string",
"StartTime": "2024-12-25T10:00:00Z",
"IsCalendarEvent": true
}`)
req, err := http.NewRequest("POST", "https://app3.timelog.com/your-account/api/v1/time-registration", 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/time-registration')
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 = '{
"ID": "123e4567-e89b-12d3-a456-426614174000",
"TaskID": 123,
"UserID": 123,
"Date": "2024-12-25T10:00:00Z",
"Hours": 3.14,
"Minutes": 42,
"GroupType": 42,
"TimeTrackingFormat": 42,
"EntityID": 123,
"Billable": true,
"BillableHours": 3.14,
"BillableMinutes": 42,
"Factor": 3.14,
"Comment": "null",
"AdditionalComment": "example_string",
"MonthlyPeriod": "example_string",
"StartTime": "2024-12-25T10:00:00Z",
"IsCalendarEvent": true
}'
response = http.request(request)
puts response.body
{
"Properties": {
"TimeRegistrationID": 123,
"TimeRegistrationGUID": "123e4567-e89b-12d3-a456-426614174000",
"UserID": 123,
"DepartmentID": 123,
"Date": "2024-12-25T10:00:00Z",
"Hours": 3.14,
"Minutes": 42,
"BillableHours": 3.14,
"BillableMinutes": 42,
"IsBillable": true,
"Factor": 3.14,
"EntityType": 42,
"EntityID": 123,
"AbsenceCode": {
"AbsenceCodeID": 123,
"AbsenceCode": "example_string",
"Name": "John Doe",
"Description": "example_string",
"IsActive": true,
"FlexPositiveFactor": 3.14,
"SalaryAccountFactor": 3.14,
"SalaryAccountFactorEffect": 10,
"SalaryAccountID": 123,
"AbsenceCodeTypeID": 123,
"DeductCapacity": true,
"TimeTrackingFormat": 42,
"RequireApproval": true,
"IsPublicVisible": true,
"IsFavourite": "null",
"ID": "123e4567-e89b-12d3-a456-426614174000"
},
"TimeTrackingFormat": 42,
"Comment": "null",
"AdditionalTextField": "null",
"MonthlyPeriod": "null",
"Status": 42,
"ApprovalStatus": 42,
"InvoiceStatus": 42,
"IsInvoiced": true,
"IsFixedPrice": "null",
"IsInClosedAccountingPeriod": true,
"IsMeeting": true,
"CalendarEventID": 123,
"StartTime": "null",
"EndTime": "null",
"Created": "2024-12-25T10:00:00Z",
"CreatedBy": 42,
"LastModified": "2024-12-25T10:00:00Z",
"LastModifiedBy": 42,
"InterfaceCreated": 42,
"InterfaceModified": 42
},
"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": "Unprocessable Entity",
"message": "The request was well-formed but contains semantic errors",
"code": 422,
"details": [
{
"field": "password",
"message": "Password must be at least 8 characters long"
}
]
}
{
"error": "Internal Server Error",
"message": "An unexpected error occurred on the server",
"code": 500,
"requestId": "req_1234567890"
}
/v1/time-registration
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
Client-generated GUID that identifies the registration. On create you choose it; on update it selects the registration to replace. It is NOT the integer TimeRegistrationID. Can be set on create only. Identifier of this object, unique per TimeLog site. Re-using an existing GUID on create fails with 38030 (422). Create is not idempotent.
The task the time is registered on.
Conditional. Required (non-zero) when GroupType is Project (1) or TravelTime (6). Not used for Absence.
Refers to TaskRead.TaskID.
If the employee is not yet allocated to the task, TimeLog CREATES the allocation (adds the employee to the project resource group, the contract team and a 0-hour allocation) — before the registration is validated. See operation behaviour.
The user (employee) the time belongs to.
Refers to UserRead.UserID.
On update this is overwritten — an update can move a registration to another user.
The date the time is registered on. Time zone: not yet documented. Stored as sent; no truncation of a time component found. Send midnight (YYYY-MM-DDT00:00:00). > Not yet documented: constraints → timezone.
Duration in decimal hours (e.g. 1.5 = 1 h 30 min). Conditional. Required and non-zero when GroupType is Project, or TimeTrackingFormat is Hours. Unit: hours (decimal) - GroupType = Absence and TimeTrackingFormat = FullDay or HalfDay — Hours are taken from the employee's normal working time. No server-side rejection of negative values was found (only != 0 is checked).
Duration in minutes. - GroupType = Project or TravelTime — Hours always wins — the API passes Hours (or 0) to the engine, so Minutes is never applied. Send Hours. - GroupType = Absence and TimeTrackingFormat = Hours — Minutes is applied after Hours and wins.
What kind of registration this is.
Allowed values:
| Value | Name | Meaning |
|---|---|---|
| 1 | Project | Time worked on a project task. |
| 3 | Absence | Absence (vacation, sickness …). EntityID = absence code. |
| 6 | TravelTime | Travel time on a project task. |
How the duration is expressed.
Allowed values:
| Value | Name | Meaning |
|---|---|---|
| 0 | Hours | Duration in hours/minutes. |
| 1 | FullDay | Absence for a full day (hours from normal working time). |
| 2 | HalfDay | Absence for half a day (hours from normal working time). |
| 3 | YesNo | Salary-code (supplement) format: a registration posts a count of 1. |
| 4 | Count | Salary-code (supplement) format: a registration posts the entered number as a count. |
For absence, the absence code to register.
Conditional. Required when GroupType = Absence. Ignored otherwise.
Refers to AbsenceCodeRead.AbsenceCodeID.
- GroupType = Project or TravelTime — Replaced by the allocation resolved from TaskID + UserID.
Whether the time is billable to the customer. - Feature BillableOnTime is OFF — Forced to the task's billable flag. - Task is fixed-price and Billable = false — Fails with 37620.
Billable duration in decimal hours. Unit: hours (decimal) - Feature BillableHoursIsEditable is OFF — Set to the actual Hours. - Feature BillableHoursIsEditable is ON and the field is omitted — 0 billable hours is stored. - Fixed-price task and BillableHoursIsEditableOnFixedPrice is OFF — Synced to actual hours. - RoundingOfBillableTime is ON — Mismatch raises 30168.
Billable duration in minutes. - always — BillableHours is always passed (0 when omitted), so BillableMinutes is never applied.
Registration factor (100 = normal). Used for overtime factors. Default: 0 on the wire → stored as 100 - Feature OvertimeFactor is OFF, or the task is fixed-price — Always 100. - OvertimeFactor ON and value is in the setting RoundingFactorOptionOfTimeRegistrations (default "100") — applied - OvertimeFactor ON, create, value not in the list — Stored as 100. - OvertimeFactor ON, update, value not in the list — Previous value kept. > Not yet documented: description.
Free-text comment on the registration. Null: On update, omitting Comment clears the stored comment. - Feature MandatoryComment is ON and comment is empty — Fails with 300116.
Second free-text field, configurable per site (title and validation are site settings). Conditional. Required when the site configures the additional field as mandatory (30048).
Salary month the registration belongs to. - Feature TempTreatAllTimeRegistrationAsRecurringTask is OFF — ignored - Value outside ±MonthlyPeriodDropdownRange months of today, or malformed — Returns 500 (Code 100), not 400.
Start time of the work. Time zone: not yet documented. - The user does not have timestamps enabled, or the registration is an absence — StartTime/EndTime are cleared. - Timestamps enabled, StartTime sent — Seconds dropped; no timezone conversion; EndTime = StartTime + Hours. - Timestamps enabled, create, no StartTime — EndTime = the user's current time on that date; StartTime = EndTime − Hours. If that would put StartTime on the previous day, StartTime = midnight of Date and EndTime = StartTime + Hours (may run past midnight). - Timestamps enabled, update, no StartTime, stored registration has a StartTime — The stored StartTime is kept; EndTime = StartTime + Hours. - Timestamps enabled, update, no StartTime, stored registration has no StartTime — The registration stays without timestamps (EndTime cleared).
Marks the registration as originating from a calendar meeting. - create — Sets IsMeeting on the registration. No other effect via this API (CalendarEventID is always 0). - update — ignored
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
Client-generated GUID that identifies the registration. On create you choose it; on update it selects the registration to replace. It is NOT the integer TimeRegistrationID.
Can be set on create only.
Identifier of this object, unique per TimeLog site.
Re-using an existing GUID on create fails with 38030 (422). Create is not idempotent.
The task the time is registered on.
Conditional. Required (non-zero) when GroupType is Project (1) or TravelTime (6). Not used for Absence.
Refers to TaskRead.TaskID.
If the employee is not yet allocated to the task, TimeLog CREATES the allocation (adds the employee to the project resource group, the contract team and a 0-hour allocation) — before the registration is validated. See operation behaviour.
The user (employee) the time belongs to.
Refers to UserRead.UserID.
On update this is overwritten — an update can move a registration to another user.
The date the time is registered on.
Time zone: not yet documented.
Stored as sent; no truncation of a time component found. Send midnight (YYYY-MM-DDT00:00:00).
Not yet documented: constraints → timezone.
Duration in decimal hours (e.g. 1.5 = 1 h 30 min).
Conditional. Required and non-zero when GroupType is Project, or TimeTrackingFormat is Hours.
Unit: hours (decimal)
- GroupType = Absence and TimeTrackingFormat = FullDay or HalfDay — Hours are taken from the employee's normal working time.
No server-side rejection of negative values was found (only != 0 is checked).
Duration in minutes.
-
GroupType = Project or TravelTime — Hours always wins — the API passes Hours (or 0) to the engine, so Minutes is never applied. Send Hours.
-
GroupType = Absence and TimeTrackingFormat = Hours — Minutes is applied after Hours and wins.
What kind of registration this is.
Allowed values:
| Value | Name | Meaning |
|---|---|---|
1 | Project | Time worked on a project task. |
3 | Absence | Absence (vacation, sickness …). EntityID = absence code. |
6 | TravelTime | Travel time on a project task. |
136How the duration is expressed.
Allowed values:
| Value | Name | Meaning |
|---|---|---|
0 | Hours | Duration in hours/minutes. |
1 | FullDay | Absence for a full day (hours from normal working time). |
2 | HalfDay | Absence for half a day (hours from normal working time). |
3 | YesNo | Salary-code (supplement) format: a registration posts a count of 1. |
4 | Count | Salary-code (supplement) format: a registration posts the entered number as a count. |
01234For absence, the absence code to register.
Conditional. Required when GroupType = Absence. Ignored otherwise.
Refers to AbsenceCodeRead.AbsenceCodeID.
- GroupType = Project or TravelTime — Replaced by the allocation resolved from TaskID + UserID.
Whether the time is billable to the customer.
-
Feature BillableOnTime is OFF — Forced to the task's billable flag.
-
Task is fixed-price and Billable = false — Fails with 37620.
Billable duration in decimal hours.
Unit: hours (decimal)
-
Feature BillableHoursIsEditable is OFF — Set to the actual Hours.
-
Feature BillableHoursIsEditable is ON and the field is omitted — 0 billable hours is stored.
-
Fixed-price task and BillableHoursIsEditableOnFixedPrice is OFF — Synced to actual hours.
-
RoundingOfBillableTime is ON — Mismatch raises 30168.
Billable duration in minutes.
- always — BillableHours is always passed (0 when omitted), so BillableMinutes is never applied.
Registration factor (100 = normal). Used for overtime factors.
Default: 0 on the wire → stored as 100
-
Feature OvertimeFactor is OFF, or the task is fixed-price — Always 100.
-
OvertimeFactor ON and value is in the setting RoundingFactorOptionOfTimeRegistrations (default "100") — applied
-
OvertimeFactor ON, create, value not in the list — Stored as 100.
-
OvertimeFactor ON, update, value not in the list — Previous value kept.
Not yet documented: description.
Free-text comment on the registration.
Null: On update, omitting Comment clears the stored comment.
- Feature MandatoryComment is ON and comment is empty — Fails with 300116.
Second free-text field, configurable per site (title and validation are site settings).
Conditional. Required when the site configures the additional field as mandatory (30048).
Salary month the registration belongs to.
-
Feature TempTreatAllTimeRegistrationAsRecurringTask is OFF — ignored
-
Value outside ±MonthlyPeriodDropdownRange months of today, or malformed — Returns 500 (Code 100), not 400.
Start time of the work.
Time zone: not yet documented.
-
The user does not have timestamps enabled, or the registration is an absence — StartTime/EndTime are cleared.
-
Timestamps enabled, StartTime sent — Seconds dropped; no timezone conversion; EndTime = StartTime + Hours.
-
Timestamps enabled, create, no StartTime — EndTime = the user's current time on that date; StartTime = EndTime − Hours. If that would put StartTime on the previous day, StartTime = midnight of Date and EndTime = StartTime + Hours (may run past midnight).
-
Timestamps enabled, update, no StartTime, stored registration has a StartTime — The stored StartTime is kept; EndTime = StartTime + Hours.
-
Timestamps enabled, update, no StartTime, stored registration has no StartTime — The registration stays without timestamps (EndTime cleared).
Marks the registration as originating from a calendar meeting.
-
create — Sets IsMeeting on the registration. No other effect via this API (CalendarEventID is always 0).
-
update — ignored