Create a customer
Creates a customer together with its invoice settings and returns it.
Creates a customer row and its invoice settings in one call. The response is the created customer in the short read representation (CustomerRead), including the new CustomerID. Address, owners, currency, industry and invoice settings are stored but cannot be read back through the REST v1 API.
Use it to:
- Create customers in TimeLog from a CRM or ERP master record
Not for:
- Dry-run validation →
POST /v1/customer/validate-new-customer
Rules across fields:
- R1: UseInvoicingAddress true → InvoicingAddressCountryID required; invoicing-address fields are ignored when it is false.
- R2: UseEanNo true → OIOXML feature on (37697), Address + ZipCode + City set (60063), VatNo set (60064).
- R3: Name + CustomerNo must not equal an existing customer's pair (case-insensitive).
Behaviour:
- Not idempotent.
- Safe to retry only under a condition. A retry with the same Name and CustomerNo fails with 38017 when the first attempt created the customer (and the caller can see it). With an empty CustomerNo and a number series active, each attempt gets a new number, so the pair differs and a duplicate is created.
- Duplicates: Only the exact Name + CustomerNo pair is rejected (and the number alone when UniqueCustomerNumber is on). No external-key deduplication.
- Side effect: Inserts the customer (CustomerGUID generated server-side) and its invoice settings.
- Side effect: Assigns the next customer number from the series when CustomerNo is blank and the series is active.
- On failure: If saving the invoice settings fails after the customer row is inserted, the customer exists without invoice settings and the call still returns 500.
- Changes are visible to the next read immediately.
Call first: GET /v1/customerstatus, GET /v1/industry/get-all
Next: GET /v1/customer/{customerID}, PUT /v1/customer/update
See also: POST /v1/customer/validate-new-customer
Not yet documented: examples (success).
curl -X POST "https://app3.timelog.com/your-account/api/v1/customer/create" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"Name": "John Doe",
"CurrencyID": 123,
"CustomerStatusID": 123,
"CountryID": 123,
"CustomerNo": "example_string",
"NickName": "John Doe",
"PrimaryKAMID": 123,
"SecondaryKAMID": 123,
"CustomerSince": "2024-12-25T10:00:00Z",
"IndustryID": 123,
"Phone": "+1-555-0123",
"Fax": "example_string",
"Email": "user@example.com",
"Website": "example_string",
"Address": "123 Main St",
"Address2": "123 Main St",
"Address3": "123 Main St",
"ZipCode": "example_string",
"City": "New York",
"State": "example_string",
"UseInvoicingAddress": true,
"InvoicingAddressCountryID": 123,
"InvoicingAddress": "123 Main St",
"InvoicingAddress2": "123 Main St",
"InvoicingAddress3": "123 Main St",
"InvoicingAddressZipCode": "123 Main St",
"InvoicingAddressCity": "123 Main St",
"InvoicingAddressState": "123 Main St",
"VatNo": "example_string",
"OrganizationNo": "example_string",
"DefaultMileageDistance": 25,
"ExpenseIsBillable": true,
"MileageIsBillable": true,
"DefaultDistIsMaxBillable": true,
"ContactID": 123,
"InvoiceAddressToUse": 42,
"InternalReferenceID": 123,
"CustomerReferenceID": 123,
"PaymentTermID": 123,
"DiscountPercentage": 3.14,
"CalculateVat": true,
"VatPercentage": 3.14,
"UseEanNo": true,
"EanNo": "example_string",
"LanguageID": 123
}'
import requests
import json
url = "https://app3.timelog.com/your-account/api/v1/customer/create"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
data = {
"Name": "John Doe",
"CurrencyID": 123,
"CustomerStatusID": 123,
"CountryID": 123,
"CustomerNo": "example_string",
"NickName": "John Doe",
"PrimaryKAMID": 123,
"SecondaryKAMID": 123,
"CustomerSince": "2024-12-25T10:00:00Z",
"IndustryID": 123,
"Phone": "+1-555-0123",
"Fax": "example_string",
"Email": "user@example.com",
"Website": "example_string",
"Address": "123 Main St",
"Address2": "123 Main St",
"Address3": "123 Main St",
"ZipCode": "example_string",
"City": "New York",
"State": "example_string",
"UseInvoicingAddress": true,
"InvoicingAddressCountryID": 123,
"InvoicingAddress": "123 Main St",
"InvoicingAddress2": "123 Main St",
"InvoicingAddress3": "123 Main St",
"InvoicingAddressZipCode": "123 Main St",
"InvoicingAddressCity": "123 Main St",
"InvoicingAddressState": "123 Main St",
"VatNo": "example_string",
"OrganizationNo": "example_string",
"DefaultMileageDistance": 25,
"ExpenseIsBillable": true,
"MileageIsBillable": true,
"DefaultDistIsMaxBillable": true,
"ContactID": 123,
"InvoiceAddressToUse": 42,
"InternalReferenceID": 123,
"CustomerReferenceID": 123,
"PaymentTermID": 123,
"DiscountPercentage": 3.14,
"CalculateVat": true,
"VatPercentage": 3.14,
"UseEanNo": true,
"EanNo": "example_string",
"LanguageID": 123
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const response = await fetch("https://app3.timelog.com/your-account/api/v1/customer/create", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
},
body: JSON.stringify({
"Name": "John Doe",
"CurrencyID": 123,
"CustomerStatusID": 123,
"CountryID": 123,
"CustomerNo": "example_string",
"NickName": "John Doe",
"PrimaryKAMID": 123,
"SecondaryKAMID": 123,
"CustomerSince": "2024-12-25T10:00:00Z",
"IndustryID": 123,
"Phone": "+1-555-0123",
"Fax": "example_string",
"Email": "user@example.com",
"Website": "example_string",
"Address": "123 Main St",
"Address2": "123 Main St",
"Address3": "123 Main St",
"ZipCode": "example_string",
"City": "New York",
"State": "example_string",
"UseInvoicingAddress": true,
"InvoicingAddressCountryID": 123,
"InvoicingAddress": "123 Main St",
"InvoicingAddress2": "123 Main St",
"InvoicingAddress3": "123 Main St",
"InvoicingAddressZipCode": "123 Main St",
"InvoicingAddressCity": "123 Main St",
"InvoicingAddressState": "123 Main St",
"VatNo": "example_string",
"OrganizationNo": "example_string",
"DefaultMileageDistance": 25,
"ExpenseIsBillable": true,
"MileageIsBillable": true,
"DefaultDistIsMaxBillable": true,
"ContactID": 123,
"InvoiceAddressToUse": 42,
"InternalReferenceID": 123,
"CustomerReferenceID": 123,
"PaymentTermID": 123,
"DiscountPercentage": 3.14,
"CalculateVat": true,
"VatPercentage": 3.14,
"UseEanNo": true,
"EanNo": "example_string",
"LanguageID": 123
})
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
"bytes"
"encoding/json"
)
func main() {
data := []byte(`{
"Name": "John Doe",
"CurrencyID": 123,
"CustomerStatusID": 123,
"CountryID": 123,
"CustomerNo": "example_string",
"NickName": "John Doe",
"PrimaryKAMID": 123,
"SecondaryKAMID": 123,
"CustomerSince": "2024-12-25T10:00:00Z",
"IndustryID": 123,
"Phone": "+1-555-0123",
"Fax": "example_string",
"Email": "user@example.com",
"Website": "example_string",
"Address": "123 Main St",
"Address2": "123 Main St",
"Address3": "123 Main St",
"ZipCode": "example_string",
"City": "New York",
"State": "example_string",
"UseInvoicingAddress": true,
"InvoicingAddressCountryID": 123,
"InvoicingAddress": "123 Main St",
"InvoicingAddress2": "123 Main St",
"InvoicingAddress3": "123 Main St",
"InvoicingAddressZipCode": "123 Main St",
"InvoicingAddressCity": "123 Main St",
"InvoicingAddressState": "123 Main St",
"VatNo": "example_string",
"OrganizationNo": "example_string",
"DefaultMileageDistance": 25,
"ExpenseIsBillable": true,
"MileageIsBillable": true,
"DefaultDistIsMaxBillable": true,
"ContactID": 123,
"InvoiceAddressToUse": 42,
"InternalReferenceID": 123,
"CustomerReferenceID": 123,
"PaymentTermID": 123,
"DiscountPercentage": 3.14,
"CalculateVat": true,
"VatPercentage": 3.14,
"UseEanNo": true,
"EanNo": "example_string",
"LanguageID": 123
}`)
req, err := http.NewRequest("POST", "https://app3.timelog.com/your-account/api/v1/customer/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/customer/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 = '{
"Name": "John Doe",
"CurrencyID": 123,
"CustomerStatusID": 123,
"CountryID": 123,
"CustomerNo": "example_string",
"NickName": "John Doe",
"PrimaryKAMID": 123,
"SecondaryKAMID": 123,
"CustomerSince": "2024-12-25T10:00:00Z",
"IndustryID": 123,
"Phone": "+1-555-0123",
"Fax": "example_string",
"Email": "user@example.com",
"Website": "example_string",
"Address": "123 Main St",
"Address2": "123 Main St",
"Address3": "123 Main St",
"ZipCode": "example_string",
"City": "New York",
"State": "example_string",
"UseInvoicingAddress": true,
"InvoicingAddressCountryID": 123,
"InvoicingAddress": "123 Main St",
"InvoicingAddress2": "123 Main St",
"InvoicingAddress3": "123 Main St",
"InvoicingAddressZipCode": "123 Main St",
"InvoicingAddressCity": "123 Main St",
"InvoicingAddressState": "123 Main St",
"VatNo": "example_string",
"OrganizationNo": "example_string",
"DefaultMileageDistance": 25,
"ExpenseIsBillable": true,
"MileageIsBillable": true,
"DefaultDistIsMaxBillable": true,
"ContactID": 123,
"InvoiceAddressToUse": 42,
"InternalReferenceID": 123,
"CustomerReferenceID": 123,
"PaymentTermID": 123,
"DiscountPercentage": 3.14,
"CalculateVat": true,
"VatPercentage": 3.14,
"UseEanNo": true,
"EanNo": "example_string",
"LanguageID": 123
}'
response = http.request(request)
puts response.body
{
"Properties": {
"CustomerID": 123,
"ID": "123e4567-e89b-12d3-a456-426614174000",
"Name": "John Doe",
"No": "example_string",
"DefaultMileageDistance": 25,
"CustomerStatusID": 123,
"VATNo": "example_string",
"OrganizationNo": "example_string"
},
"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": "Internal Server Error",
"message": "An unexpected error occurred on the server",
"code": 500,
"requestId": "req_1234567890"
}
/v1/customer/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
Company name. With CustomerNo, must not match an existing customer (case-insensitive name + number pair, 38017). Over 255 characters is not rejected by validation (server outcome unknown). > Not yet documented: notes.
The customer's currency.
Refers to CurrencyRead.CurrencyID. Look up valid values with GET /v1/currency/{status}.
0 is rejected by validation (400). Whether a non-existent ID is caught on create is unknown: the CurrencyExist rule runs only when the currency property is flagged as changed.
> Not yet documented: notes.
The customer's status.
Refers to CustomerStatusRead.CustomerStatusID. Look up valid values with GET /v1/customerstatus.
- ID does not exist — Business rule 30018 (HTTP 500, Code 102), raised before any other check.
Valid values come from GET /v1/customerstatus.
Country of the customer's address.
Refers to CountryRead.CountryID. Look up valid values with GET /v1/country/get-all.
Customer number. Alternate key, unique per TimeLog site. Default: Next number of the customer-number series when that series is active; otherwise empty. - Feature UniqueCustomerNumber is on and the number is already used — 30169 'Customer number is already in use' — returned as HTTP 403 Privilege exception. - Customer-number series (AutoNumberTypes.CustomerNo) active and the value is blank — Assigned from the series.
Short name. Matched by the searchText of the search-for-* operations. Display of the field in TAF actions depends on feature CustomerNickName; the API stores it either way.
Primary owner (key account manager), given as a USER ID (not an employee ID).
Refers to UserRead.UserID.
- Sent as 0 or negative — Validation (400): must be greater than 0 when present.
- User does not exist — 37040 (HTTP 500, Code 102).
- User is inactive — 37931 (HTTP 500, Code 102).
The caller's customer privilege is evaluated against the owner set here: with only the "customers where I am account manager" privilege, create succeeds only when the caller is the primary owner. Owner when omitted: unknown.
> Not yet documented: notes.
Secondary owner, given as a USER ID.
Refers to UserRead.UserID.
- 0 or negative — Validation (400).
- User does not exist / inactive — 37040 / 37931 (HTTP 500, Code 102).
TAF display depends on feature TwoOwnersPerCustomer; the API stores it either way.
Date the customer relationship started. Time zone: not yet documented. Default: Server's current date and time (DateTime.Now) when omitted or invalid. > Not yet documented: constraints → timezone.
Industry classification.
Refers to IndustryRead.IndustryID. Look up valid values with GET /v1/industry/get-all.
- 0 or negative — Validation (400).
- ID does not exist — 30019 (HTTP 500, Code 102). Inactive industries are accepted.
Valid values come from GET /v1/industry/get-all.
Validated as an e-mail address when not blank (400).
Whether the customer has a separate invoicing address.
Country of the invoicing address.
Conditional. Required (non-zero) when UseInvoicingAddress is true; ignored otherwise.
Refers to CountryRead.CountryID. Look up valid values with GET /v1/country/get-all.
Conditional. Stored only when UseInvoicingAddress is true; ignored otherwise.
Conditional. Stored only when UseInvoicingAddress is true; ignored otherwise.
Conditional. Stored only when UseInvoicingAddress is true; ignored otherwise.
Conditional. Stored only when UseInvoicingAddress is true; ignored otherwise.
Conditional. Stored only when UseInvoicingAddress is true; ignored otherwise.
Conditional. Stored only when UseInvoicingAddress is true; ignored otherwise.
VAT number. Returned as VATNo.
Company registration number.
> Not yet documented: constraints → unit.
The default mileage distance is also the maximum billable distance.
Invoice contact person.
Refers to ContactRead.ContactID.
-1 = the project's contact; 0 = choose when invoicing; > 0 = a contact ID (must exist: 37011).
Which address invoices go to.
Allowed values:
| Value | Name | Meaning |
|---|---|---|
| 0 | NotSpecified | Not specified. Accepted by validation; downstream effect not established. |
| 1 | CustomersAddress | Invoice to the customer's address. |
| 2 | ContactsAddress | Invoice to the invoice contact's address. |
| 4 | CustomerInvoicingAddress | Invoice to the customer's separate invoicing address. |
> Not yet documented: values → members → meaning.
Internal reference on invoices.
Refers to EmployeeRead.EmployeeID.
-3 = project's account manager; -2 = project manager; -1 = customer's owner; 0 = choose when invoicing; > 0 = an EMPLOYEE ID (must exist: 30014).
Customer reference (contact) on invoices.
Refers to ContactRead.ContactID.
-1 = project's contact; 0 = choose when invoicing; > 0 = a contact ID (must exist: 37011).
Payment term.
Refers to PaymentTermRead.ID. Look up valid values with GET /v1/payment-term.
> 0 must exist (37022). Meaning of 0 (omitted) not established.
Valid values come from GET /v1/payment-term.
> Not yet documented: notes.
Unit: percent No range check in the API layer.
Unit: percent No range check in the API layer.
Invoice this customer electronically with an EAN number (OIOXML). - true and feature OIOXML is off — 37697 (HTTP 500, Code 102). - true and Address, ZipCode or City is blank — 60063. - true, address complete, VatNo blank — 60064.
EAN number: exactly 13 characters, or empty (any other length fails validation, 400).
Invoice language.
- Any other value — Validation (400), even when the feature below is off.
- Feature InvoiceMultilingual is off — Validated, then not stored.
Allowed values:
| Value | Name | Meaning |
|---|---|---|
| 1 | Danish | Invoices in Danish. |
| 2 | English | Invoices in English. |
| 4 | Swedish | Invoices in Swedish. |
| 15 | Norwegian | Invoices in Norwegian. |
| 16 | German | Invoices in German. |
| 17 | Dutch | Invoices in Dutch. |
| 18 | French | Invoices in French. |
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
Company name.
With CustomerNo, must not match an existing customer (case-insensitive name + number pair, 38017). Over 255 characters is not rejected by validation (server outcome unknown).
Not yet documented: notes.
The customer's currency.
Refers to CurrencyRead.CurrencyID. Look up valid values with GET /v1/currency/{status}.
0 is rejected by validation (400). Whether a non-existent ID is caught on create is unknown: the CurrencyExist rule runs only when the currency property is flagged as changed.
Not yet documented: notes.
The customer's status.
Refers to CustomerStatusRead.CustomerStatusID. Look up valid values with GET /v1/customerstatus.
- ID does not exist — Business rule 30018 (HTTP 500, Code 102), raised before any other check.
Valid values come from GET /v1/customerstatus.
Country of the customer's address.
Refers to CountryRead.CountryID. Look up valid values with GET /v1/country/get-all.
Customer number.
Alternate key, unique per TimeLog site.
Default: Next number of the customer-number series when that series is active; otherwise empty.
-
Feature UniqueCustomerNumber is on and the number is already used — 30169 'Customer number is already in use' — returned as HTTP 403 Privilege exception.
-
Customer-number series (AutoNumberTypes.CustomerNo) active and the value is blank — Assigned from the series.
Short name. Matched by the searchText of the search-for-* operations.
Display of the field in TAF actions depends on feature CustomerNickName; the API stores it either way.
Primary owner (key account manager), given as a USER ID (not an employee ID).
Refers to UserRead.UserID.
-
Sent as 0 or negative — Validation (400): must be greater than 0 when present.
-
User does not exist — 37040 (HTTP 500, Code 102).
-
User is inactive — 37931 (HTTP 500, Code 102).
The caller's customer privilege is evaluated against the owner set here: with only the "customers where I am account manager" privilege, create succeeds only when the caller is the primary owner. Owner when omitted: unknown.
Not yet documented: notes.
Secondary owner, given as a USER ID.
Refers to UserRead.UserID.
-
0 or negative — Validation (400).
-
User does not exist / inactive — 37040 / 37931 (HTTP 500, Code 102).
TAF display depends on feature TwoOwnersPerCustomer; the API stores it either way.
Date the customer relationship started.
Time zone: not yet documented.
Default: Server's current date and time (DateTime.Now) when omitted or invalid.
Not yet documented: constraints → timezone.
Industry classification.
Refers to IndustryRead.IndustryID. Look up valid values with GET /v1/industry/get-all.
-
0 or negative — Validation (400).
-
ID does not exist — 30019 (HTTP 500, Code 102). Inactive industries are accepted.
Valid values come from GET /v1/industry/get-all.
Validated as an e-mail address when not blank (400).
Whether the customer has a separate invoicing address.
Country of the invoicing address.
Conditional. Required (non-zero) when UseInvoicingAddress is true; ignored otherwise.
Refers to CountryRead.CountryID. Look up valid values with GET /v1/country/get-all.
Conditional. Stored only when UseInvoicingAddress is true; ignored otherwise.
Conditional. Stored only when UseInvoicingAddress is true; ignored otherwise.
Conditional. Stored only when UseInvoicingAddress is true; ignored otherwise.
Conditional. Stored only when UseInvoicingAddress is true; ignored otherwise.
Conditional. Stored only when UseInvoicingAddress is true; ignored otherwise.
Conditional. Stored only when UseInvoicingAddress is true; ignored otherwise.
VAT number. Returned as VATNo.
Company registration number.
Not yet documented: constraints → unit.
The default mileage distance is also the maximum billable distance.
Invoice contact person.
Refers to ContactRead.ContactID.
-1 = the project's contact; 0 = choose when invoicing; > 0 = a contact ID (must exist: 37011).
Which address invoices go to.
Allowed values:
| Value | Name | Meaning |
|---|---|---|
0 | NotSpecified | Not specified. Accepted by validation; downstream effect not established. |
1 | CustomersAddress | Invoice to the customer's address. |
2 | ContactsAddress | Invoice to the invoice contact's address. |
4 | CustomerInvoicingAddress | Invoice to the customer's separate invoicing address. |
Not yet documented: values → members → meaning.
0124Internal reference on invoices.
Refers to EmployeeRead.EmployeeID.
-3 = project's account manager; -2 = project manager; -1 = customer's owner; 0 = choose when invoicing; > 0 = an EMPLOYEE ID (must exist: 30014).
Customer reference (contact) on invoices.
Refers to ContactRead.ContactID.
-1 = project's contact; 0 = choose when invoicing; > 0 = a contact ID (must exist: 37011).
Payment term.
Refers to PaymentTermRead.ID. Look up valid values with GET /v1/payment-term.
0 must exist (37022). Meaning of 0 (omitted) not established.
Valid values come from GET /v1/payment-term.
Not yet documented: notes.
Unit: percent
No range check in the API layer.
Unit: percent
No range check in the API layer.
Invoice this customer electronically with an EAN number (OIOXML).
-
true and feature OIOXML is off — 37697 (HTTP 500, Code 102).
-
true and Address, ZipCode or City is blank — 60063.
-
true, address complete, VatNo blank — 60064.
EAN number: exactly 13 characters, or empty (any other length fails validation, 400).
Invoice language.
-
Any other value — Validation (400), even when the feature below is off.
-
Feature InvoiceMultilingual is off — Validated, then not stored.
Allowed values:
| Value | Name | Meaning |
|---|---|---|
1 | Danish | Invoices in Danish. |
2 | English | Invoices in English. |
4 | Swedish | Invoices in Swedish. |
15 | Norwegian | Invoices in Norwegian. |
16 | German | Invoices in German. |
17 | Dutch | Invoices in Dutch. |
18 | French | Invoices in French. |
12415161718Responses
A customer as the REST v1 API returns it: the identifiers, name, number, status and tax numbers only.