<aside> 📋 This changelog covers user-facing GraphQL API changes from January–July 2026. Internal infrastructure, CI/CD, and non-API changes are omitted.
</aside>
This document tracks user-facing changes to the Homeworks GraphQL API over time. It is intended for external API consumers (MCP clients, integrations, and partner developers) as well as internal product and engineering teams tracking what has shipped.
Each entry describes a change that affects the observable API contract — new queries or mutations, field additions or removals, type changes, filter/sort options, and breaking renames. Internal details (CI/CD, build tooling, test coverage, refactors with no schema impact) are intentionally omitted.
⚠️ Breaking changes are marked explicitly.
createRoute(input: CreateRouteInput!, repeatEveryWeeks: Int, optimize: Boolean, provider: RoutingProvider): Route! — creates a route for a date and set of events. Optional repeatEveryWeeks creates a recurring series (up to week 52); all series routes share a seriesKey. Stops displaced from other routes on the same date; routes that become empty are deleted. Pass optimize: true to have the provider reorder stops before saving.updateRoute(routeId: SafeInt!, input: UpdateRouteInput!, ...): Route! — updates stop list, crew, and departure time. The route date is immutable. Pass andFutureRoutesInSeries: true to propagate the change to all future routes in the series (generates a new series key for affected routes; the edited route and earlier routes keep independent keys).deleteRoute(routeId: SafeInt!, andFutureRoutesInSeries: Boolean): DeleteRouteResult! — idempotent; deleting a non-existent route returns deletedCount: 0 rather than an error. andFutureRoutesInSeries: true also deletes all future routes sharing the same series key.optimizeRoute(routeId: SafeInt!, provider: RoutingProvider): Route! — re-optimizes an existing route in place. Reads the current stop list, reorders with the routing provider, saves the new order with leg data pre-populated. Requires calendar.UPDATE.saveRoute deprecated — use createRoute or updateRoute instead. Still functional but will be removed in a future release.Route fields: seriesKey: String (opaque key shared by all routes in a recurring series; null for one-off routes), departTime: LocalTime (company-local wall-clock time as HH:mm:ss), departAt: DateTime (UTC instant the route departs — resolved from departTime, else earliest event start, else 08:00 company-local), startLocation: Coordinate and endLocation: Coordinate (per-route depot overrides).Coordinate — { longitude: Float!, latitude: Float!, address: String }. Used for Route.startLocation and Route.endLocation. Unlike Location, carries an optional human-readable address so it can be reloaded without reverse-geocoding.RoutingProvider (MAPBOX | HERE) — selectable per-request on optimizedRoute, createRoute, updateRoute, and optimizeRoute. Omit to use the deployment’s configured default. Results are cached per provider independently.CoordinateInput — new optional address: String field. Stored with the coordinate so it can be reloaded without a reverse-geocode round-trip.optimizedRoute query — unknown event IDs now return an error instead of being silently excluded. Also added provider: RoutingProvider parameter. Route metrics now come from a directions call (not the optimizer itself), so leg distances always sum to the total.Company fields: resolvedRouteStartLocation: Location and resolvedRouteEndLocation: Location — fully resolved depot locations applying the same fallback chain as the route engine (useCompanyAddressAsDepot → stored coords → geocode name → company main address). Use these (not routeStartLocation / routeEndLocation) to place the depot marker on a map.User field addressLocation: Location — the employee’s on-file address geocoded to map coordinates. Suitable as a route start/end point. Null if address is blank or geocoding fails. Note: lastKnownLocation is a live GPS ping; addressLocation is where the employee starts the day.EventFilter field isRouted: Boolean — true returns only events with at least one route stop; false returns only unrouted events. Omit to return all.VISIT event budgetedHours behavior change — for VISIT-type events, budgetedHours is now automatically derived as the sum of all line items’ budgetedHours. Setting budgetedHours directly in CreateEventInput is no longer valid for VISIT events. Non-VISIT event types are unaffected.