ExpensesCreate a mileage registration

Create a mileage registration

Registers a car trip (mileage) on a project for the caller or on behalf of another user, optionally with its travel time.

Creates a mileage registration that is reimbursed to the employee and, when billable, invoiced to the customer at the mileage rate. Distance is one-way whole kilometres; IsRoundtrip doubles it. The contract is the one you send or the project's default mileage contract. With TravelTimeRegistration the same call also creates a TravelTime time registration on a travel-time task of the project and links it. Returns the created registration (same shape as GET /v1/mileage-registration/{id}).

Use it to:

  • Fleet / travel integration registering trips for employees (UserID, on-behalf privileges)

Not for:

  • Travel time without a trip — create a time registration (GroupType TravelTime)
  • Out-of-pocket costs — POST /v1/employee-expense

Rules across fields:

  • R1: ProjectSubContractID, when > 0, must belong to ProjectID (and be neither prepaid-services nor closed).
  • R2: When ShowOdometer is on and either odometer is sent, OdometerEnd > OdometerStart (400). With OdometerIsRequired also on and both sent, Distance × (IsRoundtrip ? 2 : 1) must equal OdometerEnd − OdometerStart.
  • R3: A personal CarID must belong to the owner (UserID or caller).
  • R4: MileageRateID must have a rate period covering Date.
  • R5: TravelTimeRegistration.TaskID must be a task of ProjectID.
  • R6: With TravelTimeRegistration, a body TimeRegistrationID is overwritten by the new travel-time registration's ID.

Behaviour:

  • Not idempotent.
  • Not safe to retry blindly. Every call creates a new registration (new GUID); nothing deduplicates. After a lost response, list your registrations (or the owner's, via GET by ID) before retrying.
  • Duplicates: Not deduplicated (except one mileage registration per TimeRegistrationID, 37825).
  • Side effect: Creates the registration with expense number (auto-number), travel product number and system text "<Mileage> (<owner name>) <start> » <end> <TotalDistance> km.".
  • Side effect: Creates mileage financial data and invoicing potential, recalculates the contract's estimated values, creates the employee reimbursement posting.
  • Side effect: With TravelTimeRegistration: creates a TravelTime time registration (owner, Date, Purpose as comment, billable hours = Hours when the task is billable) and links it.
  • On failure: With TravelTimeRegistration: the time registration is created first and deleted again if the mileage save throws (compensation, not one transaction).
  • Changes are visible to the next read immediately.
  • The mileage insert and its side effects run in one transaction scope; the response is read back after commit.

Call first: GET /v1/project-header/search-for-expense-travel-registration-order-by-recent-registration, GET /v1/task/travel-time-task-for-time-tracking

Next: GET /v1/mileage-registration/{mileageRegistrationID}, PUT /v1/mileage-registration

See also: POST /v1/project-header/mileage-registration

Not yet documented: behaviour → side effects on failure; examples.

curl -X POST "https://app3.timelog.com/your-account/api/v1/mileage-registration" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
  "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,
  "ProjectSubContractID": 123,
  "UserID": "null",
  "CarID": 123,
  "MileageRateID": 123,
  "IsRoundtrip": true,
  "TimeRegistrationID": "null",
  "IsBillable": true,
  "TravelTimeRegistration": {
    "TaskID": 123,
    "Hours": 3.14,
    "Minutes": 42
  }
}'
{
  "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": [
    {}
  ]
}
POST
/v1/mileage-registration
POST
Base URLstring

Target server for requests. Edit to use your own host.

Bearer Token
Bearer Tokenstring
Required

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.

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.
Content-Typestring
Required

The media type of the request body

Options: application/json
Datestring

The date of the trip. Time zone: not yet documented. Mapped as sent (the adapter does not truncate a time component, unlike the expense adapter). Must have a mileage-rate period covering it (37845) and must not be in a closed accounting period (37827). > Not yet documented: constraints → timezone.

Format: date-time
Purposestring

Why the trip was made. Becomes part of the registration; not part of the generated system text. - empty or whitespace — Not checked by the 400 validator; the domain object raises 60107 (HTTP 500, Code 102).

Max length: 255
OdometerStartintegernull

Odometer reading at the start of the trip. > Not yet documented: constraints → unit. > Not yet documented: requirement.

Format: int32
OdometerEndintegernull

Odometer reading at the end of the trip. - both odometers sent, ShowOdometer and OdometerIsRequired on, and Distance (×2 if IsRoundtrip) ≠ OdometerEnd − OdometerStart — 60100 (HTTP 500, Code 102). Distance is never derived from the odometers. > Not yet documented: constraints → unit.

Format: int32
Distanceinteger

One-way distance of the trip. The stored total distance is Distance, doubled when IsRoundtrip is true. Range: min 1. > Not yet documented: constraints → unit.

Format: int32 • Min: 1
StartAddressstring

Where the trip started (free text).

Max length: 255
EndAddressstring

Where the trip ended (free text).

Max length: 255
ProjectIDinteger

The project the trip is registered on. Refers to ProjectHeaderRead.ProjectID. Look up valid values with GET /v1/project-header/search-for-expense-travel-registration-order-by-recent-registration. No check that the project is active. No check that the owner is allocated to it. Valid values come from GET /v1/project-header/search-for-expense-travel-registration-order-by-recent-registration.

Format: int32
ProjectSubContractIDinteger

The project contract the mileage is booked on. Refers to ContractRead.ProjectSubContractID. - omitted or 0 — The project's default-for-expenses contract, else its main contract; prepaid-services contracts are skipped. None → 37854. - > 0 — Must be a contract of ProjectID (37042), not prepaid services (37847) and not closed (37846). > Not yet documented: values → lookup op.

Format: int32
UserIDintegernull

The user the mileage is registered for. Omit (or send your own UserID) to register for yourself. Refers to UserRead.UserID. Default: the caller - ≤ 0 — 400, Code 200. > Not yet documented: values → lookup op.

Format: int32
CarIDinteger

The car used. A personal car must belong to the registration's owner; a shared car (no employee) is accepted. Refers to CarRead.CarID. Look up valid values with GET /v1/car. Valid values come from GET /v1/car.

Format: int32
MileageRateIDinteger

The mileage rate the distance is reimbursed and invoiced at. Must have a rate period covering Date. Refers to MileageRateRead.MileageRateID. Look up valid values with GET /v1/mileage-rate.

Format: int32
IsRoundtripboolean

true = the trip was there and back; the stored total distance is Distance × 2.

TimeRegistrationIDintegernull

Optional link to a time registration (for example the travel time of the same trip). Refers to TimeRegistrationRead.TimeRegistrationID. - another mileage registration already links to it — 37825 (HTTP 500, Code 102). No check that the time registration exists or belongs to the same owner or project.

Format: int32
IsBillableboolean

Whether the mileage is billable to the customer. - account features Billable AND BillableExpense are on — applied - either feature is off — Replaced by the resolved contract's IsMileageBillable. > Not yet documented: default.

TravelTimeRegistrationobject

Optional travel time to register together with the mileage (POST /v1/mileage-registration only). - sent to POST /v1/mileage-registration — A TravelTime time registration is created first (owner, Date, Purpose as comment) and linked via TimeRegistrationID; it is deleted again if the mileage save fails. TimeRegistrationID in the body is then overwritten. - sent to POST /v1/project-header/mileage-registration — That route binds MileageRegistrationApiCreateModelForProject, which has no such property.

Request Preview
Response

Response will appear here after sending the request

Authentication

header
Authorizationstring
Required

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

application/json
Datestring

The date of the trip.

Time zone: not yet documented.

Mapped as sent (the adapter does not truncate a time component, unlike the expense adapter). Must have a mileage-rate period covering it (37845) and must not be in a closed accounting period (37827).

Not yet documented: constraints → timezone.

Purposestring

Why the trip was made. Becomes part of the registration; not part of the generated system text.

  • empty or whitespace — Not checked by the 400 validator; the domain object raises 60107 (HTTP 500, Code 102).
OdometerStartinteger,null

Odometer reading at the start of the trip.

Not yet documented: constraints → unit.

Not yet documented: requirement.

OdometerEndinteger,null

Odometer reading at the end of the trip.

  • both odometers sent, ShowOdometer and OdometerIsRequired on, and Distance (×2 if IsRoundtrip) ≠ OdometerEnd − OdometerStart — 60100 (HTTP 500, Code 102). Distance is never derived from the odometers.

Not yet documented: constraints → unit.

Distanceinteger

One-way distance of the trip. The stored total distance is Distance, doubled when IsRoundtrip is true.

Range: min 1.

Not yet documented: constraints → unit.

StartAddressstring

Where the trip started (free text).

EndAddressstring

Where the trip ended (free text).

ProjectIDinteger

The project the trip is registered on.

Refers to ProjectHeaderRead.ProjectID. Look up valid values with GET /v1/project-header/search-for-expense-travel-registration-order-by-recent-registration.

No check that the project is active. No check that the owner is allocated to it.

Valid values come from GET /v1/project-header/search-for-expense-travel-registration-order-by-recent-registration.

ProjectSubContractIDinteger

The project contract the mileage is booked on.

Refers to ContractRead.ProjectSubContractID.

  • omitted or 0 — The project's default-for-expenses contract, else its main contract; prepaid-services contracts are skipped. None → 37854.

  • 0 — Must be a contract of ProjectID (37042), not prepaid services (37847) and not closed (37846).

Not yet documented: values → lookup op.

UserIDinteger,null

The user the mileage is registered for. Omit (or send your own UserID) to register for yourself.

Refers to UserRead.UserID.

Default: the caller

  • ≤ 0 — 400, Code 200.

Not yet documented: values → lookup op.

CarIDinteger

The car used. A personal car must belong to the registration's owner; a shared car (no employee) is accepted.

Refers to CarRead.CarID. Look up valid values with GET /v1/car.

Valid values come from GET /v1/car.

MileageRateIDinteger

The mileage rate the distance is reimbursed and invoiced at. Must have a rate period covering Date.

Refers to MileageRateRead.MileageRateID. Look up valid values with GET /v1/mileage-rate.

IsRoundtripboolean

true = the trip was there and back; the stored total distance is Distance × 2.

TimeRegistrationIDinteger,null

Optional link to a time registration (for example the travel time of the same trip).

Refers to TimeRegistrationRead.TimeRegistrationID.

  • another mileage registration already links to it — 37825 (HTTP 500, Code 102).

No check that the time registration exists or belongs to the same owner or project.

IsBillableboolean

Whether the mileage is billable to the customer.

  • account features Billable AND BillableExpense are on — applied

  • either feature is off — Replaced by the resolved contract's IsMileageBillable.

Not yet documented: default.

TravelTimeRegistrationobject

Optional travel time to register together with the mileage (POST /v1/mileage-registration only).

  • sent to POST /v1/mileage-registration — A TravelTime time registration is created first (owner, Date, Purpose as comment) and linked via TimeRegistrationID; it is deleted again if the mileage save fails. TimeRegistrationID in the body is then overwritten.

  • sent to POST /v1/project-header/mileage-registration — That route binds MileageRegistrationApiCreateModelForProject, which has no such property.

Responses

Propertiesobject

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.

Linksarray
Actionsobject[]