Validate a new customer
Runs the create validations on a customer body without saving anything.
Runs model validation, the reference checks, the customer business rules and the invoice-setting rules of POST /v1/customer/create, then returns 200 with the body null.
Use it to:
- Validate a form before submitting, to show TimeLog's errors early
Rules across fields:
- R1: As
POST /v1/customer/createR1–R3.
Behaviour:
- Idempotent.
- Safe to retry.
- Changes are visible to the next read immediately.
Next: POST /v1/customer/create
Not yet documented: examples (success).
curl -X POST "https://app3.timelog.com/your-account/api/v1/customer/validate-new-customer" \
-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/validate-new-customer"
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/validate-new-customer", {
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/validate-new-customer", 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/validate-new-customer')
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
"null"
{
"Code": 200,
"Details": [
"'Name' must not be empty.",
"'Currency ID' must not be empty.",
"'Customer Status ID' must not be empty.",
"'Country ID' must not be empty."
],
"Message": "Invalid model state error.",
"Parameters": "customer: {...}",
"Url": "https://app{server-number}.timelog.com/{account}/api/v1/customer/validate-new-customer"
}
{
"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/customer/validate-new-customer
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
Success without content: HTTP 200 with the literal JSON body null. Literal JSON null — the body is valid.