Validate a new user
Runs every create validation on a user body without saving anything.
Runs model validation, the role checks and all business rules of POST /v1/user/create — including the caller's employee privilege and the user-name/initials uniqueness check — then returns 200 with the body null.
Use it to:
- Validate an HR record before submitting it, to show TimeLog's errors early
- Check that the integration's account has the privilege to create users in a department / legal entity
Rules across fields:
- R1: UserRoleIDs must be null or empty unless UserLicenseType is Standard (2).
- R2: StandardHourlyRateID and CostPriceID must belong to LegalEntityID.
Behaviour:
- Idempotent.
- Safe to retry.
- Changes are visible to the next read immediately.
Call first: GET /v1/legal-entity, GET /v1/department
Next: POST /v1/user/create
Not yet documented: examples (success).
curl -X POST "https://app3.timelog.com/your-account/api/v1/user/validate-new-user" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"UserName": "John Doe",
"FirstName": "John Doe",
"LastName": "John Doe",
"Initials": "example_string",
"Email": "user@example.com",
"Title": "example_string",
"EmployeeNo": "example_string",
"EmploymentDate": "2024-12-25T10:00:00Z",
"LegalEntityID": 123,
"DepartmentID": 123,
"ApprovalManagerID": 123,
"EmployeeTypeID": 123,
"StandardHourlyRateID": 123,
"CostPriceID": 123,
"PublicHolidayCalendarID": 123,
"AllowanceLegislationID": 123,
"NormalWorkingTimeID": 123,
"SalaryGroupID": 123,
"UserLicenseType": 42,
"UserRoleIDs": [
"null"
]
}'
import requests
import json
url = "https://app3.timelog.com/your-account/api/v1/user/validate-new-user"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
data = {
"UserName": "John Doe",
"FirstName": "John Doe",
"LastName": "John Doe",
"Initials": "example_string",
"Email": "user@example.com",
"Title": "example_string",
"EmployeeNo": "example_string",
"EmploymentDate": "2024-12-25T10:00:00Z",
"LegalEntityID": 123,
"DepartmentID": 123,
"ApprovalManagerID": 123,
"EmployeeTypeID": 123,
"StandardHourlyRateID": 123,
"CostPriceID": 123,
"PublicHolidayCalendarID": 123,
"AllowanceLegislationID": 123,
"NormalWorkingTimeID": 123,
"SalaryGroupID": 123,
"UserLicenseType": 42,
"UserRoleIDs": [
"null"
]
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const response = await fetch("https://app3.timelog.com/your-account/api/v1/user/validate-new-user", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
},
body: JSON.stringify({
"UserName": "John Doe",
"FirstName": "John Doe",
"LastName": "John Doe",
"Initials": "example_string",
"Email": "user@example.com",
"Title": "example_string",
"EmployeeNo": "example_string",
"EmploymentDate": "2024-12-25T10:00:00Z",
"LegalEntityID": 123,
"DepartmentID": 123,
"ApprovalManagerID": 123,
"EmployeeTypeID": 123,
"StandardHourlyRateID": 123,
"CostPriceID": 123,
"PublicHolidayCalendarID": 123,
"AllowanceLegislationID": 123,
"NormalWorkingTimeID": 123,
"SalaryGroupID": 123,
"UserLicenseType": 42,
"UserRoleIDs": [
"null"
]
})
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
"bytes"
"encoding/json"
)
func main() {
data := []byte(`{
"UserName": "John Doe",
"FirstName": "John Doe",
"LastName": "John Doe",
"Initials": "example_string",
"Email": "user@example.com",
"Title": "example_string",
"EmployeeNo": "example_string",
"EmploymentDate": "2024-12-25T10:00:00Z",
"LegalEntityID": 123,
"DepartmentID": 123,
"ApprovalManagerID": 123,
"EmployeeTypeID": 123,
"StandardHourlyRateID": 123,
"CostPriceID": 123,
"PublicHolidayCalendarID": 123,
"AllowanceLegislationID": 123,
"NormalWorkingTimeID": 123,
"SalaryGroupID": 123,
"UserLicenseType": 42,
"UserRoleIDs": [
"null"
]
}`)
req, err := http.NewRequest("POST", "https://app3.timelog.com/your-account/api/v1/user/validate-new-user", 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/user/validate-new-user')
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 = '{
"UserName": "John Doe",
"FirstName": "John Doe",
"LastName": "John Doe",
"Initials": "example_string",
"Email": "user@example.com",
"Title": "example_string",
"EmployeeNo": "example_string",
"EmploymentDate": "2024-12-25T10:00:00Z",
"LegalEntityID": 123,
"DepartmentID": 123,
"ApprovalManagerID": 123,
"EmployeeTypeID": 123,
"StandardHourlyRateID": 123,
"CostPriceID": 123,
"PublicHolidayCalendarID": 123,
"AllowanceLegislationID": 123,
"NormalWorkingTimeID": 123,
"SalaryGroupID": 123,
"UserLicenseType": 42,
"UserRoleIDs": [
"null"
]
}'
response = http.request(request)
puts response.body
"null"
{
"Code": 200,
"Details": [
"'User Name' must not be empty.",
"'First Name' must not be empty.",
"'Last Name' must not be empty.",
"'Initials' must not be empty.",
"'Email' must not be empty.",
"The specified condition was not met for 'Email'.",
"'Employment Date' must not be empty.",
"'Legal Entity ID' must not be empty.",
"'Department ID' must not be empty.",
"'Standard Hourly Rate ID' must not be empty.",
"'Cost Price ID' must not be empty.",
"'Public Holiday Calendar ID' must not be empty.",
"'Allowance Legislation ID' must not be empty.",
"'Normal Working Time ID' must not be empty."
],
"Message": "Invalid model state error.",
"Parameters": "user: {...}",
"Url": "https://app{server-number}.timelog.com/{account}/api/v1/user/validate-new-user"
}
{
"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": "user: null",
"Url": "https://app{server-number}.timelog.com/{account}/api/v1/user/validate-new-user"
}
/v1/user/validate-new-user
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
Login name of the new user. Not returned by any REST v1 read. Can be set on create only. Alternate key, unique per TimeLog site. Contains personal data. - Same user name or initials as an active user — 20022 (business rule, HTTP 500). - Same user name or initials as an inactive user — 20024 (business rule, HTTP 500). Create never reuses an inactive user's name.
First name. Can be set on create only. Contains personal data.
Last name. Can be set on create only. Contains personal data.
Short handle of the user (see User.Initials). Must not be used by another user (active: 20022, inactive: 20024). Can be set on create only. Alternate key, unique per TimeLog site. Contains personal data.
E-mail address. Can be set on create only. Contains personal data. - Empty or not an e-mail address — 400 Code 200 ('Email' must not be empty / The specified condition was not met for 'Email'). - Passes the model check but fails the business-rule format check — 31114 (business rule, HTTP 500).
Job title. Can be set on create only. Contains personal data.
Employee number. Conditional. Required when the account feature ShowEmployeeID is on (31027, business rule, HTTP 500); optional otherwise. Can be set on create only. Key from your own system, unique per TimeLog site. Uniqueness is not enforced. Contains personal data.
First day of employment. Can be set on create only. Time zone: not yet documented. Contains personal data. - Year not valid — 31029 (business rule, HTTP 500). > Not yet documented: constraints → timezone.
Legal entity the user is employed in.
Can be set on create only.
Refers to LegalEntityRead.LegalEntityID. Look up valid values with GET /v1/legal-entity.
- Feature MultiLegalEntities on and the ID does not exist — 37029 (business rule, HTTP 500).
- Feature MultiLegalEntities off and the ID is not the system legal entity — 37921 (business rule, HTTP 500). Use GET /v1/legal-entity/system.
Valid values come from GET /v1/legal-entity.
Department the user belongs to. Inactive departments are accepted.
Can be set on create only.
Refers to DepartmentRead.DepartmentID. Look up valid values with GET /v1/department.
- The ID does not exist — 37039 (business rule, HTTP 500).
Valid values come from GET /v1/department.
User ID (not employee ID) of the manager who approves this user's time sheets. 0 = no approval manager.
Can be set on create only.
Refers to UserRead.UserID. Look up valid values with GET /v1/user.
- Non-zero and not an existing user — 37038 (business rule, HTTP 500).
Valid values come from GET /v1/user.
Employee type (account-defined category). 0 = none.
Can be set on create only.
Refers to EmployeeTypeRead.EmployeeTypeID.
- Non-zero and not an existing employee type — 37028 (business rule, HTTP 500).
Valid values come from GET /v1/role.
> Not yet documented: values → lookup op.
Standard hourly (sales) rate of the user; must belong to the user's legal entity.
Can be set on create only.
Refers to HourlyRateRead.HourlyRateID. Look up valid values with GET /v1/hourly-rate.
- 0 — 400 'Standard Hourly Rate ID' must not be empty.
- Negative — Replaced by the legal entity's system default hourly rate.
- Positive and not an existing rate / not a rate of LegalEntityID — 37030 / 37031 (business rule, HTTP 500).
GET /v1/hourly-rate returns the price-list entries of ALL legal entities (incl. customer lists); pick an entry with CustomerID null and LegalEntityID = the user's legal entity.
Valid values come from GET /v1/hourly-rate.
Cost price group of the user; must belong to the user's legal entity.
Can be set on create only.
Refers to CostPriceRead.CostPriceID. Look up valid values with GET /v1/cost-price.
- Not an existing cost price / not one of LegalEntityID — 37032 / 37051 (business rule, HTTP 500).
GET /v1/cost-price returns the cost price groups of ALL legal entities; pick one with LegalEntityId = the user's legal entity.
Valid values come from GET /v1/cost-price.
Public-holiday calendar that drives the user's non-working days.
Can be set on create only.
Refers to HolidayCalendarRead.HolidayCalendarID.
- Not an existing calendar — 37033 (business rule, HTTP 500).
> Not yet documented: values → lookup op.
Allowance (per-diem) legislation that applies to the user's travel expenses.
Can be set on create only.
Refers to AllowanceLegislationRead.AllowanceLegislationID. Look up valid values with GET /v1/allowance-legislation.
- Not an existing legislation — 37034 (business rule, HTTP 500).
Valid values come from GET /v1/allowance-legislation.
Normal working time (weekly norm) of the user. Also stored as the user's work week.
Can be set on create only.
Refers to NormalWorkingTimeRead.NormalWorkingTimeID.
- Not an existing normal working time — 37035 (business rule, HTTP 500).
- Account on an edition that allows only the default normal working time — 37923 (business rule, HTTP 500).
> Not yet documented: values → lookup op.
Salary group of the user.
Can be set on create only.
Refers to SalaryGroupRead.SalaryGroupID.
- Feature SalaryGroups on and the group does not exist / is inactive — 37036 / 37925 (business rule, HTTP 500).
- Feature SalaryGroups off and the ID is not the system salary group — 37924 (business rule, HTTP 500).
Model validation only checks NotNull (never fails on an int); omitting it sends 0, which the business rule rejects in both feature states.
> Not yet documented: values → lookup op.
Licence the user is billed for; decides which functionality the user can reach.
Can be set on create only.
Allowed values:
| Value | Name | Meaning |
|---|---|---|
| 2 | Standard | Standard user: access to most functionality, depending on the account's licence model. The only value accepted when neither ExternalUserLicense nor TimeExpenseUserLicense is on. |
| 7 | Limited | Limited user. Accepted only when feature ExternalUserLicense is on; the system role 'Limited user' is assigned and UserRoleIDs must be empty. |
| 8 | TimeExpense | Time and expense tracking with reports. Accepted only when feature TimeExpenseUserLicense is on; the system role 'Time and expense registrations' is assigned and UserRoleIDs must be empty. |
| 1 | External | External user (time tracking only). Rejected by this operation. |
| 3 | Advanced | Advanced user of special licence models. Rejected by this operation. |
| 4 | ExternalPlus | External user with external disbursement. Rejected by this operation. |
| 6 | TlpBasicLimited | Limited user of the Basic contract. Rejected by this operation. |
| 0 | NotSpecified | No licence (no access). Rejected by this operation. |
Additional roles to give the user. Only for Standard (2) users.
Conditional. Allowed only when UserLicenseType is Standard (2); must be null or empty otherwise (400).
Can be set on create only.
Refers to RoleRead.RoleID. Look up valid values with GET /v1/role.
> Not yet documented: multiple → max items.
> Not yet documented: multiple → duplicates allowed.
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
Login name of the new user. Not returned by any REST v1 read.
Can be set on create only.
Alternate key, unique per TimeLog site.
Contains personal data.
-
Same user name or initials as an active user — 20022 (business rule, HTTP 500).
-
Same user name or initials as an inactive user — 20024 (business rule, HTTP 500). Create never reuses an inactive user's name.
First name.
Can be set on create only.
Contains personal data.
Last name.
Can be set on create only.
Contains personal data.
Short handle of the user (see User.Initials). Must not be used by another user (active: 20022, inactive: 20024).
Can be set on create only.
Alternate key, unique per TimeLog site.
Contains personal data.
E-mail address.
Can be set on create only.
Contains personal data.
-
Empty or not an e-mail address — 400 Code 200 ('Email' must not be empty / The specified condition was not met for 'Email').
-
Passes the model check but fails the business-rule format check — 31114 (business rule, HTTP 500).
Job title.
Can be set on create only.
Contains personal data.
Employee number.
Conditional. Required when the account feature ShowEmployeeID is on (31027, business rule, HTTP 500); optional otherwise.
Can be set on create only.
Key from your own system, unique per TimeLog site. Uniqueness is not enforced.
Contains personal data.
First day of employment.
Can be set on create only.
Time zone: not yet documented.
Contains personal data.
- Year not valid — 31029 (business rule, HTTP 500).
Not yet documented: constraints → timezone.
Legal entity the user is employed in.
Can be set on create only.
Refers to LegalEntityRead.LegalEntityID. Look up valid values with GET /v1/legal-entity.
-
Feature MultiLegalEntities on and the ID does not exist — 37029 (business rule, HTTP 500).
-
Feature MultiLegalEntities off and the ID is not the system legal entity — 37921 (business rule, HTTP 500). Use
GET /v1/legal-entity/system.
Valid values come from GET /v1/legal-entity.
Department the user belongs to. Inactive departments are accepted.
Can be set on create only.
Refers to DepartmentRead.DepartmentID. Look up valid values with GET /v1/department.
- The ID does not exist — 37039 (business rule, HTTP 500).
Valid values come from GET /v1/department.
User ID (not employee ID) of the manager who approves this user's time sheets. 0 = no approval manager.
Can be set on create only.
Refers to UserRead.UserID. Look up valid values with GET /v1/user.
- Non-zero and not an existing user — 37038 (business rule, HTTP 500).
Valid values come from GET /v1/user.
Employee type (account-defined category). 0 = none.
Can be set on create only.
Refers to EmployeeTypeRead.EmployeeTypeID.
- Non-zero and not an existing employee type — 37028 (business rule, HTTP 500).
Valid values come from GET /v1/role.
Not yet documented: values → lookup op.
Standard hourly (sales) rate of the user; must belong to the user's legal entity.
Can be set on create only.
Refers to HourlyRateRead.HourlyRateID. Look up valid values with GET /v1/hourly-rate.
-
0 — 400 'Standard Hourly Rate ID' must not be empty.
-
Negative — Replaced by the legal entity's system default hourly rate.
-
Positive and not an existing rate / not a rate of LegalEntityID — 37030 / 37031 (business rule, HTTP 500).
GET /v1/hourly-rate returns the price-list entries of ALL legal entities (incl. customer lists); pick an entry with CustomerID null and LegalEntityID = the user's legal entity.
Valid values come from GET /v1/hourly-rate.
Cost price group of the user; must belong to the user's legal entity.
Can be set on create only.
Refers to CostPriceRead.CostPriceID. Look up valid values with GET /v1/cost-price.
- Not an existing cost price / not one of LegalEntityID — 37032 / 37051 (business rule, HTTP 500).
GET /v1/cost-price returns the cost price groups of ALL legal entities; pick one with LegalEntityId = the user's legal entity.
Valid values come from GET /v1/cost-price.
Public-holiday calendar that drives the user's non-working days.
Can be set on create only.
Refers to HolidayCalendarRead.HolidayCalendarID.
- Not an existing calendar — 37033 (business rule, HTTP 500).
Not yet documented: values → lookup op.
Allowance (per-diem) legislation that applies to the user's travel expenses.
Can be set on create only.
Refers to AllowanceLegislationRead.AllowanceLegislationID. Look up valid values with GET /v1/allowance-legislation.
- Not an existing legislation — 37034 (business rule, HTTP 500).
Valid values come from GET /v1/allowance-legislation.
Normal working time (weekly norm) of the user. Also stored as the user's work week.
Can be set on create only.
Refers to NormalWorkingTimeRead.NormalWorkingTimeID.
-
Not an existing normal working time — 37035 (business rule, HTTP 500).
-
Account on an edition that allows only the default normal working time — 37923 (business rule, HTTP 500).
Not yet documented: values → lookup op.
Salary group of the user.
Can be set on create only.
Refers to SalaryGroupRead.SalaryGroupID.
-
Feature SalaryGroups on and the group does not exist / is inactive — 37036 / 37925 (business rule, HTTP 500).
-
Feature SalaryGroups off and the ID is not the system salary group — 37924 (business rule, HTTP 500).
Model validation only checks NotNull (never fails on an int); omitting it sends 0, which the business rule rejects in both feature states.
Not yet documented: values → lookup op.
Licence the user is billed for; decides which functionality the user can reach.
Can be set on create only.
Allowed values:
| Value | Name | Meaning |
|---|---|---|
2 | Standard | Standard user: access to most functionality, depending on the account's licence model. The only value accepted when neither ExternalUserLicense nor TimeExpenseUserLicense is on. |
7 | Limited | Limited user. Accepted only when feature ExternalUserLicense is on; the system role 'Limited user' is assigned and UserRoleIDs must be empty. |
8 | TimeExpense | Time and expense tracking with reports. Accepted only when feature TimeExpenseUserLicense is on; the system role 'Time and expense registrations' is assigned and UserRoleIDs must be empty. |
1 | External | External user (time tracking only). Rejected by this operation. |
3 | Advanced | Advanced user of special licence models. Rejected by this operation. |
4 | ExternalPlus | External user with external disbursement. Rejected by this operation. |
6 | TlpBasicLimited | Limited user of the Basic contract. Rejected by this operation. |
0 | NotSpecified | No licence (no access). Rejected by this operation. |
27813460Additional roles to give the user. Only for Standard (2) users.
Conditional. Allowed only when UserLicenseType is Standard (2); must be null or empty otherwise (400).
Can be set on create only.
Refers to RoleRead.RoleID. Look up valid values with GET /v1/role.
Not yet documented: multiple → max items.
Not yet documented: multiple → duplicates allowed.
Responses
Success without content: HTTP 200 with the literal JSON body null. Literal JSON null — the body is valid.