Changelog

2026-07-13 — place ids, manual FX rates, per-diem overrides, exchange-rate direction

Additive, backward-compatible contract fields plus a new exchange-rate discriminator.

  • Place ids on travel entries. Create and update requests accept departureLocationPlaceId and destinationPlaceId (Google Places place_id), and the travel-entry response returns them. Echo the stored values back on a full-replace update to preserve place identity when you edit destination text.
  • Destination country on travel entries. Requests accept an optional destinationCountryCode (ISO 3166-1 alpha-2). When supplied it is stored directly and the server-side geocoding fallback is skipped.
  • Manual exchange rates. Expense and per-diem requests accept an optional manualExchangeRate, and travel-entry requests accept manualAdvanceExchangeRate for the advance. Each is honored only when the FX provider publishes no rate for the referential date; supplying one while an official rate exists is rejected with EXCHANGE_RATE_UNAVAILABLE.
  • Per-diem overrides. The per-diem request accepts overrideAmount and overrideReason, and the per-diem read models return them. To preserve an existing override on a full-replace update, echo the values back; omitting them clears the override and reverts to the statutory payout.
  • Exchange-rate direction. GET /api/v1/reference/exchange-rates now carries a direction discriminator: ForeignPerBase (HNB / ECB) means divide the rate to convert to the base currency, BasePerForeign (CNB) means multiply.

2026-07-08 — advance-payment date, CZ/SK exchange-rate provider

Two additive, backward-compatible changes.

  • Advance-payment date on travel entries. Create and update requests now accept an optional advancePaymentDate (yyyy-MM-dd), and the travel-entry response returns it. When omitted on create the server defaults it to the entry date. It sets the FX date for the advance and, for Czech tenants, the per-diem FX referential date.
  • Exchange-rate provider for CZ/SK. GET /api/v1/reference/exchange-rates now documents the full provider set: HNB for HR, ECB for SI and SK, CNB for CZ. The source field can be "CNB", and baseCurrency is the provider’s native base (EUR for HNB/ECB, CZK for CNB). Behavior is unchanged; the docs now match runtime.

2026-06-11 — per-meal counts, FX provenance, exchange-rate-unavailable code

Three additive surface changes. All are backward compatible: existing payloads keep working unchanged, and the new response fields are additions you can ignore until you need them.

  • Per-meal counts on per-diem create/update. Per-diem requests now accept breakfastsProvided, lunchesProvided, and dinnersProvided (per-meal integer counts) plus an optional per-day mealsProvidedAllocation. They are the canonical write model: the server recomputes the reduction from these counts. The existing v1 aggregate mealsProvided is still accepted; when the per-meal fields are absent the server maps the aggregate as before, so legacy clients are unaffected. Responses keep returning the derived mealsProvided (lunch + dinner, the HR Pravilnik shape) alongside the per-meal counts, so read paths are unchanged.
  • FX provenance on responses. Expense and per-diem responses gain exchangeRateSource, and the travel-entry response gains advancePaymentExchangeRateSource, recording where the applied rate came from: a provider code ("HNB", "ECB") for an official rate, "MANUAL" for a user-supplied rate, or null for a same-currency row (no conversion) or a legacy row saved before provenance tracking. Read-only; no request change.
  • New error code EXCHANGE_RATE_UNAVAILABLE (HTTP 400). Fires when the tenant’s FX provider publishes no rate for a currency on the requested date and no historical rate exists to fall back on. It is a validation-family failure with the standard field-keyed errors body, so you can discriminate the FX-unavailable case specifically and prompt for a manual rate. Added to the errors catalog.

2026-05-23 — per-diem itinerary times stored as true UTC (fix)

Per-diem times persisted via POST /api/v1/travel-entries/{id}/per-diems/from-itinerary are now stored as true UTC instants. Previously each leg’s departure/arrival was written using its wall-clock value tagged as UTC without shifting (an 08:00 local leg was stored as 08:00Z instead of 07:00Z/06:00Z), so itinerary-generated rows disagreed with manually-added rows by 1–2 hours and displayed late. The request and response schemas are unchanged, but the departureTime/arrivalTime values this endpoint returns (and that later reads of the same rows return) now reflect the corrected UTC instant. The stateless calculator preview still echoes wall-clock times. Per-diem amounts are unaffected.

2026-05-23 — per-diem calculator + whole-trip persist

The per-diem calculation surface is consolidated and gains whole-trip persist parity with the locco web app.

  • New POST /api/v1/utilities/per-diems/calculate — a stateless calculator. Send an itinerary (one leg = a 2-stop itinerary) and get the computed rows back, with itinerary-level redistribution applied. No entry context, nothing persisted.
  • New POST /api/v1/travel-entries/{id}/per-diems/from-itinerary — rebuild a travel entry’s per-diems from an itinerary in one call. The server re-runs its own engine over the stops (submitted amounts are never trusted), applies per-leg meals keyed by (countryCode, departureTime) (the pairs the calculator returns), then atomically replaces the entry’s per-diems. This reproduces itinerary-level redistribution and domestic-unit upgrades that posting rows one at a time cannot.
  • Removed POST /api/v1/travel-entries/{id}/per-diems/compute and POST /api/v1/per-diems/calculate-simple. Both are subsumed by utilities/per-diems/calculate — a single leg is just a 2-stop itinerary. Move integrations to the new calculator.

2026-05-22 — Slovenian market support

Initial support for Slovenian tenants. Update your client from the refreshed OpenAPI snapshot at /api-reference/:

  • Slovenian municipalities — new GET /api/v1/reference/slovenian-municipalities returns the SURS municipality list for place dropdowns on Slovenian travel forms. Callable from any tenant.
  • Employee tax residency — employee records gain a residency field (Resident / NonResident) for Slovenian per-diem and payroll-tax reporting. Defaults to Resident.

2026-04-28 — v1.0 launching

The first stable release of the locco partner API. Every surface is wired, typed, and covered by the OpenAPI snapshot at /api-reference/:

  • Travel entries — full CRUD plus the workflow transitions (submit, approve, reject, cancel, request-changes), with atomic creates that accept nested expenses, per-diems, cost-center allocations, and attachments in a single call.
  • Attachments — direct upload to a travel entry via POST /api/v1/travel-entries/{id}/attachments. Multipart, up to 5 files per request.
  • Webhooks — subscription registration with one-time signing-secret exposure, delivery history per subscription, and retry endpoint for failed or exhausted deliveries.
  • Payouts — list ready-for-payout entries, create batches, list history, delete a batch.
  • Employees, Departments, Vehicles, Cost centers — CRUD + list, with custom-field values on employees.
  • Reference — Croatian municipalities, HNB exchange rates, per-diem rate table, the company’s enabled-country and enabled-currency allow-lists.
  • Company — profile read.

Enum values for Status, TravelType, PayoutMethod, and the rest are documented in the OpenAPI schema as enum: [...] allow-lists, so generated clients pin against the canonical strings ("PendingApproval", "BankTransfer", etc.) rather than opaque values.