Update a mileage registration
Replaces the trip details of an unapproved, uninvoiced mileage registration, optionally updating or removing its travel time.
A full replacement (PUT) addressed by MileageRegistrationID in the body: every field of the body overwrites the stored value, so read the registration first and send everything back. Owner and contract cannot be changed; a project change keeps the old contract. Send TravelTimeRegistration to update the linked travel time (or create one), or ?deleteTravelTimeRegistration=true to unlink and delete it. Returns the updated registration.
Use it to:
- Employee correcting a trip (distance, addresses, purpose) before approval
- Adding travel time to an existing trip
Not for:
- Changing the owner or the contract — delete and re-create
- Approving — not available in this API
Rules across fields:
- R1: TravelTimeRegistration.TaskID must be a task of ProjectID.
- R2: When ShowOdometer is on and either odometer is sent, OdometerEnd ≥ OdometerStart (400); with OdometerIsRequired also on, Distance × (IsRoundtrip ? 2 : 1) = OdometerEnd − OdometerStart.
- R3: MileageRateID must have a rate period covering Date.
- R4: A personal CarID must belong to the registration's owner.
Behaviour:
- Idempotent.
- Same body → same stored values (last write wins, no concurrency token). With TravelTimeRegistration on a mileage that has none, the FIRST call creates a time registration and later calls update it.
- Safe to retry.
- Duplicates: n/a
- Side effect: With TravelTimeRegistration: updates (or creates and links) the travel-time registration — task, hours, date, Purpose as comment.
- Side effect: With ?deleteTravelTimeRegistration=true: unlinks the travel-time registration, saves the mileage, then deletes the time registration.
- On failure: Travel-time changes are compensated, not transactional: the previous time registration is re-saved (or the new one deleted) when the mileage save throws; if time-registration delete fails after unlinking, the mileage is re-saved with the link.
- Changes are visible to the next read immediately.
Call first: GET /v1/mileage-registration/{mileageRegistrationID}
Next: GET /v1/mileage-registration/{mileageRegistrationID}
See also: DELETE /v1/mileage-registration/{mileageRegistrationID}
Not yet documented: examples.
curl -X PUT "https://app3.timelog.com/your-account/api/v1/mileage-registration?deleteTravelTimeRegistration=false" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"MileageRegistrationID": 123,
"Date": "2024-12-25T10:00:00Z",
"Purpose": "example_string",
"OdometerStart": "null",
"OdometerEnd": "null",
"Distance": 42,
"StartAddress": "123 Main St",
"EndAddress": "123 Main St",
"ProjectID": 123,
"CarID": 123,
"MileageRateID": 123,
"IsRoundtrip": true,
"IsBillable": true,
"TravelTimeRegistration": {
"TaskID": 123,
"Hours": 3.14,
"Minutes": 42
}
}'
import requests
import json
url = "https://app3.timelog.com/your-account/api/v1/mileage-registration?deleteTravelTimeRegistration=false"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
data = {
"MileageRegistrationID": 123,
"Date": "2024-12-25T10:00:00Z",
"Purpose": "example_string",
"OdometerStart": "null",
"OdometerEnd": "null",
"Distance": 42,
"StartAddress": "123 Main St",
"EndAddress": "123 Main St",
"ProjectID": 123,
"CarID": 123,
"MileageRateID": 123,
"IsRoundtrip": true,
"IsBillable": true,
"TravelTimeRegistration": {
"TaskID": 123,
"Hours": 3.14,
"Minutes": 42
}
}
response = requests.put(url, headers=headers, json=data)
print(response.json())
const response = await fetch("https://app3.timelog.com/your-account/api/v1/mileage-registration?deleteTravelTimeRegistration=false", {
method: "PUT",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
},
body: JSON.stringify({
"MileageRegistrationID": 123,
"Date": "2024-12-25T10:00:00Z",
"Purpose": "example_string",
"OdometerStart": "null",
"OdometerEnd": "null",
"Distance": 42,
"StartAddress": "123 Main St",
"EndAddress": "123 Main St",
"ProjectID": 123,
"CarID": 123,
"MileageRateID": 123,
"IsRoundtrip": true,
"IsBillable": true,
"TravelTimeRegistration": {
"TaskID": 123,
"Hours": 3.14,
"Minutes": 42
}
})
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
"bytes"
"encoding/json"
)
func main() {
data := []byte(`{
"MileageRegistrationID": 123,
"Date": "2024-12-25T10:00:00Z",
"Purpose": "example_string",
"OdometerStart": "null",
"OdometerEnd": "null",
"Distance": 42,
"StartAddress": "123 Main St",
"EndAddress": "123 Main St",
"ProjectID": 123,
"CarID": 123,
"MileageRateID": 123,
"IsRoundtrip": true,
"IsBillable": true,
"TravelTimeRegistration": {
"TaskID": 123,
"Hours": 3.14,
"Minutes": 42
}
}`)
req, err := http.NewRequest("PUT", "https://app3.timelog.com/your-account/api/v1/mileage-registration?deleteTravelTimeRegistration=false", 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/mileage-registration?deleteTravelTimeRegistration=false')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Put.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request.body = '{
"MileageRegistrationID": 123,
"Date": "2024-12-25T10:00:00Z",
"Purpose": "example_string",
"OdometerStart": "null",
"OdometerEnd": "null",
"Distance": 42,
"StartAddress": "123 Main St",
"EndAddress": "123 Main St",
"ProjectID": 123,
"CarID": 123,
"MileageRateID": 123,
"IsRoundtrip": true,
"IsBillable": true,
"TravelTimeRegistration": {
"TaskID": 123,
"Hours": 3.14,
"Minutes": 42
}
}'
response = http.request(request)
puts response.body
{
"Properties": {
"MileageRegistrationID": 123,
"ID": "123e4567-e89b-12d3-a456-426614174000",
"Date": "2024-12-25T10:00:00Z",
"Purpose": "example_string",
"OdometerStart": "null",
"OdometerEnd": "null",
"Distance": 42,
"UnitType": 42,
"TotalDistance": 3.14,
"IsRoundtrip": true,
"StartAddress": "123 Main St",
"EndAddress": "123 Main St",
"ProjectID": 123,
"ProjectSubContractID": 123,
"UserID": 123,
"CarID": 123,
"MileageRatesID": 123,
"IsBillable": true,
"IsApproved": true,
"IsInvoiced": true,
"InterfaceCreated": 42,
"InterfaceModified": 42,
"Project": {
"ProjectID": 123,
"ID": "123e4567-e89b-12d3-a456-426614174000",
"Name": "John Doe",
"No": "example_string",
"ExpenseIsBillable": "null",
"CustomerID": 123,
"Customer": {
"CustomerID": 123,
"ID": "123e4567-e89b-12d3-a456-426614174000",
"Name": "John Doe",
"No": "example_string",
"DefaultMileageDistance": 25,
"CustomerStatusID": 123,
"VATNo": "example_string",
"OrganizationNo": "example_string"
},
"MainContractId": 123
},
"Car": {
"CarID": 123,
"ID": "123e4567-e89b-12d3-a456-426614174000",
"Name": "John Doe",
"LicensePlate": "example_string",
"CarType": 42,
"EmployeeUserID": "null",
"EmployeeUser": {
"UserID": 123,
"ID": "123e4567-e89b-12d3-a456-426614174000",
"FirstName": "John Doe",
"LastName": "John Doe",
"Initials": "example_string",
"IsActive": true,
"UserType": "Employee",
"LegalEntityID": 123,
"Email": "user@example.com"
},
"IsActive": true,
"DefaultMileageRate": {
"ID": "123e4567-e89b-12d3-a456-426614174000",
"MileageRateID": 123,
"Name": "John Doe",
"IsActive": true,
"SalaryCodeTaxFree": "null",
"SalaryCodeTaxable": "null",
"AccountNumber": "null",
"IsDefaultForPrivateCar": true,
"HasReimbursement": true,
"UnitType": 42,
"ReimbursementCurrencyID": 123,
"LegalEntityID": 123
},
"IsDefault": true,
"AllowEmployeeChangeMileageRate": true
},
"MileageRate": {
"ID": "123e4567-e89b-12d3-a456-426614174000",
"MileageRateID": 123,
"Name": "John Doe",
"IsActive": true,
"SalaryCodeTaxFree": "null",
"SalaryCodeTaxable": "null",
"AccountNumber": "null",
"IsDefaultForPrivateCar": true,
"HasReimbursement": true,
"UnitType": 42,
"ReimbursementCurrencyID": 123,
"LegalEntityID": 123
},
"TravelTimeRegistration": {
"TimeRegistrationID": 123,
"TaskID": 123,
"Hours": 3.14,
"Minutes": 42,
"Comment": "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/mileage-registration
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.true = unlink AND delete the linked travel-time registration (ignored when the body carries TravelTimeRegistration, or when none is linked).
The media type of the request body
The registration to update. Identifier of this object, unique per TimeLog site. - 0 — 400, Code 200. - unknown or negative — 500, Code 100.
The trip date. Time zone: not yet documented. - in a closed accounting period of the owner — 37827. - no rate period of MileageRateID covers it — 37845. > Not yet documented: constraints → timezone.
Why the trip was made. - omitted, empty or whitespace — Not checked by the 400 validator; the domain raises 60107 (HTTP 500, Code 102).
Odometer reading at the start (km). Omitting it clears the stored value. Null: null / omitted = cleared Unit: km
Odometer reading at the end (km). Omitting it clears the stored value. Null: null / omitted = cleared Unit: km - both odometers sent, ShowOdometer and OdometerIsRequired on, and Distance × (IsRoundtrip ? 2 : 1) ≠ OdometerEnd − OdometerStart — 60100.
One-way distance in whole km. TotalDistance is recalculated (× 2 when IsRoundtrip). Unit: km - ≤ 0 or omitted — The update validator's NotNull on an int never fires; the domain raises 60103 (HTTP 500, Code 102), not 400 as on create. Range: min 1.
Where the trip started.
Where the trip ended.
The project. Changeable, but the stored contract is NOT re-resolved.
Refers to ProjectHeaderRead.ProjectID. Look up valid values with GET /v1/project-header/search-for-expense-travel-registration-order-by-recent-registration.
- changed to another project — ProjectSubContractID keeps the old project's contract; the contract rule only checks that contract is not closed (37846).
Valid values come from GET /v1/project-header/search-for-expense-travel-registration-order-by-recent-registration.
The car used. A personal car must belong to the registration's owner.
Refers to CarRead.CarID. Look up valid values with GET /v1/car.
- personal car of another employee — 37932.
Valid values come from GET /v1/car.
The mileage rate (returned as MileageRatesID on reads). Must have a rate period covering Date.
Refers to MileageRateRead.MileageRateID. Look up valid values with GET /v1/mileage-rate.
true = there and back (TotalDistance = Distance × 2).
Whether the mileage is invoiced to the customer.
Optional travel time to create or update together with the mileage. - sent and the mileage already has a travel-time registration — That time registration is updated (task, hours, date, comment) BEFORE the mileage is validated; on a mileage failure it is re-saved with its previous values. - sent and the mileage has none — A new travel-time registration is created and linked; deleted again if the mileage save fails. - sent together with ?deleteTravelTimeRegistration=true — The body wins; the query flag is ignored. - omitted — An existing travel-time registration is left unchanged (unless ?deleteTravelTimeRegistration=true).
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.
Query Parameters
true = unlink AND delete the linked travel-time registration (ignored when the body carries TravelTimeRegistration, or when none is linked).
Body
The registration to update.
Identifier of this object, unique per TimeLog site.
-
0 — 400, Code 200.
-
unknown or negative — 500, Code 100.
The trip date.
Time zone: not yet documented.
-
in a closed accounting period of the owner — 37827.
-
no rate period of MileageRateID covers it — 37845.
Not yet documented: constraints → timezone.
Why the trip was made.
- omitted, empty or whitespace — Not checked by the 400 validator; the domain raises 60107 (HTTP 500, Code 102).
Odometer reading at the start (km). Omitting it clears the stored value.
Null: null / omitted = cleared
Unit: km
Odometer reading at the end (km). Omitting it clears the stored value.
Null: null / omitted = cleared
Unit: km
- both odometers sent, ShowOdometer and OdometerIsRequired on, and Distance × (IsRoundtrip ? 2 : 1) ≠ OdometerEnd − OdometerStart — 60100.
One-way distance in whole km. TotalDistance is recalculated (× 2 when IsRoundtrip).
Unit: km
- ≤ 0 or omitted — The update validator's NotNull on an int never fires; the domain raises 60103 (HTTP 500, Code 102), not 400 as on create.
Range: min 1.
Where the trip started.
Where the trip ended.
The project. Changeable, but the stored contract is NOT re-resolved.
Refers to ProjectHeaderRead.ProjectID. Look up valid values with GET /v1/project-header/search-for-expense-travel-registration-order-by-recent-registration.
- changed to another project — ProjectSubContractID keeps the old project's contract; the contract rule only checks that contract is not closed (37846).
Valid values come from GET /v1/project-header/search-for-expense-travel-registration-order-by-recent-registration.
The car used. A personal car must belong to the registration's owner.
Refers to CarRead.CarID. Look up valid values with GET /v1/car.
- personal car of another employee — 37932.
Valid values come from GET /v1/car.
The mileage rate (returned as MileageRatesID on reads). Must have a rate period covering Date.
Refers to MileageRateRead.MileageRateID. Look up valid values with GET /v1/mileage-rate.
true = there and back (TotalDistance = Distance × 2).
Whether the mileage is invoiced to the customer.
Optional travel time to create or update together with the mileage.
-
sent and the mileage already has a travel-time registration — That time registration is updated (task, hours, date, comment) BEFORE the mileage is validated; on a mileage failure it is re-saved with its previous values.
-
sent and the mileage has none — A new travel-time registration is created and linked; deleted again if the mileage save fails.
-
sent together with ?deleteTravelTimeRegistration=true — The body wins; the query flag is ignored.
-
omitted — An existing travel-time registration is left unchanged (unless ?deleteTravelTimeRegistration=true).
Responses
A mileage (car travel) registration: the trip (date, purpose, addresses, one-way distance, round-trip flag, odometer readings), the project and contract it is booked on, the owner (UserID), car and mileage rate, approval / invoicing state, and the optional linked travel-time registration. Money is not on this object — the reimbursement and invoice amounts are derived from TotalDistance and the mileage rate's period rates. Nested Project, Car and MileageRate are filled only on $expand. Each entity carries a self link to GET /v1/mileage-registration/{id} and the mileage TAF actions.