ProjectsCreate a mileage registration on a project

Create a mileage registration on a project

Creates a mileage (car travel) registration on a project, for the caller or on behalf of another user.

Registers a trip by car on a project so it is reimbursed to the employee and, when billable, invoiced to the customer at the mileage rate. The contract is the one you send, or the project's default mileage contract. On success the API does not return the registration: it answers 302 with a Location header pointing to GET /v1/mileage-registration/{id}.

Use it to:

  • Mobile or expense app posting a trip after the user picked a project from search-for-expense-travel-registration-…
  • An assistant or admin system registering mileage for other employees (UserID)

Not for:

  • Travel TIME (hours) — use a time registration; the Expenses route POST /v1/mileage-registration can create both together

Rules across fields:

  • R1: ProjectSubContractID, when > 0, must belong to ProjectID.
  • 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.

Behaviour:

  • Not idempotent.
  • Not safe to retry blindly. Each call creates a new registration with a new GUID; nothing deduplicates. After a lost response, check the owner's mileage registrations before retrying.
  • Duplicates: Not deduplicated (except: one mileage registration per TimeRegistrationID, 37825).
  • Side effect: Creates the mileage registration, with an expense number when the auto-number series is on, the travel product number and a generated system text.
  • Side effect: Creates mileage financial data and invoicing potential, recalculates the contract's estimated values, and creates the employee reimbursement posting.
  • Changes are visible to the next read immediately.
  • The insert and the side effects run in one transaction scope; the redirect target reads the new row.

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

See also: POST /v1/project-header/employee-expense

Not yet documented: examples; response → success.

curl -X POST "https://app3.timelog.com/your-account/api/v1/project-header/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
  }
}'
{}
POST
/v1/project-header/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