@serviceai/api-spec 1.2.409 → 1.2.411

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/openapi.json +5653 -2181
  2. package/openapi.yaml +3893 -1662
  3. package/package.json +1 -1
  4. package/types.d.ts +1961 -1
package/types.d.ts CHANGED
@@ -1826,6 +1826,1575 @@ export interface paths {
1826
1826
  patch?: never;
1827
1827
  trace?: never;
1828
1828
  };
1829
+ "/api/time-tracking/day": {
1830
+ parameters: {
1831
+ query?: never;
1832
+ header?: never;
1833
+ path?: never;
1834
+ cookie?: never;
1835
+ };
1836
+ /** My work day: what is open, today's days, and what this client may record. */
1837
+ get: {
1838
+ parameters: {
1839
+ query?: never;
1840
+ header?: {
1841
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
1842
+ "X-Client-Version"?: string;
1843
+ /** @description Send `1` to declare this client understands work days. The first request carrying it moves the caller onto the day model permanently (users.time_clock_day_model_since); from then on their fence arrivals only open entries inside an open day. */
1844
+ "X-Day-Clock"?: string;
1845
+ };
1846
+ path?: never;
1847
+ cookie?: never;
1848
+ };
1849
+ requestBody?: never;
1850
+ responses: {
1851
+ /** @description Always returned for a permitted caller. */
1852
+ 200: {
1853
+ headers: {
1854
+ [name: string]: unknown;
1855
+ };
1856
+ content: {
1857
+ "application/json": components["schemas"]["MyDayResponse"];
1858
+ };
1859
+ };
1860
+ /** @description Not signed in. */
1861
+ 401: {
1862
+ headers: {
1863
+ [name: string]: unknown;
1864
+ };
1865
+ content: {
1866
+ "application/json": components["schemas"]["ErrorEnvelope"];
1867
+ };
1868
+ };
1869
+ /** @description Time tracking access denied, or no organization on the session. */
1870
+ 403: {
1871
+ headers: {
1872
+ [name: string]: unknown;
1873
+ };
1874
+ content: {
1875
+ "application/json": components["schemas"]["ErrorEnvelope"];
1876
+ };
1877
+ };
1878
+ /** @description Client major below minClientVersion. */
1879
+ 426: {
1880
+ headers: {
1881
+ [name: string]: unknown;
1882
+ };
1883
+ content: {
1884
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
1885
+ };
1886
+ };
1887
+ };
1888
+ };
1889
+ put?: never;
1890
+ post?: never;
1891
+ delete?: never;
1892
+ options?: never;
1893
+ head?: never;
1894
+ patch?: never;
1895
+ trace?: never;
1896
+ };
1897
+ "/api/time-tracking/day/clock-in": {
1898
+ parameters: {
1899
+ query?: never;
1900
+ header?: never;
1901
+ path?: never;
1902
+ cookie?: never;
1903
+ };
1904
+ get?: never;
1905
+ put?: never;
1906
+ /**
1907
+ * Start my day (the master clock). The snapshot is taken at the tap.
1908
+ * @description Where the tap landed decides whether an admin has to look: inside the office fence needs no review; inside an assigned job's site circle needs none only when the org auto-approves those; anywhere else, or no usable fix, goes to review. The button must not wait on GPS — send the cached fix and follow up with POST /api/time-tracking/entries/{id}/location.
1909
+ */
1910
+ post: {
1911
+ parameters: {
1912
+ query?: never;
1913
+ header?: {
1914
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
1915
+ "X-Client-Version"?: string;
1916
+ /** @description Send `1` to declare this client understands work days. The first request carrying it moves the caller onto the day model permanently (users.time_clock_day_model_since); from then on their fence arrivals only open entries inside an open day. */
1917
+ "X-Day-Clock"?: string;
1918
+ };
1919
+ path?: never;
1920
+ cookie?: never;
1921
+ };
1922
+ requestBody?: {
1923
+ content: {
1924
+ "application/json": {
1925
+ lat?: number | null;
1926
+ lng?: number | null;
1927
+ accuracyM?: number | null;
1928
+ /** @enum {string|null} */
1929
+ device?: "ios" | "web" | "android" | null;
1930
+ /**
1931
+ * Format: date-time
1932
+ * @description When the tap happened. The server clamps it.
1933
+ */
1934
+ occurredAt?: string | null;
1935
+ notes?: string | null;
1936
+ };
1937
+ };
1938
+ };
1939
+ responses: {
1940
+ /** @description Day started. */
1941
+ 201: {
1942
+ headers: {
1943
+ [name: string]: unknown;
1944
+ };
1945
+ content: {
1946
+ "application/json": components["schemas"]["DayClockResponse"];
1947
+ };
1948
+ };
1949
+ /** @description Validation failure. */
1950
+ 400: {
1951
+ headers: {
1952
+ [name: string]: unknown;
1953
+ };
1954
+ content: {
1955
+ "application/json": components["schemas"]["ErrorEnvelope"];
1956
+ };
1957
+ };
1958
+ /** @description Not signed in. */
1959
+ 401: {
1960
+ headers: {
1961
+ [name: string]: unknown;
1962
+ };
1963
+ content: {
1964
+ "application/json": components["schemas"]["ErrorEnvelope"];
1965
+ };
1966
+ };
1967
+ /** @description Time tracking access denied, or no organization on the session. */
1968
+ 403: {
1969
+ headers: {
1970
+ [name: string]: unknown;
1971
+ };
1972
+ content: {
1973
+ "application/json": components["schemas"]["ErrorEnvelope"];
1974
+ };
1975
+ };
1976
+ /** @description A day is already running (code DAY_ALREADY_OPEN). The body carries that day — adopt it rather than retrying. */
1977
+ 409: {
1978
+ headers: {
1979
+ [name: string]: unknown;
1980
+ };
1981
+ content: {
1982
+ "application/json": {
1983
+ message: string;
1984
+ /** @enum {string} */
1985
+ code: "DAY_ALREADY_OPEN";
1986
+ day: components["schemas"]["WorkDay"] & (Record<string, never> | null);
1987
+ };
1988
+ };
1989
+ };
1990
+ /** @description Client major below minClientVersion. */
1991
+ 426: {
1992
+ headers: {
1993
+ [name: string]: unknown;
1994
+ };
1995
+ content: {
1996
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
1997
+ };
1998
+ };
1999
+ };
2000
+ };
2001
+ delete?: never;
2002
+ options?: never;
2003
+ head?: never;
2004
+ patch?: never;
2005
+ trace?: never;
2006
+ };
2007
+ "/api/time-tracking/day/clock-out": {
2008
+ parameters: {
2009
+ query?: never;
2010
+ header?: never;
2011
+ path?: never;
2012
+ cookie?: never;
2013
+ };
2014
+ get?: never;
2015
+ put?: never;
2016
+ /** End my day. Closes every open entry with it. */
2017
+ post: {
2018
+ parameters: {
2019
+ query?: never;
2020
+ header?: {
2021
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
2022
+ "X-Client-Version"?: string;
2023
+ /** @description Send `1` to declare this client understands work days. The first request carrying it moves the caller onto the day model permanently (users.time_clock_day_model_since); from then on their fence arrivals only open entries inside an open day. */
2024
+ "X-Day-Clock"?: string;
2025
+ };
2026
+ path?: never;
2027
+ cookie?: never;
2028
+ };
2029
+ requestBody?: {
2030
+ content: {
2031
+ "application/json": {
2032
+ lat?: number | null;
2033
+ lng?: number | null;
2034
+ accuracyM?: number | null;
2035
+ /** @enum {string|null} */
2036
+ device?: "ios" | "web" | "android" | null;
2037
+ /**
2038
+ * Format: date-time
2039
+ * @description When the tap happened. The server clamps it.
2040
+ */
2041
+ occurredAt?: string | null;
2042
+ note?: string | null;
2043
+ };
2044
+ };
2045
+ };
2046
+ responses: {
2047
+ /** @description Day ended. */
2048
+ 200: {
2049
+ headers: {
2050
+ [name: string]: unknown;
2051
+ };
2052
+ content: {
2053
+ "application/json": {
2054
+ day: components["schemas"]["WorkDay"] & (Record<string, never> | null);
2055
+ closedEntries: number;
2056
+ };
2057
+ };
2058
+ };
2059
+ /** @description Not signed in. */
2060
+ 401: {
2061
+ headers: {
2062
+ [name: string]: unknown;
2063
+ };
2064
+ content: {
2065
+ "application/json": components["schemas"]["ErrorEnvelope"];
2066
+ };
2067
+ };
2068
+ /** @description Time tracking access denied, or no organization on the session. */
2069
+ 403: {
2070
+ headers: {
2071
+ [name: string]: unknown;
2072
+ };
2073
+ content: {
2074
+ "application/json": components["schemas"]["ErrorEnvelope"];
2075
+ };
2076
+ };
2077
+ /** @description No day is running (code NO_OPEN_DAY). */
2078
+ 404: {
2079
+ headers: {
2080
+ [name: string]: unknown;
2081
+ };
2082
+ content: {
2083
+ "application/json": components["schemas"]["ErrorEnvelope"];
2084
+ };
2085
+ };
2086
+ /** @description The day was already closed. */
2087
+ 409: {
2088
+ headers: {
2089
+ [name: string]: unknown;
2090
+ };
2091
+ content: {
2092
+ "application/json": components["schemas"]["ErrorEnvelope"];
2093
+ };
2094
+ };
2095
+ /** @description Client major below minClientVersion. */
2096
+ 426: {
2097
+ headers: {
2098
+ [name: string]: unknown;
2099
+ };
2100
+ content: {
2101
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
2102
+ };
2103
+ };
2104
+ };
2105
+ };
2106
+ delete?: never;
2107
+ options?: never;
2108
+ head?: never;
2109
+ patch?: never;
2110
+ trace?: never;
2111
+ };
2112
+ "/api/time-tracking/crossings": {
2113
+ parameters: {
2114
+ query?: never;
2115
+ header?: never;
2116
+ path?: never;
2117
+ cookie?: never;
2118
+ };
2119
+ get?: never;
2120
+ put?: never;
2121
+ /**
2122
+ * Report fence crossings. Batched, idempotent, and the ONLY way a client moves the automatic clock.
2123
+ * @description The server resolver owns automatic clock-in/out. Report what the device saw and read the outcome back from `openEntries`; never decide the clock locally. Re-delivery of a clientEventId is a no-op, so an at-least-once queue is correct. Max 200 events per call.
2124
+ */
2125
+ post: {
2126
+ parameters: {
2127
+ query?: never;
2128
+ header?: {
2129
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
2130
+ "X-Client-Version"?: string;
2131
+ /** @description Send `1` to declare this client can decode an entry or fence with a null jobId (the office as a site). Older builds decode jobId as a required number, so office rows are withheld from them. */
2132
+ "X-Sites"?: string;
2133
+ };
2134
+ path?: never;
2135
+ cookie?: never;
2136
+ };
2137
+ requestBody?: {
2138
+ content: {
2139
+ "application/json": {
2140
+ events: components["schemas"]["ClockCrossing"][];
2141
+ };
2142
+ };
2143
+ };
2144
+ responses: {
2145
+ /** @description Stored. Also returned when nothing was stored because consent is missing — check `reason`. */
2146
+ 202: {
2147
+ headers: {
2148
+ [name: string]: unknown;
2149
+ };
2150
+ content: {
2151
+ "application/json": components["schemas"]["CrossingsAck"];
2152
+ };
2153
+ };
2154
+ /** @description Validation failure, or a job crossing with no jobId. */
2155
+ 400: {
2156
+ headers: {
2157
+ [name: string]: unknown;
2158
+ };
2159
+ content: {
2160
+ "application/json": components["schemas"]["ErrorEnvelope"];
2161
+ };
2162
+ };
2163
+ /** @description Not signed in. */
2164
+ 401: {
2165
+ headers: {
2166
+ [name: string]: unknown;
2167
+ };
2168
+ content: {
2169
+ "application/json": components["schemas"]["ErrorEnvelope"];
2170
+ };
2171
+ };
2172
+ /** @description Time tracking access denied, or no organization on the session. */
2173
+ 403: {
2174
+ headers: {
2175
+ [name: string]: unknown;
2176
+ };
2177
+ content: {
2178
+ "application/json": components["schemas"]["ErrorEnvelope"];
2179
+ };
2180
+ };
2181
+ /** @description A job in the batch is not in this organization, or the office fence is not set. */
2182
+ 404: {
2183
+ headers: {
2184
+ [name: string]: unknown;
2185
+ };
2186
+ content: {
2187
+ "application/json": components["schemas"]["ErrorEnvelope"];
2188
+ };
2189
+ };
2190
+ /** @description Client major below minClientVersion. */
2191
+ 426: {
2192
+ headers: {
2193
+ [name: string]: unknown;
2194
+ };
2195
+ content: {
2196
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
2197
+ };
2198
+ };
2199
+ };
2200
+ };
2201
+ delete?: never;
2202
+ options?: never;
2203
+ head?: never;
2204
+ patch?: never;
2205
+ trace?: never;
2206
+ };
2207
+ "/api/time-tracking/clock-config": {
2208
+ parameters: {
2209
+ query?: never;
2210
+ header?: never;
2211
+ path?: never;
2212
+ cookie?: never;
2213
+ };
2214
+ /** What this org's clock does — dwell, radii, sampling mode, limits. */
2215
+ get: {
2216
+ parameters: {
2217
+ query?: never;
2218
+ header?: {
2219
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
2220
+ "X-Client-Version"?: string;
2221
+ };
2222
+ path?: never;
2223
+ cookie?: never;
2224
+ };
2225
+ requestBody?: never;
2226
+ responses: {
2227
+ /** @description Effective settings, clamped. */
2228
+ 200: {
2229
+ headers: {
2230
+ [name: string]: unknown;
2231
+ };
2232
+ content: {
2233
+ "application/json": components["schemas"]["ClockConfig"];
2234
+ };
2235
+ };
2236
+ /** @description Not signed in. */
2237
+ 401: {
2238
+ headers: {
2239
+ [name: string]: unknown;
2240
+ };
2241
+ content: {
2242
+ "application/json": components["schemas"]["ErrorEnvelope"];
2243
+ };
2244
+ };
2245
+ /** @description Time tracking access denied, or no organization on the session. */
2246
+ 403: {
2247
+ headers: {
2248
+ [name: string]: unknown;
2249
+ };
2250
+ content: {
2251
+ "application/json": components["schemas"]["ErrorEnvelope"];
2252
+ };
2253
+ };
2254
+ /** @description Organization not found. */
2255
+ 404: {
2256
+ headers: {
2257
+ [name: string]: unknown;
2258
+ };
2259
+ content: {
2260
+ "application/json": components["schemas"]["ErrorEnvelope"];
2261
+ };
2262
+ };
2263
+ /** @description Client major below minClientVersion. */
2264
+ 426: {
2265
+ headers: {
2266
+ [name: string]: unknown;
2267
+ };
2268
+ content: {
2269
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
2270
+ };
2271
+ };
2272
+ };
2273
+ };
2274
+ put?: never;
2275
+ post?: never;
2276
+ delete?: never;
2277
+ options?: never;
2278
+ head?: never;
2279
+ patch?: never;
2280
+ trace?: never;
2281
+ };
2282
+ "/api/time-tracking/user-geofences": {
2283
+ parameters: {
2284
+ query?: never;
2285
+ header?: never;
2286
+ path?: never;
2287
+ cookie?: never;
2288
+ };
2289
+ /** The fences this user should watch. */
2290
+ get: {
2291
+ parameters: {
2292
+ query?: {
2293
+ /** @description 'assigned' fences only the jobs the caller is on. Useful when an owner's job count exceeds the 20 regions iOS can watch. */
2294
+ scope?: "all" | "assigned";
2295
+ /** @description Keep jobs outside their tracking window. For the manual Clock In picker only — never for fences. */
2296
+ includeClosed?: "1" | "true";
2297
+ /** @description Prepend the office fence (jobId null). Only send it if this client can decode a null jobId. */
2298
+ sites?: "1" | "true";
2299
+ };
2300
+ header?: {
2301
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
2302
+ "X-Client-Version"?: string;
2303
+ };
2304
+ path?: never;
2305
+ cookie?: never;
2306
+ };
2307
+ requestBody?: never;
2308
+ responses: {
2309
+ /** @description Fences, office first when requested. */
2310
+ 200: {
2311
+ headers: {
2312
+ [name: string]: unknown;
2313
+ };
2314
+ content: {
2315
+ "application/json": components["schemas"]["Geofence"][];
2316
+ };
2317
+ };
2318
+ /** @description Not signed in. */
2319
+ 401: {
2320
+ headers: {
2321
+ [name: string]: unknown;
2322
+ };
2323
+ content: {
2324
+ "application/json": components["schemas"]["ErrorEnvelope"];
2325
+ };
2326
+ };
2327
+ /** @description Time tracking access denied, or no organization on the session. */
2328
+ 403: {
2329
+ headers: {
2330
+ [name: string]: unknown;
2331
+ };
2332
+ content: {
2333
+ "application/json": components["schemas"]["ErrorEnvelope"];
2334
+ };
2335
+ };
2336
+ /** @description Client major below minClientVersion. */
2337
+ 426: {
2338
+ headers: {
2339
+ [name: string]: unknown;
2340
+ };
2341
+ content: {
2342
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
2343
+ };
2344
+ };
2345
+ };
2346
+ };
2347
+ put?: never;
2348
+ post?: never;
2349
+ delete?: never;
2350
+ options?: never;
2351
+ head?: never;
2352
+ patch?: never;
2353
+ trace?: never;
2354
+ };
2355
+ "/api/time-tracking/active": {
2356
+ parameters: {
2357
+ query?: never;
2358
+ header?: never;
2359
+ path?: never;
2360
+ cookie?: never;
2361
+ };
2362
+ /** My open entries right now (or the team's). */
2363
+ get: {
2364
+ parameters: {
2365
+ query?: {
2366
+ /** @description Everyone on the clock. Requires time_tracking.view_all. */
2367
+ scope?: "team";
2368
+ };
2369
+ header?: {
2370
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
2371
+ "X-Client-Version"?: string;
2372
+ /** @description Send `1` to declare this client can decode an entry or fence with a null jobId (the office as a site). Older builds decode jobId as a required number, so office rows are withheld from them. */
2373
+ "X-Sites"?: string;
2374
+ };
2375
+ path?: never;
2376
+ cookie?: never;
2377
+ };
2378
+ requestBody?: never;
2379
+ responses: {
2380
+ /** @description Open entries. Normally zero or one for a single user. */
2381
+ 200: {
2382
+ headers: {
2383
+ [name: string]: unknown;
2384
+ };
2385
+ content: {
2386
+ "application/json": components["schemas"]["TimeEntry"][];
2387
+ };
2388
+ };
2389
+ /** @description Not signed in. */
2390
+ 401: {
2391
+ headers: {
2392
+ [name: string]: unknown;
2393
+ };
2394
+ content: {
2395
+ "application/json": components["schemas"]["ErrorEnvelope"];
2396
+ };
2397
+ };
2398
+ /** @description Time tracking access denied, or no organization on the session. */
2399
+ 403: {
2400
+ headers: {
2401
+ [name: string]: unknown;
2402
+ };
2403
+ content: {
2404
+ "application/json": components["schemas"]["ErrorEnvelope"];
2405
+ };
2406
+ };
2407
+ /** @description Client major below minClientVersion. */
2408
+ 426: {
2409
+ headers: {
2410
+ [name: string]: unknown;
2411
+ };
2412
+ content: {
2413
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
2414
+ };
2415
+ };
2416
+ };
2417
+ };
2418
+ put?: never;
2419
+ post?: never;
2420
+ delete?: never;
2421
+ options?: never;
2422
+ head?: never;
2423
+ patch?: never;
2424
+ trace?: never;
2425
+ };
2426
+ "/api/time-tracking/entries": {
2427
+ parameters: {
2428
+ query?: never;
2429
+ header?: never;
2430
+ path?: never;
2431
+ cookie?: never;
2432
+ };
2433
+ /** Time entries, scoped by permission. */
2434
+ get: {
2435
+ parameters: {
2436
+ query?: {
2437
+ /** @description `me` always means the caller — use it for 'My Time' rather than a cached id. Absent means the whole org for a view_all role. Another user's id requires view_all. */
2438
+ userId?: string;
2439
+ jobId?: number;
2440
+ from?: string;
2441
+ to?: string;
2442
+ limit?: number;
2443
+ offset?: number;
2444
+ };
2445
+ header?: {
2446
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
2447
+ "X-Client-Version"?: string;
2448
+ /** @description Send `1` to declare this client can decode an entry or fence with a null jobId (the office as a site). Older builds decode jobId as a required number, so office rows are withheld from them. */
2449
+ "X-Sites"?: string;
2450
+ };
2451
+ path?: never;
2452
+ cookie?: never;
2453
+ };
2454
+ requestBody?: never;
2455
+ responses: {
2456
+ /** @description Entries, newest-first by the storage layer's ordering. */
2457
+ 200: {
2458
+ headers: {
2459
+ [name: string]: unknown;
2460
+ };
2461
+ content: {
2462
+ "application/json": components["schemas"]["TimeEntry"][];
2463
+ };
2464
+ };
2465
+ /** @description Not signed in. */
2466
+ 401: {
2467
+ headers: {
2468
+ [name: string]: unknown;
2469
+ };
2470
+ content: {
2471
+ "application/json": components["schemas"]["ErrorEnvelope"];
2472
+ };
2473
+ };
2474
+ /** @description Time tracking access denied, or no organization on the session. */
2475
+ 403: {
2476
+ headers: {
2477
+ [name: string]: unknown;
2478
+ };
2479
+ content: {
2480
+ "application/json": components["schemas"]["ErrorEnvelope"];
2481
+ };
2482
+ };
2483
+ /** @description Client major below minClientVersion. */
2484
+ 426: {
2485
+ headers: {
2486
+ [name: string]: unknown;
2487
+ };
2488
+ content: {
2489
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
2490
+ };
2491
+ };
2492
+ };
2493
+ };
2494
+ put?: never;
2495
+ post?: never;
2496
+ delete?: never;
2497
+ options?: never;
2498
+ head?: never;
2499
+ patch?: never;
2500
+ trace?: never;
2501
+ };
2502
+ "/api/time-tracking/clock-in": {
2503
+ parameters: {
2504
+ query?: never;
2505
+ header?: never;
2506
+ path?: never;
2507
+ cookie?: never;
2508
+ };
2509
+ get?: never;
2510
+ put?: never;
2511
+ /**
2512
+ * Clock in to a job or the office by hand.
2513
+ * @description A deliberate button press. On the day model a press with no day running starts the day at that moment; a fence-driven clock-in from an old-model client cannot, and is refused.
2514
+ */
2515
+ post: {
2516
+ parameters: {
2517
+ query?: never;
2518
+ header?: {
2519
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
2520
+ "X-Client-Version"?: string;
2521
+ /** @description Send `1` to declare this client can decode an entry or fence with a null jobId (the office as a site). Older builds decode jobId as a required number, so office rows are withheld from them. */
2522
+ "X-Sites"?: string;
2523
+ /** @description Send `1` to declare this client understands work days. The first request carrying it moves the caller onto the day model permanently (users.time_clock_day_model_since); from then on their fence arrivals only open entries inside an open day. */
2524
+ "X-Day-Clock"?: string;
2525
+ };
2526
+ path?: never;
2527
+ cookie?: never;
2528
+ };
2529
+ requestBody?: {
2530
+ content: {
2531
+ "application/json": {
2532
+ /** @description Omit or null together with siteType 'office'. */
2533
+ jobId?: number | null;
2534
+ /** @enum {string} */
2535
+ siteType?: "job" | "office";
2536
+ /** @description Clock someone else in. Requires time_tracking.edit_all. */
2537
+ userId?: string;
2538
+ lat?: number;
2539
+ lng?: number;
2540
+ /** @enum {string} */
2541
+ source?: "manual" | "auto";
2542
+ notes?: string;
2543
+ budgetItemId?: number | null;
2544
+ /**
2545
+ * Format: date-time
2546
+ * @description When the tech actually arrived. The server clamps it.
2547
+ */
2548
+ occurredAt?: string;
2549
+ accuracyM?: number | null;
2550
+ /** @enum {string|null} */
2551
+ device?: "ios" | "web" | "android" | null;
2552
+ };
2553
+ };
2554
+ };
2555
+ responses: {
2556
+ /** @description Entry opened. */
2557
+ 201: {
2558
+ headers: {
2559
+ [name: string]: unknown;
2560
+ };
2561
+ content: {
2562
+ "application/json": components["schemas"]["TimeEntry"];
2563
+ };
2564
+ };
2565
+ /** @description Validation failure, or jobId missing on a job clock-in. */
2566
+ 400: {
2567
+ headers: {
2568
+ [name: string]: unknown;
2569
+ };
2570
+ content: {
2571
+ "application/json": components["schemas"]["ErrorEnvelope"];
2572
+ };
2573
+ };
2574
+ /** @description Not signed in. */
2575
+ 401: {
2576
+ headers: {
2577
+ [name: string]: unknown;
2578
+ };
2579
+ content: {
2580
+ "application/json": components["schemas"]["ErrorEnvelope"];
2581
+ };
2582
+ };
2583
+ /** @description Time tracking access denied, or no organization on the session. */
2584
+ 403: {
2585
+ headers: {
2586
+ [name: string]: unknown;
2587
+ };
2588
+ content: {
2589
+ "application/json": components["schemas"]["ErrorEnvelope"];
2590
+ };
2591
+ };
2592
+ /** @description Job not in this organization, or the office fence is not set. */
2593
+ 404: {
2594
+ headers: {
2595
+ [name: string]: unknown;
2596
+ };
2597
+ content: {
2598
+ "application/json": components["schemas"]["ErrorEnvelope"];
2599
+ };
2600
+ };
2601
+ /** @description Already clocked in at this site (code ALREADY_CLOCKED_IN_SAME_JOB). The body carries the existing entry — adopt it. */
2602
+ 409: {
2603
+ headers: {
2604
+ [name: string]: unknown;
2605
+ };
2606
+ content: {
2607
+ "application/json": {
2608
+ message: string;
2609
+ code: string;
2610
+ entry: components["schemas"]["TimeEntry"];
2611
+ };
2612
+ };
2613
+ };
2614
+ /** @description Client major below minClientVersion. */
2615
+ 426: {
2616
+ headers: {
2617
+ [name: string]: unknown;
2618
+ };
2619
+ content: {
2620
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
2621
+ };
2622
+ };
2623
+ };
2624
+ };
2625
+ delete?: never;
2626
+ options?: never;
2627
+ head?: never;
2628
+ patch?: never;
2629
+ trace?: never;
2630
+ };
2631
+ "/api/time-tracking/clock-out": {
2632
+ parameters: {
2633
+ query?: never;
2634
+ header?: never;
2635
+ path?: never;
2636
+ cookie?: never;
2637
+ };
2638
+ get?: never;
2639
+ put?: never;
2640
+ /** Clock out of a job or the office by hand. */
2641
+ post: {
2642
+ parameters: {
2643
+ query?: never;
2644
+ header?: {
2645
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
2646
+ "X-Client-Version"?: string;
2647
+ /** @description Send `1` to declare this client can decode an entry or fence with a null jobId (the office as a site). Older builds decode jobId as a required number, so office rows are withheld from them. */
2648
+ "X-Sites"?: string;
2649
+ /** @description Send `1` to declare this client understands work days. The first request carrying it moves the caller onto the day model permanently (users.time_clock_day_model_since); from then on their fence arrivals only open entries inside an open day. */
2650
+ "X-Day-Clock"?: string;
2651
+ };
2652
+ path?: never;
2653
+ cookie?: never;
2654
+ };
2655
+ requestBody?: {
2656
+ content: {
2657
+ "application/json": {
2658
+ jobId?: number | null;
2659
+ /** @enum {string} */
2660
+ siteType?: "job" | "office";
2661
+ /** @description Requires time_tracking.edit_all. */
2662
+ userId?: string;
2663
+ lat?: number;
2664
+ lng?: number;
2665
+ /** @enum {string} */
2666
+ source?: "manual" | "auto";
2667
+ notes?: string;
2668
+ /**
2669
+ * Format: date-time
2670
+ * @description When the tech actually left. Clamped to [entry.clockIn, now].
2671
+ */
2672
+ occurredAt?: string;
2673
+ accuracyM?: number | null;
2674
+ /** @enum {string|null} */
2675
+ device?: "ios" | "web" | "android" | null;
2676
+ };
2677
+ };
2678
+ };
2679
+ responses: {
2680
+ /** @description Entry closed. */
2681
+ 200: {
2682
+ headers: {
2683
+ [name: string]: unknown;
2684
+ };
2685
+ content: {
2686
+ "application/json": components["schemas"]["TimeEntry"];
2687
+ };
2688
+ };
2689
+ /** @description Validation failure. */
2690
+ 400: {
2691
+ headers: {
2692
+ [name: string]: unknown;
2693
+ };
2694
+ content: {
2695
+ "application/json": components["schemas"]["ErrorEnvelope"];
2696
+ };
2697
+ };
2698
+ /** @description Not signed in. */
2699
+ 401: {
2700
+ headers: {
2701
+ [name: string]: unknown;
2702
+ };
2703
+ content: {
2704
+ "application/json": components["schemas"]["ErrorEnvelope"];
2705
+ };
2706
+ };
2707
+ /** @description Time tracking access denied, or no organization on the session. */
2708
+ 403: {
2709
+ headers: {
2710
+ [name: string]: unknown;
2711
+ };
2712
+ content: {
2713
+ "application/json": components["schemas"]["ErrorEnvelope"];
2714
+ };
2715
+ };
2716
+ /** @description No open entry at that site. */
2717
+ 404: {
2718
+ headers: {
2719
+ [name: string]: unknown;
2720
+ };
2721
+ content: {
2722
+ "application/json": components["schemas"]["ErrorEnvelope"];
2723
+ };
2724
+ };
2725
+ /** @description Client major below minClientVersion. */
2726
+ 426: {
2727
+ headers: {
2728
+ [name: string]: unknown;
2729
+ };
2730
+ content: {
2731
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
2732
+ };
2733
+ };
2734
+ };
2735
+ };
2736
+ delete?: never;
2737
+ options?: never;
2738
+ head?: never;
2739
+ patch?: never;
2740
+ trace?: never;
2741
+ };
2742
+ "/api/time-tracking/switch": {
2743
+ parameters: {
2744
+ query?: never;
2745
+ header?: never;
2746
+ path?: never;
2747
+ cookie?: never;
2748
+ };
2749
+ get?: never;
2750
+ put?: never;
2751
+ /**
2752
+ * Move my clock to another job in one atomic call.
2753
+ * @description Closes the caller's open entry and opens one on the target. One request instead of clock-out + clock-in, so an offline drop between the two cannot strand the tech clocked out. Own entry only.
2754
+ */
2755
+ post: {
2756
+ parameters: {
2757
+ query?: never;
2758
+ header?: {
2759
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
2760
+ "X-Client-Version"?: string;
2761
+ };
2762
+ path?: never;
2763
+ cookie?: never;
2764
+ };
2765
+ requestBody?: {
2766
+ content: {
2767
+ "application/json": {
2768
+ targetJobId: number;
2769
+ /**
2770
+ * Format: date-time
2771
+ * @description Pull the start back to when they actually moved. Must be after the current clock-in and no more than a minute in the future.
2772
+ */
2773
+ switchAt?: string;
2774
+ lat?: number;
2775
+ lng?: number;
2776
+ };
2777
+ };
2778
+ };
2779
+ responses: {
2780
+ /** @description Switched. */
2781
+ 200: {
2782
+ headers: {
2783
+ [name: string]: unknown;
2784
+ };
2785
+ content: {
2786
+ "application/json": {
2787
+ closed: components["schemas"]["TimeEntry"];
2788
+ opened: components["schemas"]["TimeEntry"];
2789
+ targetEligible: boolean;
2790
+ };
2791
+ };
2792
+ };
2793
+ /** @description No active clock-in, already on that job, or an out-of-range switch time. */
2794
+ 400: {
2795
+ headers: {
2796
+ [name: string]: unknown;
2797
+ };
2798
+ content: {
2799
+ "application/json": components["schemas"]["ErrorEnvelope"];
2800
+ };
2801
+ };
2802
+ /** @description Not signed in. */
2803
+ 401: {
2804
+ headers: {
2805
+ [name: string]: unknown;
2806
+ };
2807
+ content: {
2808
+ "application/json": components["schemas"]["ErrorEnvelope"];
2809
+ };
2810
+ };
2811
+ /** @description Time tracking access denied, or no organization on the session. */
2812
+ 403: {
2813
+ headers: {
2814
+ [name: string]: unknown;
2815
+ };
2816
+ content: {
2817
+ "application/json": components["schemas"]["ErrorEnvelope"];
2818
+ };
2819
+ };
2820
+ /** @description Target job not in this organization. */
2821
+ 404: {
2822
+ headers: {
2823
+ [name: string]: unknown;
2824
+ };
2825
+ content: {
2826
+ "application/json": components["schemas"]["ErrorEnvelope"];
2827
+ };
2828
+ };
2829
+ /** @description Client major below minClientVersion. */
2830
+ 426: {
2831
+ headers: {
2832
+ [name: string]: unknown;
2833
+ };
2834
+ content: {
2835
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
2836
+ };
2837
+ };
2838
+ };
2839
+ };
2840
+ delete?: never;
2841
+ options?: never;
2842
+ head?: never;
2843
+ patch?: never;
2844
+ trace?: never;
2845
+ };
2846
+ "/api/time-tracking/entries/{id}/retarget": {
2847
+ parameters: {
2848
+ query?: never;
2849
+ header?: never;
2850
+ path?: never;
2851
+ cookie?: never;
2852
+ };
2853
+ get?: never;
2854
+ put?: never;
2855
+ /**
2856
+ * Move an open automatic entry to a co-located job ('which unit?').
2857
+ * @description The target's fence must overlap the entry's — same address. Anything further is a job reassignment and belongs on the web. Open, automatic, own entries only.
2858
+ */
2859
+ post: {
2860
+ parameters: {
2861
+ query?: never;
2862
+ header?: {
2863
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
2864
+ "X-Client-Version"?: string;
2865
+ };
2866
+ path: {
2867
+ id: number;
2868
+ };
2869
+ cookie?: never;
2870
+ };
2871
+ requestBody?: {
2872
+ content: {
2873
+ "application/json": {
2874
+ jobId: number;
2875
+ };
2876
+ };
2877
+ };
2878
+ responses: {
2879
+ /** @description Entry moved. */
2880
+ 200: {
2881
+ headers: {
2882
+ [name: string]: unknown;
2883
+ };
2884
+ content: {
2885
+ "application/json": components["schemas"]["TimeEntry"];
2886
+ };
2887
+ };
2888
+ /** @description Not open, not automatic, office time, or the target is not at this address. */
2889
+ 400: {
2890
+ headers: {
2891
+ [name: string]: unknown;
2892
+ };
2893
+ content: {
2894
+ "application/json": components["schemas"]["ErrorEnvelope"];
2895
+ };
2896
+ };
2897
+ /** @description Not signed in. */
2898
+ 401: {
2899
+ headers: {
2900
+ [name: string]: unknown;
2901
+ };
2902
+ content: {
2903
+ "application/json": components["schemas"]["ErrorEnvelope"];
2904
+ };
2905
+ };
2906
+ /** @description Time tracking access denied, or no organization on the session. */
2907
+ 403: {
2908
+ headers: {
2909
+ [name: string]: unknown;
2910
+ };
2911
+ content: {
2912
+ "application/json": components["schemas"]["ErrorEnvelope"];
2913
+ };
2914
+ };
2915
+ /** @description Entry or target job not found. */
2916
+ 404: {
2917
+ headers: {
2918
+ [name: string]: unknown;
2919
+ };
2920
+ content: {
2921
+ "application/json": components["schemas"]["ErrorEnvelope"];
2922
+ };
2923
+ };
2924
+ /** @description Client major below minClientVersion. */
2925
+ 426: {
2926
+ headers: {
2927
+ [name: string]: unknown;
2928
+ };
2929
+ content: {
2930
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
2931
+ };
2932
+ };
2933
+ };
2934
+ };
2935
+ delete?: never;
2936
+ options?: never;
2937
+ head?: never;
2938
+ patch?: never;
2939
+ trace?: never;
2940
+ };
2941
+ "/api/time-tracking/entries/{id}/location": {
2942
+ parameters: {
2943
+ query?: never;
2944
+ header?: never;
2945
+ path?: never;
2946
+ cookie?: never;
2947
+ };
2948
+ get?: never;
2949
+ put?: never;
2950
+ /**
2951
+ * The fresh fix taken AFTER a manual clock press, so the button never waits on GPS.
2952
+ * @description Manual entries only, own entry (or edit_all), within ten minutes of the press.
2953
+ */
2954
+ post: {
2955
+ parameters: {
2956
+ query?: never;
2957
+ header?: {
2958
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
2959
+ "X-Client-Version"?: string;
2960
+ };
2961
+ path: {
2962
+ id: number;
2963
+ };
2964
+ cookie?: never;
2965
+ };
2966
+ requestBody?: {
2967
+ content: {
2968
+ "application/json": {
2969
+ /** @enum {string} */
2970
+ kind: "in" | "out";
2971
+ lat: number;
2972
+ lng: number;
2973
+ accuracyM?: number | null;
2974
+ /** @enum {string|null} */
2975
+ device?: "ios" | "web" | "android" | null;
2976
+ };
2977
+ };
2978
+ };
2979
+ responses: {
2980
+ /** @description Recorded. */
2981
+ 200: {
2982
+ headers: {
2983
+ [name: string]: unknown;
2984
+ };
2985
+ content: {
2986
+ "application/json": {
2987
+ /** @enum {boolean} */
2988
+ ok: true;
2989
+ };
2990
+ };
2991
+ };
2992
+ /** @description Not kept — no signed location consent. */
2993
+ 202: {
2994
+ headers: {
2995
+ [name: string]: unknown;
2996
+ };
2997
+ content: {
2998
+ "application/json": {
2999
+ /** @enum {boolean} */
3000
+ ok: false;
3001
+ reason: string;
3002
+ };
3003
+ };
3004
+ };
3005
+ /** @description Not a manual entry, not clocked out yet, or too late. */
3006
+ 400: {
3007
+ headers: {
3008
+ [name: string]: unknown;
3009
+ };
3010
+ content: {
3011
+ "application/json": components["schemas"]["ErrorEnvelope"];
3012
+ };
3013
+ };
3014
+ /** @description Not signed in. */
3015
+ 401: {
3016
+ headers: {
3017
+ [name: string]: unknown;
3018
+ };
3019
+ content: {
3020
+ "application/json": components["schemas"]["ErrorEnvelope"];
3021
+ };
3022
+ };
3023
+ /** @description Time tracking access denied, or no organization on the session. */
3024
+ 403: {
3025
+ headers: {
3026
+ [name: string]: unknown;
3027
+ };
3028
+ content: {
3029
+ "application/json": components["schemas"]["ErrorEnvelope"];
3030
+ };
3031
+ };
3032
+ /** @description Entry not found. */
3033
+ 404: {
3034
+ headers: {
3035
+ [name: string]: unknown;
3036
+ };
3037
+ content: {
3038
+ "application/json": components["schemas"]["ErrorEnvelope"];
3039
+ };
3040
+ };
3041
+ /** @description Client major below minClientVersion. */
3042
+ 426: {
3043
+ headers: {
3044
+ [name: string]: unknown;
3045
+ };
3046
+ content: {
3047
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
3048
+ };
3049
+ };
3050
+ };
3051
+ };
3052
+ delete?: never;
3053
+ options?: never;
3054
+ head?: never;
3055
+ patch?: never;
3056
+ trace?: never;
3057
+ };
3058
+ "/api/time-tracking/pings": {
3059
+ parameters: {
3060
+ query?: never;
3061
+ header?: never;
3062
+ path?: never;
3063
+ cookie?: never;
3064
+ };
3065
+ get?: never;
3066
+ put?: never;
3067
+ /**
3068
+ * The location trail while my day is open.
3069
+ * @description Nothing is stored outside an open day, without signed consent, or when the org turned the trail off — each returns 202 with a `reason`. Max 200 readings per call.
3070
+ */
3071
+ post: {
3072
+ parameters: {
3073
+ query?: never;
3074
+ header?: {
3075
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
3076
+ "X-Client-Version"?: string;
3077
+ };
3078
+ path?: never;
3079
+ cookie?: never;
3080
+ };
3081
+ requestBody?: {
3082
+ content: {
3083
+ "application/json": {
3084
+ pings: {
3085
+ lat: number;
3086
+ lng: number;
3087
+ accuracyM?: number | null;
3088
+ speedMps?: number | null;
3089
+ /**
3090
+ * Format: date-time
3091
+ * @description When the device took the reading.
3092
+ */
3093
+ recordedAt: string;
3094
+ /** @enum {string} */
3095
+ source?: "trail" | "day_start" | "day_end";
3096
+ }[];
3097
+ /** @enum {string|null} */
3098
+ device?: "ios" | "web" | "android" | null;
3099
+ };
3100
+ };
3101
+ };
3102
+ responses: {
3103
+ /** @description Always returned. A non-null `reason` means stop sending until state changes. */
3104
+ 202: {
3105
+ headers: {
3106
+ [name: string]: unknown;
3107
+ };
3108
+ content: {
3109
+ "application/json": components["schemas"]["TrailPingResponse"];
3110
+ };
3111
+ };
3112
+ /** @description Validation failure. */
3113
+ 400: {
3114
+ headers: {
3115
+ [name: string]: unknown;
3116
+ };
3117
+ content: {
3118
+ "application/json": components["schemas"]["ErrorEnvelope"];
3119
+ };
3120
+ };
3121
+ /** @description Not signed in. */
3122
+ 401: {
3123
+ headers: {
3124
+ [name: string]: unknown;
3125
+ };
3126
+ content: {
3127
+ "application/json": components["schemas"]["ErrorEnvelope"];
3128
+ };
3129
+ };
3130
+ /** @description Time tracking access denied, or no organization on the session. */
3131
+ 403: {
3132
+ headers: {
3133
+ [name: string]: unknown;
3134
+ };
3135
+ content: {
3136
+ "application/json": components["schemas"]["ErrorEnvelope"];
3137
+ };
3138
+ };
3139
+ /** @description Client major below minClientVersion. */
3140
+ 426: {
3141
+ headers: {
3142
+ [name: string]: unknown;
3143
+ };
3144
+ content: {
3145
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
3146
+ };
3147
+ };
3148
+ };
3149
+ };
3150
+ delete?: never;
3151
+ options?: never;
3152
+ head?: never;
3153
+ patch?: never;
3154
+ trace?: never;
3155
+ };
3156
+ "/api/time-tracking/activity-hints": {
3157
+ parameters: {
3158
+ query?: never;
3159
+ header?: never;
3160
+ path?: never;
3161
+ cookie?: never;
3162
+ };
3163
+ /**
3164
+ * Job-scoped work I did recently — the input to the 'switch the clock?' prompt.
3165
+ * @description Activity NEVER creates time by itself; it only proposes rerouting time the geofence already justified. Window defaults to 4 hours and is capped at 24.
3166
+ */
3167
+ get: {
3168
+ parameters: {
3169
+ query?: {
3170
+ /** @description Clamped to at most 24 hours ago. */
3171
+ since?: string;
3172
+ };
3173
+ header?: {
3174
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
3175
+ "X-Client-Version"?: string;
3176
+ };
3177
+ path?: never;
3178
+ cookie?: never;
3179
+ };
3180
+ requestBody?: never;
3181
+ responses: {
3182
+ /** @description One row per job touched. */
3183
+ 200: {
3184
+ headers: {
3185
+ [name: string]: unknown;
3186
+ };
3187
+ content: {
3188
+ "application/json": {
3189
+ jobId: number;
3190
+ jobName: string;
3191
+ /** Format: date-time */
3192
+ firstAt: string;
3193
+ /** Format: date-time */
3194
+ lastAt: string;
3195
+ count: number;
3196
+ }[];
3197
+ };
3198
+ };
3199
+ /** @description Invalid `since`. */
3200
+ 400: {
3201
+ headers: {
3202
+ [name: string]: unknown;
3203
+ };
3204
+ content: {
3205
+ "application/json": components["schemas"]["ErrorEnvelope"];
3206
+ };
3207
+ };
3208
+ /** @description Not signed in. */
3209
+ 401: {
3210
+ headers: {
3211
+ [name: string]: unknown;
3212
+ };
3213
+ content: {
3214
+ "application/json": components["schemas"]["ErrorEnvelope"];
3215
+ };
3216
+ };
3217
+ /** @description Time tracking access denied, or no organization on the session. */
3218
+ 403: {
3219
+ headers: {
3220
+ [name: string]: unknown;
3221
+ };
3222
+ content: {
3223
+ "application/json": components["schemas"]["ErrorEnvelope"];
3224
+ };
3225
+ };
3226
+ /** @description Client major below minClientVersion. */
3227
+ 426: {
3228
+ headers: {
3229
+ [name: string]: unknown;
3230
+ };
3231
+ content: {
3232
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
3233
+ };
3234
+ };
3235
+ };
3236
+ };
3237
+ put?: never;
3238
+ post?: never;
3239
+ delete?: never;
3240
+ options?: never;
3241
+ head?: never;
3242
+ patch?: never;
3243
+ trace?: never;
3244
+ };
3245
+ "/api/me/week": {
3246
+ parameters: {
3247
+ query?: never;
3248
+ header?: never;
3249
+ path?: never;
3250
+ cookie?: never;
3251
+ };
3252
+ /**
3253
+ * My published schedule, my hours, and my time-off requests.
3254
+ * @description The clock-in expectation. Published shifts only. Job assignments are deliberately absent — the job calendar is where those live.
3255
+ */
3256
+ get: {
3257
+ parameters: {
3258
+ query?: {
3259
+ /** @description Any day inside the wanted week. Defaults to today; the response covers Sunday to Saturday. */
3260
+ date?: string;
3261
+ };
3262
+ header?: {
3263
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
3264
+ "X-Client-Version"?: string;
3265
+ };
3266
+ path?: never;
3267
+ cookie?: never;
3268
+ };
3269
+ requestBody?: never;
3270
+ responses: {
3271
+ /** @description The week. */
3272
+ 200: {
3273
+ headers: {
3274
+ [name: string]: unknown;
3275
+ };
3276
+ content: {
3277
+ "application/json": components["schemas"]["MyWeek"];
3278
+ };
3279
+ };
3280
+ /** @description Not signed in. */
3281
+ 401: {
3282
+ headers: {
3283
+ [name: string]: unknown;
3284
+ };
3285
+ content: {
3286
+ "application/json": components["schemas"]["ErrorEnvelope"];
3287
+ };
3288
+ };
3289
+ /** @description Client major below minClientVersion. */
3290
+ 426: {
3291
+ headers: {
3292
+ [name: string]: unknown;
3293
+ };
3294
+ content: {
3295
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
3296
+ };
3297
+ };
3298
+ };
3299
+ };
3300
+ put?: never;
3301
+ post?: never;
3302
+ delete?: never;
3303
+ options?: never;
3304
+ head?: never;
3305
+ patch?: never;
3306
+ trace?: never;
3307
+ };
3308
+ "/api/me/time-off": {
3309
+ parameters: {
3310
+ query?: never;
3311
+ header?: never;
3312
+ path?: never;
3313
+ cookie?: never;
3314
+ };
3315
+ get?: never;
3316
+ put?: never;
3317
+ /**
3318
+ * Ask for time off.
3319
+ * @description Goes to a manager. On approval the days are written onto the schedule as published all-day entries and the person is notified.
3320
+ */
3321
+ post: {
3322
+ parameters: {
3323
+ query?: never;
3324
+ header?: {
3325
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
3326
+ "X-Client-Version"?: string;
3327
+ };
3328
+ path?: never;
3329
+ cookie?: never;
3330
+ };
3331
+ requestBody?: {
3332
+ content: {
3333
+ "application/json": {
3334
+ /** @enum {string} */
3335
+ kind: "pto" | "off";
3336
+ /** Format: date */
3337
+ startDate: string;
3338
+ /** Format: date */
3339
+ endDate: string;
3340
+ note?: string | null;
3341
+ };
3342
+ };
3343
+ };
3344
+ responses: {
3345
+ /** @description Request queued for review. */
3346
+ 201: {
3347
+ headers: {
3348
+ [name: string]: unknown;
3349
+ };
3350
+ content: {
3351
+ "application/json": components["schemas"]["TimeOffRequestSummary"];
3352
+ };
3353
+ };
3354
+ /** @description Validation failure. */
3355
+ 400: {
3356
+ headers: {
3357
+ [name: string]: unknown;
3358
+ };
3359
+ content: {
3360
+ "application/json": components["schemas"]["ErrorEnvelope"];
3361
+ };
3362
+ };
3363
+ /** @description Not signed in. */
3364
+ 401: {
3365
+ headers: {
3366
+ [name: string]: unknown;
3367
+ };
3368
+ content: {
3369
+ "application/json": components["schemas"]["ErrorEnvelope"];
3370
+ };
3371
+ };
3372
+ /** @description A request for those dates is already waiting. */
3373
+ 409: {
3374
+ headers: {
3375
+ [name: string]: unknown;
3376
+ };
3377
+ content: {
3378
+ "application/json": components["schemas"]["ErrorEnvelope"];
3379
+ };
3380
+ };
3381
+ /** @description Client major below minClientVersion. */
3382
+ 426: {
3383
+ headers: {
3384
+ [name: string]: unknown;
3385
+ };
3386
+ content: {
3387
+ "application/json": components["schemas"]["UpgradeRequiredEnvelope"];
3388
+ };
3389
+ };
3390
+ };
3391
+ };
3392
+ delete?: never;
3393
+ options?: never;
3394
+ head?: never;
3395
+ patch?: never;
3396
+ trace?: never;
3397
+ };
1829
3398
  }
1830
3399
  export type webhooks = Record<string, never>;
1831
3400
  export interface components {
@@ -1850,7 +3419,7 @@ export interface components {
1850
3419
  };
1851
3420
  /** @description Cross-platform version handshake manifest. See docs/api-versioning.md. */
1852
3421
  VersionManifest: {
1853
- /** @example 1.2.409 */
3422
+ /** @example 1.2.411 */
1854
3423
  apiVersion: string;
1855
3424
  /** @example 1.0.0 */
1856
3425
  minClientVersion: string;
@@ -2450,6 +4019,397 @@ export interface components {
2450
4019
  RealtimeWebrtcRequest: {
2451
4020
  sdp: string;
2452
4021
  };
4022
+ /** @description One stretch of time at one site, inside a work day. durationMinutes stays gross; paid time is gross minus breakMinutes. */
4023
+ TimeEntry: {
4024
+ id: number;
4025
+ organizationId: string;
4026
+ userId: string;
4027
+ /** @description Null at the office — see siteType. */
4028
+ jobId: number | null;
4029
+ /** @enum {string} */
4030
+ siteType: "job" | "office";
4031
+ /** @description The work day this entry sits inside, when the caller is on the day model. */
4032
+ workDayId: number | null;
4033
+ budgetItemId: number | null;
4034
+ /** Format: date-time */
4035
+ clockIn: string;
4036
+ /**
4037
+ * Format: date-time
4038
+ * @description Null while the entry is open.
4039
+ */
4040
+ clockOut: string | null;
4041
+ clockInLat: number | null;
4042
+ clockInLng: number | null;
4043
+ clockOutLat: number | null;
4044
+ clockOutLng: number | null;
4045
+ /**
4046
+ * @description 'manual' (a button press) or 'auto' (the resolver).
4047
+ * @example auto
4048
+ */
4049
+ source: string;
4050
+ /** @description Time outside the job's Accepted→Completed window, and all office time: on the timesheet and in payroll, never in the job's labor actuals. */
4051
+ excludedFromBudget: boolean;
4052
+ /** @description GROSS minutes (clock-out minus clock-in). Subtract breakMinutes for paid time. */
4053
+ durationMinutes: number | null;
4054
+ notes: string | null;
4055
+ editedBy: string | null;
4056
+ /** Format: date-time */
4057
+ editedAt: string | null;
4058
+ /** Format: date-time */
4059
+ originalClockIn: string | null;
4060
+ /** Format: date-time */
4061
+ originalClockOut: string | null;
4062
+ /** Format: date-time */
4063
+ staleWarnedAt: string | null;
4064
+ /** @description Unpaid break minutes recorded against this entry. */
4065
+ breakMinutes: number;
4066
+ /** Format: date-time */
4067
+ createdAt: string | null;
4068
+ /** @description Enrichment on /active and /entries. 'Office' for an office entry. */
4069
+ jobName?: string;
4070
+ /** @description Enrichment on /entries and /active?scope=team. */
4071
+ userName?: string;
4072
+ };
4073
+ /** @description Where a day's minutes went (shared/workDays.ts splitDayMinutes). */
4074
+ WorkDayMinutes: {
4075
+ /** @description Gross minutes on the day; an open day is measured to now. */
4076
+ total: number;
4077
+ /** @description total minus unpaid breaks. */
4078
+ paid: number;
4079
+ office: number;
4080
+ jobs: number;
4081
+ /** @description paid minus office minus jobs — time between sites, shown rather than hidden. */
4082
+ drive: number;
4083
+ breaks: number;
4084
+ };
4085
+ /** @description The master clock. One per person per day; the day itself is the payroll number. */
4086
+ WorkDay: {
4087
+ id: number;
4088
+ userId: string;
4089
+ /** Format: date-time */
4090
+ clockIn: string;
4091
+ /**
4092
+ * Format: date-time
4093
+ * @description Null while the day is running.
4094
+ */
4095
+ clockOut: string | null;
4096
+ /**
4097
+ * @description 'tech' (the button), 'admin', or 'job_clock_in'.
4098
+ * @example tech
4099
+ */
4100
+ openedBy: string;
4101
+ /** @description 'tech', 'admin', or 'auto' (the stale safety net). */
4102
+ closedBy: string | null;
4103
+ /** @enum {string} */
4104
+ clockInSite: "office" | "job" | "away" | "unknown";
4105
+ /**
4106
+ * @description An away-from-office start waits for an admin unless the org auto-approves a start inside an assigned job.
4107
+ * @enum {string}
4108
+ */
4109
+ clockInReview: "not_needed" | "pending" | "approved" | "rejected";
4110
+ reviewedBy: string | null;
4111
+ /** Format: date-time */
4112
+ reviewedAt: string | null;
4113
+ reviewNote: string | null;
4114
+ /** @description The fix taken at the Clock In tap. Null when the tap had no usable position. */
4115
+ snapshot: {
4116
+ lat: number;
4117
+ lng: number;
4118
+ accuracyM: number | null;
4119
+ device: string | null;
4120
+ distanceToOfficeM: number | null;
4121
+ nearestJobId: number | null;
4122
+ nearestJobDistanceM: number | null;
4123
+ /** @description Reverse-geocoded after the tap, best effort. */
4124
+ address: string | null;
4125
+ nearestJob?: unknown;
4126
+ } | null;
4127
+ notes: string | null;
4128
+ editedBy: string | null;
4129
+ /** Format: date-time */
4130
+ editedAt: string | null;
4131
+ /** Format: date-time */
4132
+ originalClockIn: string | null;
4133
+ /** Format: date-time */
4134
+ originalClockOut: string | null;
4135
+ minutes: components["schemas"]["WorkDayMinutes"];
4136
+ entryIds: number[];
4137
+ /** @description Every admin edit to this day (time_edit_log). */
4138
+ edits: {
4139
+ id: number;
4140
+ editedBy: string | null;
4141
+ /** Format: date-time */
4142
+ editedAt: string;
4143
+ /** @description { field: { from, to } } */
4144
+ changes?: unknown;
4145
+ /** @description Required — a changed timesheet always says why. */
4146
+ note: string;
4147
+ }[];
4148
+ };
4149
+ /** @description The location gate (shared/locationConsent.ts). Without a signed consent the clock still runs by hand — only location stops. */
4150
+ LocationConsentSummary: {
4151
+ required: boolean;
4152
+ /** @description May this client use location for the clock right now? False → report no crossings, pings or fixes. */
4153
+ locationAllowed: boolean;
4154
+ needsSignature: boolean;
4155
+ version: string;
4156
+ };
4157
+ /** @description Everything the phone needs to draw the day card and decide what it may record. */
4158
+ MyDayResponse: {
4159
+ /** @description True once this person is on the day model. */
4160
+ dayModel: boolean;
4161
+ open: components["schemas"]["WorkDay"] & (Record<string, never> | null);
4162
+ today: components["schemas"]["WorkDay"][];
4163
+ /** @example America/New_York */
4164
+ timeZone: string;
4165
+ /** @description Set when the published work shift already ended and the day is still running — the 'Still working?' prompt. */
4166
+ shiftEnd: {
4167
+ /** @example 17:00 */
4168
+ endTime: string;
4169
+ minutesAgo: number;
4170
+ } | null;
4171
+ locationConsent: components["schemas"]["LocationConsentSummary"];
4172
+ locationTrail: boolean;
4173
+ /** @description The office fence, when the org profile address is on the map. */
4174
+ office: {
4175
+ lat: number;
4176
+ lng: number;
4177
+ radiusM: number;
4178
+ address: string | null;
4179
+ } | null;
4180
+ };
4181
+ /** @description The day after the tap. */
4182
+ DayClockResponse: {
4183
+ day: components["schemas"]["WorkDay"] & (Record<string, never> | null);
4184
+ };
4185
+ /** @description One fence to watch. iOS can register at most 20 regions, so watch the nearest 20 and re-pick as the device travels. */
4186
+ Geofence: {
4187
+ /** @description Null for the office fence (siteType 'office'). */
4188
+ jobId: number | null;
4189
+ /** @enum {string} */
4190
+ siteType: "job" | "office";
4191
+ jobName: string;
4192
+ jobNumber: string | null;
4193
+ lat: number;
4194
+ lng: number;
4195
+ /** @description The TRIGGER circle to hand CoreLocation. Deliberately large. */
4196
+ radius: number;
4197
+ /** @description The SITE circle the server verifies against. Small. Use it only to tell co-located fences apart, never to decide the clock. */
4198
+ siteRadius: number;
4199
+ location: string | null;
4200
+ eligible: boolean;
4201
+ eligibilityReason: string;
4202
+ stagePriority: number | null;
4203
+ /** Format: date-time */
4204
+ stageChangedAt: string | null;
4205
+ };
4206
+ /** @description One fence crossing as the device saw it. */
4207
+ ClockCrossing: {
4208
+ /** @description Client-generated UUID. Re-delivery is a no-op (unique index), so retry freely. */
4209
+ clientEventId: string;
4210
+ jobId?: number | null;
4211
+ /**
4212
+ * @description Defaults to 'office' when jobId is null, else 'job'.
4213
+ * @enum {string}
4214
+ */
4215
+ siteType?: "job" | "office";
4216
+ /**
4217
+ * @description enter / exit / present are fence events. 'sample' is a location fix taken during a dwell window — presence evidence the resolver weighs against the fence, never an event on its own.
4218
+ * @enum {string}
4219
+ */
4220
+ kind: "enter" | "exit" | "present" | "sample";
4221
+ /** Format: date-time */
4222
+ occurredAt: string;
4223
+ lat?: number | null;
4224
+ lng?: number | null;
4225
+ accuracyM?: number | null;
4226
+ /** @enum {string|null} */
4227
+ device?: "ios" | "web" | "android" | null;
4228
+ /** @description The user's auto-clock toggle at crossing time. False → the crossing is logged but never acted on. */
4229
+ autoClockEnabled?: boolean;
4230
+ };
4231
+ /** @description Always 202. The clock state to trust is `openEntries`, not the client's own guess. */
4232
+ CrossingsAck: {
4233
+ accepted: number;
4234
+ duplicates: number;
4235
+ /** @description Present when nothing was stored — see `reason`. */
4236
+ dropped?: number;
4237
+ /**
4238
+ * @description Why the batch was dropped. On 'no_consent' the client should clear its queue rather than retry.
4239
+ * @example no_consent
4240
+ */
4241
+ reason?: string | null;
4242
+ /** @description Result of the inline resolver kick. Null means it did not finish inside the deadline — the crossings ARE stored and the background tick will resolve them. Not an error; do not retry. */
4243
+ resolver: {
4244
+ applied: number;
4245
+ clockedIn: number;
4246
+ clockedOut: number;
4247
+ pending: number;
4248
+ } | null;
4249
+ resolverError: string | null;
4250
+ resolverSkip: string | null;
4251
+ /** @description The caller's open entries after resolution — the authoritative clock state. */
4252
+ openEntries: components["schemas"]["TimeEntry"][];
4253
+ };
4254
+ /** @description What this org's clock does (shared/timeClockSettings.ts). Several keys are platform-owned and read at fixed values regardless of any stored override. */
4255
+ ClockConfig: {
4256
+ serverResolver: boolean;
4257
+ /**
4258
+ * @description How long a device must stay on one side of a fence before it counts. A choice from a fixed set of steps, not a free number: a tech driving past a job must not be clocked in, and one walking in must not wait. An older org may still have a free value stored; the server snaps it to the nearest step, so read this value rather than assuming what was configured.
4259
+ * @enum {integer}
4260
+ */
4261
+ dwellSeconds: 30 | 60 | 100 | 120;
4262
+ staleShiftHours: number;
4263
+ autoCloseHours: number;
4264
+ presenceCheck: boolean;
4265
+ /**
4266
+ * @description What the phone does during a dwell. 'balanced' = cheap positioning, GPS only for the last ~30s. The battery lever an admin can pull without an app update — re-read this every few minutes.
4267
+ * @enum {string}
4268
+ */
4269
+ presenceSampling: "precise" | "balanced" | "off";
4270
+ switchOnArrival: boolean;
4271
+ impliedDeparture: boolean;
4272
+ heartbeat: boolean;
4273
+ siteRadiusM: number;
4274
+ /** @enum {string} */
4275
+ masterClock: "auto" | "on" | "off";
4276
+ awayClockInAutoApproveOnJob: boolean;
4277
+ lateGraceMinutes: number;
4278
+ shiftEndReminderMinutes: number;
4279
+ /** @description The master switch. Off → no fences, no automatic clock, no readings, no trail; people clock in by hand. */
4280
+ locationTracking: boolean;
4281
+ locationConsentRequired: boolean;
4282
+ locationTrail: boolean;
4283
+ locationTrailRetentionDays: number;
4284
+ afterHours: {
4285
+ /** @example 17:00 */
4286
+ start: string;
4287
+ /** @example 08:00 */
4288
+ end: string;
4289
+ weekends: boolean;
4290
+ };
4291
+ limits: {
4292
+ [key: string]: {
4293
+ min: number;
4294
+ max: number;
4295
+ };
4296
+ };
4297
+ /** @description True only for the primary account. */
4298
+ canEdit: boolean;
4299
+ };
4300
+ /** @description Always 202. */
4301
+ TrailPingResponse: {
4302
+ accepted: number;
4303
+ dropped: number;
4304
+ /** @description 'no_open_day' | 'no_consent' | 'trail_off' | null. Any non-null reason means stop sending until state changes. */
4305
+ reason: string | null;
4306
+ };
4307
+ /** @description One person, one day: working, on call, off, or PTO. Job assignments are NOT part of this schedule. */
4308
+ EmployeeShift: {
4309
+ id: number;
4310
+ organizationId: string;
4311
+ userId: string;
4312
+ /**
4313
+ * Format: date
4314
+ * @example 2026-09-16
4315
+ */
4316
+ shiftDate: string;
4317
+ /**
4318
+ * @description 'HH:MM' local. Null = all day, and always null for 'off' and 'pto'.
4319
+ * @example 08:00
4320
+ */
4321
+ startTime: string | null;
4322
+ /**
4323
+ * @description An end at or before the start means the shift runs overnight into the next day.
4324
+ * @example 17:00
4325
+ */
4326
+ endTime: string | null;
4327
+ /**
4328
+ * @description 'on_call' never sets a clock-in deadline — on call means reachable, not at work.
4329
+ * @enum {string}
4330
+ */
4331
+ kind: "work" | "on_call" | "off" | "pto";
4332
+ note: string | null;
4333
+ /**
4334
+ * Format: date-time
4335
+ * @description Null = DRAFT. /api/me/week only ever returns published rows.
4336
+ */
4337
+ publishedAt: string | null;
4338
+ createdBy: string | null;
4339
+ /** Format: date-time */
4340
+ createdAt: string | null;
4341
+ /** Format: date-time */
4342
+ updatedAt: string | null;
4343
+ };
4344
+ /** @description A time-off ask. Approval writes published all-day shifts onto the schedule for every date in the range. */
4345
+ TimeOffRequestSummary: {
4346
+ id: number;
4347
+ userId: string;
4348
+ /** @enum {string} */
4349
+ status: "pending" | "approved" | "rejected";
4350
+ /** @enum {string} */
4351
+ kind: "pto" | "off";
4352
+ /** Format: date */
4353
+ startDate: string;
4354
+ /** Format: date */
4355
+ endDate: string;
4356
+ note: string | null;
4357
+ /** Format: date-time */
4358
+ createdAt: string | null;
4359
+ reviewNote: string | null;
4360
+ /** Format: date-time */
4361
+ reviewedAt: string | null;
4362
+ };
4363
+ /** @description When I should be in, plus what I actually worked. The clock-in expectation lives here. */
4364
+ MyWeek: {
4365
+ /**
4366
+ * Format: date
4367
+ * @description Sunday of the week containing the anchor date.
4368
+ */
4369
+ from: string;
4370
+ /** Format: date */
4371
+ to: string;
4372
+ /**
4373
+ * Format: date
4374
+ * @description The org-local date, which is not necessarily the device's.
4375
+ */
4376
+ today: string;
4377
+ /** @description Keyed by org-local ISO date. */
4378
+ hours: {
4379
+ [key: string]: {
4380
+ paid: number;
4381
+ office: number;
4382
+ jobs: number;
4383
+ drive: number;
4384
+ breaks: number;
4385
+ /** @description True when the numbers come from work days; false when they were added up from entries. */
4386
+ fromDays: boolean;
4387
+ /** @description 'pending' | 'rejected' when that day's clock-in is under review. */
4388
+ review?: string;
4389
+ };
4390
+ };
4391
+ attendance: {
4392
+ /**
4393
+ * @description An exempt person is never called late or missing.
4394
+ * @enum {string}
4395
+ */
4396
+ mode: "tracked" | "exempt";
4397
+ lateGraceMinutes: number;
4398
+ dayModel: boolean;
4399
+ /** @description Whether a published work / on-call shift covers this minute, yesterday's overnight shifts included. Drives the missed clock-in prompt. */
4400
+ coveredNow: {
4401
+ work: boolean;
4402
+ onCall: boolean;
4403
+ };
4404
+ locationConsent: components["schemas"]["LocationConsentSummary"];
4405
+ };
4406
+ /** @description Published rows only — a draft the person has never seen can't make them late. */
4407
+ shifts: components["schemas"]["EmployeeShift"][];
4408
+ /** @description Every pending request, not only this week's. */
4409
+ timeOff: components["schemas"]["TimeOffRequestSummary"][];
4410
+ /** @description The last few decided ones, newest first. */
4411
+ recentTimeOff: components["schemas"]["TimeOffRequestSummary"][];
4412
+ };
2453
4413
  };
2454
4414
  responses: never;
2455
4415
  parameters: never;