ExpensesUpdate an employee expense

Update an employee expense

Replaces the amounts, date, currency, type, payment method, billability, comment and project of an unapproved employee expense.

A full replacement (PUT) of the expense's ten writable fields, addressed by EmployeeExpenseID in the body. Read the expense first and send every field back — an omitted Comment is cleared and an omitted Amount becomes 0. The owner and the contract cannot be changed; changing ProjectID fails when the expense has a contract. Approved, invoiced, closed-period and completed-project expenses are refused. The response body is JSON null: GET the expense to see the result.

Use it to:

  • Expense app letting an employee correct an expense before approval
  • Receipt-capture integration filling in amount / VAT / currency after OCR of the uploaded receipt

Not for:

  • Changing the owner or contract — delete and re-create the expense
  • Changing receipts — the attach / delete image and file operations

Rules across fields:

  • R1: When the expense has a contract, ProjectID must be a project that contract belongs to — i.e. ProjectID cannot be changed.
  • R2: ExpenseTypeID must belong to the owner's legal entity.
  • R3: Date must be inside the owner's employment and not in a closed accounting period.

Behaviour:

  • Idempotent.
  • Same body → same stored values; last write wins (no concurrency token).
  • Safe to retry.
  • Duplicates: n/a
  • Changes are visible to the next read immediately.

Call first: GET /v1/employee-expense/{employeeExpenseID}

Next: GET /v1/employee-expense/{employeeExpenseID}

See also: DELETE /v1/employee-expense/{employeeExpenseID}

Not yet documented: behaviour → side effects; examples.

curl -X PUT "https://app3.timelog.com/your-account/api/v1/employee-expense" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -d '{
  "EmployeeExpenseID": 123,
  "Date": "2024-12-25T10:00:00Z",
  "Amount": 3.14,
  "VatAmount": 3.14,
  "IsBillable": true,
  "Comment": "null",
  "ExchangeRate": 3.14,
  "CurrencyID": 123,
  "ExpenseTypeID": 123,
  "PaymentMethodID": 123,
  "ProjectID": 123
}'
"null"
PUT
/v1/employee-expense
PUT
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
EmployeeExpenseIDinteger

The expense to update (from GET /v1/employee-expense). Identifier of this object, unique per TimeLog site. - ≤ 0 — 400, Code 200. - unknown ID — 500, Code 100 (the expense lookup throws a data-access exception).

Format: int32
Datestring

The expense (receipt) date. Date only — no time component. - a time component is sent — Truncated to the date (DateTime.Date). Must be inside the owner's employment (38022) and not in a closed accounting period (37699). > Not yet documented: constraints → timezone.

Format: date-time
Amountnumber

The expense amount, in the currency CurrencyID. Unit: currency of CurrencyID - omitted — Written as 0 (full replacement). NotNull on a non-nullable double never fails: 0 and negative amounts pass.

Format: double
VatAmountnumber

The VAT part, in the currency CurrencyID. Unit: currency of CurrencyID - omitted — Written as 0 (full replacement).

Format: double
IsBillableboolean

Whether the expense is billable to the customer. - omitted — Written as false (full replacement). > Not yet documented: behaviour.

Commentstringnull

Free-text comment. Null: Omitted or null clears the stored comment (full replacement). > Not yet documented: constraints → max length.

Max length: 255
ExchangeRatenumber

Exchange rate from CurrencyID to the owner's legal-entity currency. > Not yet documented: constraints → unit.

Format: double
CurrencyIDinteger

Currency of Amount and VatAmount. Refers to CurrencyRead.CurrencyID. Look up valid values with GET /v1/currency/{status}. Valid values come from GET /v1/currency/{status}.

Format: int32
ExpenseTypeIDinteger

The expense type; must belong to the owner's legal entity. Refers to ExpenseTypeRead.ExpenseTypeID. > Not yet documented: values → lookup op.

Format: int32
PaymentMethodIDinteger

How the employee paid. Refers to PaymentMethodRead.PaymentMethodID. Look up valid values with GET /v1/payment-method/{status}.

Format: int32
ProjectIDinteger

The project the expense is booked on. Refers to ProjectHeaderRead.ProjectID. Look up valid values with GET /v1/project-header/search-for-expense-travel-registration-order-by-recent-registration. - changed, and the expense has a contract (ProjectSubContractID > 0) — The stored contract is not on the new project → 37042. In practice the project cannot be changed. - unchanged — Project must not be Completed (38006). Valid values come from GET /v1/project-header/search-for-expense-travel-registration-order-by-recent-registration.

Format: int32
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
EmployeeExpenseIDinteger

The expense to update (from GET /v1/employee-expense).

Identifier of this object, unique per TimeLog site.

  • ≤ 0 — 400, Code 200.

  • unknown ID — 500, Code 100 (the expense lookup throws a data-access exception).

Datestring

The expense (receipt) date.

Date only — no time component.

  • a time component is sent — Truncated to the date (DateTime.Date).

Must be inside the owner's employment (38022) and not in a closed accounting period (37699).

Not yet documented: constraints → timezone.

Amountnumber

The expense amount, in the currency CurrencyID.

Unit: currency of CurrencyID

  • omitted — Written as 0 (full replacement).

NotNull on a non-nullable double never fails: 0 and negative amounts pass.

VatAmountnumber

The VAT part, in the currency CurrencyID.

Unit: currency of CurrencyID

  • omitted — Written as 0 (full replacement).
IsBillableboolean

Whether the expense is billable to the customer.

  • omitted — Written as false (full replacement).

Not yet documented: behaviour.

Commentstring,null

Free-text comment.

Null: Omitted or null clears the stored comment (full replacement).

Not yet documented: constraints → max length.

ExchangeRatenumber

Exchange rate from CurrencyID to the owner's legal-entity currency.

Not yet documented: constraints → unit.

CurrencyIDinteger

Currency of Amount and VatAmount.

Refers to CurrencyRead.CurrencyID. Look up valid values with GET /v1/currency/{status}.

Valid values come from GET /v1/currency/{status}.

ExpenseTypeIDinteger

The expense type; must belong to the owner's legal entity.

Refers to ExpenseTypeRead.ExpenseTypeID.

Not yet documented: values → lookup op.

PaymentMethodIDinteger

How the employee paid.

Refers to PaymentMethodRead.PaymentMethodID. Look up valid values with GET /v1/payment-method/{status}.

ProjectIDinteger

The project the expense is booked on.

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

  • changed, and the expense has a contract (ProjectSubContractID > 0) — The stored contract is not on the new project → 37042. In practice the project cannot be changed.

  • unchanged — Project must not be Completed (38006).

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

Responses

*Success without content: HTTP 200 with the literal JSON body null. Body is the JSON literal null.

Order: none

Empty result: none*