Give an employee several competences
Adds several competences with values to one employee in one call; competences the employee already has are skipped.
Items whose competence the employee already has are skipped silently — their stored value is NOT changed and the response does not report them. The response is the literal null: no IDs come back.
Use it to:
- Initial load of an employee's skills profile from an HR system
Not for:
- Changing existing values — skipped here; use
PUT /v2/employee-competences - Several employees in one call — one call per employee
Rules across fields:
- R1: Each CompetenceId may appear only once in EmployeeCompetences — a repeat makes the single INSERT violate the unique constraint and nothing is inserted (500 Code 100).
Behaviour:
- Idempotent.
- A repeat of the same body inserts nothing new (existing pairs are skipped) and answers 200 null. Values sent the second time are NOT applied to existing pairs.
- Safe to retry.
- Duplicates: Pairs that already exist are skipped (NOT EXISTS); duplicates inside one request fail the whole batch.
- Side effect: Raises CompetenceValueBatchCreated once, without a domain object (no per-item events) — even when nothing was inserted.
- Changes are visible to the next read immediately.
- Batch: Every check runs before the single INSERT ... Not partial in the error sense — but items for existing pairs are silently skipped, so 'success' does not mean every item was written.
Call first: GET /v1/user, GET /v2/employee-competences/available
Next: GET /v2/employee-competences
See also: POST /v2/employee-competences
Not yet documented: errors; examples.
curl -X POST "https://app3.timelog.com/your-account/api/v2/employee-competences/bulk" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"UserId": 123,
"EmployeeCompetences": [
{
"CompetenceId": 123,
"Value": 42
}
]
}'
import requests
import json
url = "https://app3.timelog.com/your-account/api/v2/employee-competences/bulk"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
data = {
"UserId": 123,
"EmployeeCompetences": [
{
"CompetenceId": 123,
"Value": 42
}
]
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const response = await fetch("https://app3.timelog.com/your-account/api/v2/employee-competences/bulk", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
},
body: JSON.stringify({
"UserId": 123,
"EmployeeCompetences": [
{
"CompetenceId": 123,
"Value": 42
}
]
})
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
"bytes"
"encoding/json"
)
func main() {
data := []byte(`{
"UserId": 123,
"EmployeeCompetences": [
{
"CompetenceId": 123,
"Value": 42
}
]
}`)
req, err := http.NewRequest("POST", "https://app3.timelog.com/your-account/api/v2/employee-competences/bulk", 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/v2/employee-competences/bulk')
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,
"EmployeeCompetences": [
{
"CompetenceId": 123,
"Value": 42
}
]
}'
response = http.request(request)
puts response.body
"null"
{
"Code": 200,
"Details": [
""
],
"Message": "Invalid model state error.",
"Parameters": "model: {"UserId":0,"EmployeeCompetences":null}",
"Url": "https://app{server-number}.timelog.com/{account}/api/v2/employee-competences/bulk"
}
{
"error": "Unauthorized",
"message": "Authentication required. Please provide a valid API token",
"code": 401
}
{
"error": "Internal Server Error",
"message": "An unexpected error occurred on the server",
"code": 500,
"requestId": "req_1234567890"
}
/v2/employee-competences/bulk
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
UserID of the employee who gets every competence in the list.
Can be set on create only.
Refers to UserRead.UserID. Look up valid values with GET /v1/user.
- Absent from the JSON — 400 Code 200, Details [""] (captured).
- 0 or not an existing user — 500 Code 102 / 37040.
- User deactivated — 500 Code 102 / 37931.
- Caller has no Employee skills privilege covering this employee — 500 Code 102 / 38015 — nothing is written.
The competences to give, one item per competence. Can be set on create only. - An item whose competence the employee already has — Silently skipped by the insert (NOT EXISTS); the stored value is NOT updated; the response does not say which items were skipped. - The same CompetenceId twice in the array — Both rows pass the NOT EXISTS check and the second violates the unique constraint → the single INSERT fails → nothing inserted, 500 Code 100. > Not yet documented: behaviour.
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
UserID of the employee who gets every competence in the list.
Can be set on create only.
Refers to UserRead.UserID. Look up valid values with GET /v1/user.
-
Absent from the JSON — 400 Code 200, Details [""] (captured).
-
0 or not an existing user — 500 Code 102 / 37040.
-
User deactivated — 500 Code 102 / 37931.
-
Caller has no Employee skills privilege covering this employee — 500 Code 102 / 38015 — nothing is written.
The competences to give, one item per competence.
Can be set on create only.
-
An item whose competence the employee already has — Silently skipped by the insert (NOT EXISTS); the stored value is NOT updated; the response does not say which items were skipped.
-
The same CompetenceId twice in the array — Both rows pass the NOT EXISTS check and the second violates the unique constraint → the single INSERT fails → nothing inserted, 500 Code 100.
Not yet documented: behaviour.
Responses
*Success without content: HTTP 200 with the literal JSON body null.
Order: none
Empty result: none*