Create a contact person
Creates a contact person, optionally on a customer and with an owner, and returns it.
Creates a row in the account's contact list. The contact can then be used as a customer contact, project contact or invoice recipient (ContactID on CustomerCreate / ProjectCreate). Only FirstName, LastName and a valid CountryID are required; without CustomerID the contact belongs to no customer. The response is the stored contact (ContactRead); its ShownName is the contact's own full name. The controller ignores the manager's result and re-reads contact 0, which fails with a generic 500 (Code 100).
Use it to:
- Create the contact persons of a customer synchronised from a CRM
Not for:
- Updating or deleting contacts — REST v1 has no contact update / delete operation.
Behaviour:
- Not idempotent.
- Not safe to retry blindly.
- Duplicates: Not deduplicated — the same names on the same customer create a second contact.
- Side effect: Inserts the contact, stamped with the caller as creator.
- Changes are visible to the next read immediately.
Call first: GET /v1/customer, GET /v1/country/get-all, GET /v1/user
Next: POST /v1/customer/create, POST /v1/project/create-from-template
See also: POST /v1/contact/validate-new-contact, GET /v1/contact
Not yet documented: examples (success).
curl -X POST "https://app3.timelog.com/your-account/api/v1/contact/create" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"FirstName": "John Doe",
"LastName": "John Doe",
"CountryID": 123,
"CustomerID": 123,
"OwnerID": 123,
"Phone": "+1-555-0123",
"PrivatePhone": "+1-555-0123",
"Mobile": "example_string",
"Fax": "example_string",
"Email": "user@example.com",
"Department": "example_string",
"JobTitle": "example_string",
"Address": "123 Main St",
"Address2": "123 Main St",
"Address3": "123 Main St",
"ZipCode": "example_string",
"City": "New York",
"State": "example_string",
"Salutation": "example_string",
"Initials": "example_string",
"ProfessionalTitle1": "example_string",
"ProfessionalTitle2": "example_string",
"Comment": "example_string",
"Birthday": "null"
}'
import requests
import json
url = "https://app3.timelog.com/your-account/api/v1/contact/create"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
data = {
"FirstName": "John Doe",
"LastName": "John Doe",
"CountryID": 123,
"CustomerID": 123,
"OwnerID": 123,
"Phone": "+1-555-0123",
"PrivatePhone": "+1-555-0123",
"Mobile": "example_string",
"Fax": "example_string",
"Email": "user@example.com",
"Department": "example_string",
"JobTitle": "example_string",
"Address": "123 Main St",
"Address2": "123 Main St",
"Address3": "123 Main St",
"ZipCode": "example_string",
"City": "New York",
"State": "example_string",
"Salutation": "example_string",
"Initials": "example_string",
"ProfessionalTitle1": "example_string",
"ProfessionalTitle2": "example_string",
"Comment": "example_string",
"Birthday": "null"
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const response = await fetch("https://app3.timelog.com/your-account/api/v1/contact/create", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
},
body: JSON.stringify({
"FirstName": "John Doe",
"LastName": "John Doe",
"CountryID": 123,
"CustomerID": 123,
"OwnerID": 123,
"Phone": "+1-555-0123",
"PrivatePhone": "+1-555-0123",
"Mobile": "example_string",
"Fax": "example_string",
"Email": "user@example.com",
"Department": "example_string",
"JobTitle": "example_string",
"Address": "123 Main St",
"Address2": "123 Main St",
"Address3": "123 Main St",
"ZipCode": "example_string",
"City": "New York",
"State": "example_string",
"Salutation": "example_string",
"Initials": "example_string",
"ProfessionalTitle1": "example_string",
"ProfessionalTitle2": "example_string",
"Comment": "example_string",
"Birthday": "null"
})
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
"bytes"
"encoding/json"
)
func main() {
data := []byte(`{
"FirstName": "John Doe",
"LastName": "John Doe",
"CountryID": 123,
"CustomerID": 123,
"OwnerID": 123,
"Phone": "+1-555-0123",
"PrivatePhone": "+1-555-0123",
"Mobile": "example_string",
"Fax": "example_string",
"Email": "user@example.com",
"Department": "example_string",
"JobTitle": "example_string",
"Address": "123 Main St",
"Address2": "123 Main St",
"Address3": "123 Main St",
"ZipCode": "example_string",
"City": "New York",
"State": "example_string",
"Salutation": "example_string",
"Initials": "example_string",
"ProfessionalTitle1": "example_string",
"ProfessionalTitle2": "example_string",
"Comment": "example_string",
"Birthday": "null"
}`)
req, err := http.NewRequest("POST", "https://app3.timelog.com/your-account/api/v1/contact/create", 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/contact/create')
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 = '{
"FirstName": "John Doe",
"LastName": "John Doe",
"CountryID": 123,
"CustomerID": 123,
"OwnerID": 123,
"Phone": "+1-555-0123",
"PrivatePhone": "+1-555-0123",
"Mobile": "example_string",
"Fax": "example_string",
"Email": "user@example.com",
"Department": "example_string",
"JobTitle": "example_string",
"Address": "123 Main St",
"Address2": "123 Main St",
"Address3": "123 Main St",
"ZipCode": "example_string",
"City": "New York",
"State": "example_string",
"Salutation": "example_string",
"Initials": "example_string",
"ProfessionalTitle1": "example_string",
"ProfessionalTitle2": "example_string",
"Comment": "example_string",
"Birthday": "null"
}'
response = http.request(request)
puts response.body
{
"Properties": {
"ID": 123,
"GUID": "123e4567-e89b-12d3-a456-426614174000",
"FirstName": "null",
"LastName": "null",
"Phone": "null",
"Fax": "null",
"Email": "null",
"Department": "null",
"Address": "null",
"Address2": "null",
"Address3": "null",
"ZipCode": "null",
"City": "null",
"State": "null",
"CountryID": 123,
"CustomerID": 123,
"Initials": "example_string",
"PrivatePhone": "null",
"Mobile": "null",
"JobTitle": "null",
"ProfessionalTitle1": "example_string",
"ProfessionalTitle2": "example_string",
"Salutation": "example_string",
"ShownName": "null"
},
"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": "Internal Server Error",
"message": "An unexpected error occurred on the server",
"code": 500,
"requestId": "req_1234567890"
}
/v1/contact/create
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
First name of the contact. Can be set on create only. Contains personal data. - null, empty or whitespace — 400 "'First Name' must not be empty."
Last name of the contact. Can be set on create only. Contains personal data. - null, empty or whitespace — 400 "'Last Name' must not be empty."
Country of the contact's address.
Can be set on create only.
Refers to CountryRead.CountryID. Look up valid values with GET /v1/country/get-all.
- 0, negative or omitted — 400 "'Country ID' must be greater than 0."
- positive but unknown — 500 Code 102, ErrorCode 30017 (rest.crm.country-unknown).
Valid values come from GET /v1/country/get-all.
The customer the contact belongs to.
Can be set on create only.
Refers to CustomerRead.CustomerID. Look up valid values with GET /v1/customer.
- 0 or omitted — The contact is created WITHOUT a customer (KundeId 0); CustomerID 0 in the response.
- The business rule CustomerExist never runs — it is gated on a change flag that is never set for CustomerID.
Valid values come from GET /v1/customer.
UserID of the TimeLog user who owns the contact. It also decides access for callers whose customer privilege is "customers where I am account manager": such a caller may create a contact only when they name themselves as owner.
Refers to UserRead.UserID. Look up valid values with GET /v1/user.
- 0 or omitted — No owner.
- unknown UserID — The lookup exception is swallowed and the contact is created without an owner — no error.
Valid values come from GET /v1/user.
Work phone. Can be set on create only. Contains personal data.
Private phone. Can be set on create only. Contains personal data.
Mobile phone. Can be set on create only. Contains personal data.
Fax number. Can be set on create only.
E-mail address. No format validation. Can be set on create only. Contains personal data.
Free-text department of the contact at the customer. Can be set on create only.
Job title (stored as Stillingsbetegnelse). Can be set on create only.
Address line 1. Can be set on create only. Contains personal data.
Address line 2. Can be set on create only. Contains personal data.
Accepted and discarded — contacts have no third address line. Can be set on create only.
Postal code. Can be set on create only. Contains personal data.
City. Can be set on create only. Contains personal data.
State / region. Can be set on create only.
Salutation / prefix (e.g. a courtesy title). Can be set on create only.
Initials. Longer values are silently truncated to 10. Can be set on create only. Contains personal data.
First professional title. Longer values are silently truncated to 20. Can be set on create only.
Second professional title. Longer values are silently truncated to 20. Can be set on create only.
Free-text comment on the contact.
Birthday of the contact. Time zone: not yet documented. Contains personal data. > 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
First name of the contact.
Can be set on create only.
Contains personal data.
- null, empty or whitespace — 400 "'First Name' must not be empty."
Last name of the contact.
Can be set on create only.
Contains personal data.
- null, empty or whitespace — 400 "'Last Name' must not be empty."
Country of the contact's address.
Can be set on create only.
Refers to CountryRead.CountryID. Look up valid values with GET /v1/country/get-all.
-
0, negative or omitted — 400 "'Country ID' must be greater than 0."
-
positive but unknown — 500 Code 102, ErrorCode 30017 (rest.crm.country-unknown).
Valid values come from GET /v1/country/get-all.
The customer the contact belongs to.
Can be set on create only.
Refers to CustomerRead.CustomerID. Look up valid values with GET /v1/customer.
-
0 or omitted — The contact is created WITHOUT a customer (KundeId 0); CustomerID 0 in the response.
-
The business rule CustomerExist never runs — it is gated on a change flag that is never set for CustomerID.
Valid values come from GET /v1/customer.
UserID of the TimeLog user who owns the contact. It also decides access for callers whose customer privilege is "customers where I am account manager": such a caller may create a contact only when they name themselves as owner.
Refers to UserRead.UserID. Look up valid values with GET /v1/user.
-
0 or omitted — No owner.
-
unknown UserID — The lookup exception is swallowed and the contact is created without an owner — no error.
Valid values come from GET /v1/user.
Work phone.
Can be set on create only.
Contains personal data.
Private phone.
Can be set on create only.
Contains personal data.
Mobile phone.
Can be set on create only.
Contains personal data.
Fax number.
Can be set on create only.
E-mail address. No format validation.
Can be set on create only.
Contains personal data.
Free-text department of the contact at the customer.
Can be set on create only.
Job title (stored as Stillingsbetegnelse).
Can be set on create only.
Address line 1.
Can be set on create only.
Contains personal data.
Address line 2.
Can be set on create only.
Contains personal data.
Accepted and discarded — contacts have no third address line.
Can be set on create only.
Postal code.
Can be set on create only.
Contains personal data.
City.
Can be set on create only.
Contains personal data.
State / region.
Can be set on create only.
Salutation / prefix (e.g. a courtesy title).
Can be set on create only.
Initials. Longer values are silently truncated to 10.
Can be set on create only.
Contains personal data.
First professional title. Longer values are silently truncated to 20.
Can be set on create only.
Second professional title. Longer values are silently truncated to 20.
Can be set on create only.
Free-text comment on the contact.
Birthday of the contact.
Time zone: not yet documented.
Contains personal data.
Not yet documented: constraints → timezone.
Responses
A contact person — a named person at a customer that projects and invoices can point to as contact or invoice recipient. Personal data: name, phone numbers, e-mail and address of a private individual.