@serviceai/api-spec 1.2.433 → 1.6.1

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 +2415 -141
  2. package/openapi.yaml +1837 -172
  3. package/package.json +1 -1
  4. package/types.d.ts +1033 -68
package/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "ServiceAi API",
5
- "version": "1.2.433",
5
+ "version": "1.6.1",
6
6
  "description": "Source-of-truth API contract for ServiceAi. Generated from `shared/schema.ts` Zod schemas by `scripts/build-api-spec.ts`. Versioning policy: see `docs/api-versioning.md`. The hand-written docs/ios-contract.md and docs/lidar-mobile-upload-contract.md are human-readable design notes only — this spec is the canonical wire contract.",
7
7
  "license": {
8
8
  "name": "Proprietary",
@@ -52,6 +52,10 @@
52
52
  "name": "chatty",
53
53
  "description": "Realtime voice-enabled AI assistant."
54
54
  },
55
+ {
56
+ "name": "checklists",
57
+ "description": "Job checklists: the voice walk-through draft."
58
+ },
55
59
  {
56
60
  "name": "time-tracking",
57
61
  "description": "The clock. A work day is the master clock and the payroll number; time entries sit inside it and say where the time went. Automatic clock-in/out is solved SERVER-side from reported fence crossings — a client reports what it saw and reads the outcome back."
@@ -66,6 +70,22 @@
66
70
  }
67
71
  ],
68
72
  "components": {
73
+ "securitySchemes": {
74
+ "UserBearer": {
75
+ "type": "http",
76
+ "scheme": "bearer"
77
+ },
78
+ "BrowserSession": {
79
+ "type": "apiKey",
80
+ "in": "cookie",
81
+ "name": "connect.sid"
82
+ },
83
+ "BrowserAuthCookie": {
84
+ "type": "apiKey",
85
+ "in": "cookie",
86
+ "name": "auth_token"
87
+ }
88
+ },
69
89
  "schemas": {
70
90
  "ErrorEnvelope": {
71
91
  "type": "object",
@@ -126,7 +146,7 @@
126
146
  "properties": {
127
147
  "apiVersion": {
128
148
  "type": "string",
129
- "example": "1.2.433"
149
+ "example": "1.6.1"
130
150
  },
131
151
  "minClientVersion": {
132
152
  "type": "string",
@@ -240,9 +260,6 @@
240
260
  {
241
261
  "type": "array",
242
262
  "items": {}
243
- },
244
- {
245
- "type": "null"
246
263
  }
247
264
  ]
248
265
  },
@@ -250,7 +267,8 @@
250
267
  "type": [
251
268
  "string",
252
269
  "null"
253
- ]
270
+ ],
271
+ "format": "date-time"
254
272
  },
255
273
  "parentContactId": {
256
274
  "type": [
@@ -412,7 +430,8 @@
412
430
  "type": [
413
431
  "string",
414
432
  "null"
415
- ]
433
+ ],
434
+ "format": "date-time"
416
435
  },
417
436
  "qboLastPushedHash": {
418
437
  "type": [
@@ -430,7 +449,8 @@
430
449
  "type": [
431
450
  "string",
432
451
  "null"
433
- ]
452
+ ],
453
+ "format": "date-time"
434
454
  },
435
455
  "deletedBy": {
436
456
  "type": [
@@ -639,9 +659,6 @@
639
659
  {
640
660
  "type": "array",
641
661
  "items": {}
642
- },
643
- {
644
- "type": "null"
645
662
  }
646
663
  ]
647
664
  },
@@ -672,9 +689,6 @@
672
689
  {
673
690
  "type": "array",
674
691
  "items": {}
675
- },
676
- {
677
- "type": "null"
678
692
  }
679
693
  ]
680
694
  },
@@ -707,9 +721,6 @@
707
721
  {
708
722
  "type": "array",
709
723
  "items": {}
710
- },
711
- {
712
- "type": "null"
713
724
  }
714
725
  ]
715
726
  },
@@ -734,9 +745,6 @@
734
745
  {
735
746
  "type": "array",
736
747
  "items": {}
737
- },
738
- {
739
- "type": "null"
740
748
  }
741
749
  ],
742
750
  "description": "Persisted append-room doorway links on a composite scan. Each link carries both a `sourceOpeningKind` (the appended child scan's entry side — always `door` in practice, since iOS does not capture openings on a single-room append payload) and a `targetOpeningKind` (`door` or `opening`) that controls kind-specific resolution against the parent scan: a link with `targetOpeningKind: \"door\"` indexes into the target room's `doors[]`, a link with `targetOpeningKind: \"opening\"` indexes into the target room's `openings[]`. The two arrays are never folded together. Windows are not traversable and never appear here."
@@ -762,9 +770,6 @@
762
770
  {
763
771
  "type": "array",
764
772
  "items": {}
765
- },
766
- {
767
- "type": "null"
768
773
  }
769
774
  ]
770
775
  },
@@ -807,9 +812,6 @@
807
812
  {
808
813
  "type": "array",
809
814
  "items": {}
810
- },
811
- {
812
- "type": "null"
813
815
  }
814
816
  ]
815
817
  },
@@ -834,9 +836,6 @@
834
836
  {
835
837
  "type": "array",
836
838
  "items": {}
837
- },
838
- {
839
- "type": "null"
840
839
  }
841
840
  ]
842
841
  },
@@ -846,10 +845,7 @@
846
845
  "null"
847
846
  ],
848
847
  "items": {
849
- "type": [
850
- "string",
851
- "null"
852
- ]
848
+ "type": "string"
853
849
  }
854
850
  },
855
851
  "metricCorrection": {
@@ -892,7 +888,8 @@
892
888
  "type": [
893
889
  "string",
894
890
  "null"
895
- ]
891
+ ],
892
+ "format": "date-time"
896
893
  },
897
894
  "captureMode": {
898
895
  "type": [
@@ -912,7 +909,15 @@
912
909
  "type": [
913
910
  "string",
914
911
  "null"
915
- ]
912
+ ],
913
+ "format": "date-time"
914
+ },
915
+ "clientScanId": {
916
+ "type": [
917
+ "string",
918
+ "null"
919
+ ],
920
+ "maxLength": 64
916
921
  },
917
922
  "floorPlanAssetId": {
918
923
  "type": [
@@ -960,6 +965,54 @@
960
965
  "minimum": -2147483648,
961
966
  "maximum": 2147483647
962
967
  },
968
+ "sourceSketchId": {
969
+ "type": [
970
+ "integer",
971
+ "null"
972
+ ],
973
+ "minimum": -2147483648,
974
+ "maximum": 2147483647
975
+ },
976
+ "takeoffPagePixelsPerMeter": {
977
+ "type": [
978
+ "number",
979
+ "null"
980
+ ],
981
+ "minimum": -140737488355328,
982
+ "maximum": 140737488355327
983
+ },
984
+ "takeoffPageOffsetX": {
985
+ "type": [
986
+ "number",
987
+ "null"
988
+ ],
989
+ "minimum": -140737488355328,
990
+ "maximum": 140737488355327
991
+ },
992
+ "takeoffPageOffsetY": {
993
+ "type": [
994
+ "number",
995
+ "null"
996
+ ],
997
+ "minimum": -140737488355328,
998
+ "maximum": 140737488355327
999
+ },
1000
+ "takeoffPageWidthPx": {
1001
+ "type": [
1002
+ "integer",
1003
+ "null"
1004
+ ],
1005
+ "minimum": -2147483648,
1006
+ "maximum": 2147483647
1007
+ },
1008
+ "takeoffPageHeightPx": {
1009
+ "type": [
1010
+ "integer",
1011
+ "null"
1012
+ ],
1013
+ "minimum": -2147483648,
1014
+ "maximum": 2147483647
1015
+ },
963
1016
  "floorId": {
964
1017
  "type": [
965
1018
  "integer",
@@ -984,7 +1037,8 @@
984
1037
  "type": [
985
1038
  "string",
986
1039
  "null"
987
- ]
1040
+ ],
1041
+ "format": "date-time"
988
1042
  },
989
1043
  "deletedBy": {
990
1044
  "type": [
@@ -1063,13 +1117,15 @@
1063
1117
  "type": [
1064
1118
  "string",
1065
1119
  "null"
1066
- ]
1120
+ ],
1121
+ "format": "date-time"
1067
1122
  },
1068
1123
  "dueDate": {
1069
1124
  "type": [
1070
1125
  "string",
1071
1126
  "null"
1072
- ]
1127
+ ],
1128
+ "format": "date-time"
1073
1129
  },
1074
1130
  "allDay": {
1075
1131
  "type": [
@@ -1110,9 +1166,6 @@
1110
1166
  {
1111
1167
  "type": "array",
1112
1168
  "items": {}
1113
- },
1114
- {
1115
- "type": "null"
1116
1169
  }
1117
1170
  ]
1118
1171
  },
@@ -1137,9 +1190,6 @@
1137
1190
  {
1138
1191
  "type": "array",
1139
1192
  "items": {}
1140
- },
1141
- {
1142
- "type": "null"
1143
1193
  }
1144
1194
  ]
1145
1195
  },
@@ -1178,9 +1228,6 @@
1178
1228
  {
1179
1229
  "type": "array",
1180
1230
  "items": {}
1181
- },
1182
- {
1183
- "type": "null"
1184
1231
  }
1185
1232
  ]
1186
1233
  },
@@ -1205,9 +1252,6 @@
1205
1252
  {
1206
1253
  "type": "array",
1207
1254
  "items": {}
1208
- },
1209
- {
1210
- "type": "null"
1211
1255
  }
1212
1256
  ]
1213
1257
  },
@@ -1223,7 +1267,8 @@
1223
1267
  "30_min",
1224
1268
  "1_hour",
1225
1269
  "1_day",
1226
- "1_week"
1270
+ "1_week",
1271
+ null
1227
1272
  ]
1228
1273
  },
1229
1274
  "reminderNotificationId": {
@@ -1258,7 +1303,8 @@
1258
1303
  "type": [
1259
1304
  "string",
1260
1305
  "null"
1261
- ]
1306
+ ],
1307
+ "format": "date-time"
1262
1308
  },
1263
1309
  "parentTaskId": {
1264
1310
  "type": [
@@ -1272,7 +1318,8 @@
1272
1318
  "type": [
1273
1319
  "string",
1274
1320
  "null"
1275
- ]
1321
+ ],
1322
+ "format": "date-time"
1276
1323
  },
1277
1324
  "isRecurrenceException": {
1278
1325
  "type": [
@@ -1301,9 +1348,6 @@
1301
1348
  {
1302
1349
  "type": "array",
1303
1350
  "items": {}
1304
- },
1305
- {
1306
- "type": "null"
1307
1351
  }
1308
1352
  ]
1309
1353
  },
@@ -1319,7 +1363,8 @@
1319
1363
  "type": [
1320
1364
  "string",
1321
1365
  "null"
1322
- ]
1366
+ ],
1367
+ "format": "date-time"
1323
1368
  },
1324
1369
  "deletedBy": {
1325
1370
  "type": [
@@ -1374,7 +1419,8 @@
1374
1419
  "type": [
1375
1420
  "string",
1376
1421
  "null"
1377
- ]
1422
+ ],
1423
+ "format": "date-time"
1378
1424
  },
1379
1425
  "deletedBy": {
1380
1426
  "type": [
@@ -1457,9 +1503,6 @@
1457
1503
  {
1458
1504
  "type": "array",
1459
1505
  "items": {}
1460
- },
1461
- {
1462
- "type": "null"
1463
1506
  }
1464
1507
  ]
1465
1508
  },
@@ -1469,10 +1512,7 @@
1469
1512
  "null"
1470
1513
  ],
1471
1514
  "items": {
1472
- "type": [
1473
- "string",
1474
- "null"
1475
- ]
1515
+ "type": "string"
1476
1516
  }
1477
1517
  },
1478
1518
  "uploadedBy": {
@@ -1485,7 +1525,8 @@
1485
1525
  "type": [
1486
1526
  "string",
1487
1527
  "null"
1488
- ]
1528
+ ],
1529
+ "format": "date-time"
1489
1530
  },
1490
1531
  "deletedBy": {
1491
1532
  "type": [
@@ -1540,7 +1581,8 @@
1540
1581
  "type": [
1541
1582
  "string",
1542
1583
  "null"
1543
- ]
1584
+ ],
1585
+ "format": "date-time"
1544
1586
  },
1545
1587
  "deletedBy": {
1546
1588
  "type": [
@@ -1682,9 +1724,6 @@
1682
1724
  {
1683
1725
  "type": "array",
1684
1726
  "items": {}
1685
- },
1686
- {
1687
- "type": "null"
1688
1727
  }
1689
1728
  ]
1690
1729
  },
@@ -1710,19 +1749,22 @@
1710
1749
  "type": [
1711
1750
  "string",
1712
1751
  "null"
1713
- ]
1752
+ ],
1753
+ "format": "date-time"
1714
1754
  },
1715
1755
  "sentAt": {
1716
1756
  "type": [
1717
1757
  "string",
1718
1758
  "null"
1719
- ]
1759
+ ],
1760
+ "format": "date-time"
1720
1761
  },
1721
1762
  "approvedAt": {
1722
1763
  "type": [
1723
1764
  "string",
1724
1765
  "null"
1725
- ]
1766
+ ],
1767
+ "format": "date-time"
1726
1768
  },
1727
1769
  "approvedBy": {
1728
1770
  "type": [
@@ -1734,7 +1776,8 @@
1734
1776
  "type": [
1735
1777
  "string",
1736
1778
  "null"
1737
- ]
1779
+ ],
1780
+ "format": "date-time"
1738
1781
  },
1739
1782
  "rejectionReason": {
1740
1783
  "type": [
@@ -1746,7 +1789,8 @@
1746
1789
  "type": [
1747
1790
  "string",
1748
1791
  "null"
1749
- ]
1792
+ ],
1793
+ "format": "date-time"
1750
1794
  },
1751
1795
  "signatureMeta": {
1752
1796
  "anyOf": [
@@ -1769,9 +1813,6 @@
1769
1813
  {
1770
1814
  "type": "array",
1771
1815
  "items": {}
1772
- },
1773
- {
1774
- "type": "null"
1775
1816
  }
1776
1817
  ]
1777
1818
  },
@@ -1809,7 +1850,8 @@
1809
1850
  "type": [
1810
1851
  "string",
1811
1852
  "null"
1812
- ]
1853
+ ],
1854
+ "format": "date-time"
1813
1855
  },
1814
1856
  "revisionNotes": {
1815
1857
  "type": [
@@ -1821,7 +1863,8 @@
1821
1863
  "type": [
1822
1864
  "string",
1823
1865
  "null"
1824
- ]
1866
+ ],
1867
+ "format": "date-time"
1825
1868
  },
1826
1869
  "convertedToInvoiceId": {
1827
1870
  "type": [
@@ -1835,7 +1878,8 @@
1835
1878
  "type": [
1836
1879
  "string",
1837
1880
  "null"
1838
- ]
1881
+ ],
1882
+ "format": "date-time"
1839
1883
  },
1840
1884
  "voidedBy": {
1841
1885
  "type": [
@@ -1883,7 +1927,8 @@
1883
1927
  "type": [
1884
1928
  "string",
1885
1929
  "null"
1886
- ]
1930
+ ],
1931
+ "format": "date-time"
1887
1932
  },
1888
1933
  "qboId": {
1889
1934
  "type": [
@@ -1901,7 +1946,8 @@
1901
1946
  "type": [
1902
1947
  "string",
1903
1948
  "null"
1904
- ]
1949
+ ],
1950
+ "format": "date-time"
1905
1951
  },
1906
1952
  "qboLastPushedHash": {
1907
1953
  "type": [
@@ -1928,11 +1974,26 @@
1928
1974
  ]
1929
1975
  },
1930
1976
  "qboTotalsAt": {
1977
+ "type": [
1978
+ "string",
1979
+ "null"
1980
+ ],
1981
+ "format": "date-time"
1982
+ },
1983
+ "sourceKind": {
1931
1984
  "type": [
1932
1985
  "string",
1933
1986
  "null"
1934
1987
  ]
1935
1988
  },
1989
+ "takeoffScanId": {
1990
+ "type": [
1991
+ "integer",
1992
+ "null"
1993
+ ],
1994
+ "minimum": -2147483648,
1995
+ "maximum": 2147483647
1996
+ },
1936
1997
  "createdBy": {
1937
1998
  "type": [
1938
1999
  "string",
@@ -1943,7 +2004,8 @@
1943
2004
  "type": [
1944
2005
  "string",
1945
2006
  "null"
1946
- ]
2007
+ ],
2008
+ "format": "date-time"
1947
2009
  },
1948
2010
  "deletedBy": {
1949
2011
  "type": [
@@ -2103,12 +2165,14 @@
2103
2165
  "isOpening": {
2104
2166
  "type": "boolean"
2105
2167
  }
2106
- }
2168
+ },
2169
+ "additionalProperties": {}
2107
2170
  },
2108
2171
  "captureMethod": {
2109
2172
  "type": "string"
2110
2173
  }
2111
- }
2174
+ },
2175
+ "additionalProperties": {}
2112
2176
  }
2113
2177
  },
2114
2178
  "doors": {
@@ -2139,6 +2203,11 @@
2139
2203
  "type": "number",
2140
2204
  "exclusiveMinimum": 0
2141
2205
  },
2206
+ "sillHeightM": {
2207
+ "type": "number",
2208
+ "minimum": 0,
2209
+ "maximum": 50
2210
+ },
2142
2211
  "hostWallIndex": {
2143
2212
  "type": [
2144
2213
  "integer",
@@ -2201,6 +2270,11 @@
2201
2270
  "type": "number",
2202
2271
  "exclusiveMinimum": 0
2203
2272
  },
2273
+ "sillHeightM": {
2274
+ "type": "number",
2275
+ "minimum": 0,
2276
+ "maximum": 50
2277
+ },
2204
2278
  "hostWallIndex": {
2205
2279
  "type": [
2206
2280
  "integer",
@@ -2263,6 +2337,11 @@
2263
2337
  "type": "number",
2264
2338
  "exclusiveMinimum": 0
2265
2339
  },
2340
+ "sillHeightM": {
2341
+ "type": "number",
2342
+ "minimum": 0,
2343
+ "maximum": 50
2344
+ },
2266
2345
  "hostWallIndex": {
2267
2346
  "type": [
2268
2347
  "integer",
@@ -2342,7 +2421,8 @@
2342
2421
  "required": [
2343
2422
  "id",
2344
2423
  "position"
2345
- ]
2424
+ ],
2425
+ "additionalProperties": {}
2346
2426
  }
2347
2427
  },
2348
2428
  "objects": {
@@ -2385,7 +2465,8 @@
2385
2465
  "rotation": {
2386
2466
  "type": "number"
2387
2467
  }
2388
- }
2468
+ },
2469
+ "additionalProperties": {}
2389
2470
  }
2390
2471
  },
2391
2472
  "boundaryPolygon": {
@@ -2414,6 +2495,7 @@
2414
2495
  "type": "string"
2415
2496
  }
2416
2497
  },
2498
+ "additionalProperties": {},
2417
2499
  "description": "Mobile-upload scan room. `doors`, `windows`, and `openings` are three distinct non-overlapping arrays — array membership IS the discriminator (there is no `kind` / `type` field on the item itself). Append-room `entryDoorwayId` of the form `scan-<scanId>-room-<roomLocalId>-(door|opening)-<idx>` resolves kind-specifically against the parent scan's matching array only: a `door` segment indexes `doors[]`, an `opening` segment indexes `openings[]`. Windows are not traversable and never appear as an `entryDoorwayId` target."
2418
2500
  }
2419
2501
  }
@@ -2421,6 +2503,7 @@
2421
2503
  "required": [
2422
2504
  "rooms"
2423
2505
  ],
2506
+ "additionalProperties": {},
2424
2507
  "description": "RoomPlan JSON envelope. `rooms[]` carries the per-room geometry (walls/doors/windows/openings/pins/objects/boundaryPolygon). iOS continues to upload positions in the RoomPlan-native frame; the server applies the view-alignment intake transform once at `/scans/mobile-upload` (see docs/lidar-contract-v4.md)."
2425
2508
  },
2426
2509
  "name": {
@@ -2617,6 +2700,7 @@
2617
2700
  "roomPlanJSON",
2618
2701
  "name"
2619
2702
  ],
2703
+ "additionalProperties": {},
2620
2704
  "description": "POST /api/jobs/:jobId/scans/mobile-upload body. v5 of the LiDAR contract (Task #879) — additive over v3/v4. Required: `roomPlanJSON` (object with nested `rooms` array) + `name`. All v3 and v4 payloads remain accepted byte-for-byte. New first-class fields: per-room `roomLocalId`, `pins[]`, `objects[]`, `boundaryPolygon[]`, per-wall `captureMetadata`; append-room `parentScanId`, `entryDoorwayId`, `entryDoorwayWallIndex`, `entryDoorwayPosition`; opening `position` is 2D `{x, y}` (an additional `z` is tolerated for backwards compatibility but ignored by the server). `wallConfidences` advertises the canonical nested `(number[] | null)[]` shape; the two legacy shapes (flat `number[]` and `Record<roomLocalIdOrIndex, number[]>`) are still parsed by `normalizeWallConfidences` in `server/routes/lidar.ts`."
2621
2705
  },
2622
2706
  "MobileUploadResponse": {
@@ -2684,6 +2768,39 @@
2684
2768
  "roomConnections"
2685
2769
  ]
2686
2770
  },
2771
+ "EventStreamTicketResponse": {
2772
+ "type": "object",
2773
+ "properties": {
2774
+ "ticket": {
2775
+ "type": "string",
2776
+ "minLength": 1,
2777
+ "maxLength": 8192
2778
+ }
2779
+ },
2780
+ "required": [
2781
+ "ticket"
2782
+ ],
2783
+ "additionalProperties": false
2784
+ },
2785
+ "StreamAuthenticationError": {
2786
+ "type": "object",
2787
+ "properties": {
2788
+ "message": {
2789
+ "type": "string"
2790
+ },
2791
+ "code": {
2792
+ "type": "string",
2793
+ "enum": [
2794
+ "IMPERSONATION_UNAVAILABLE",
2795
+ "SUPPORT_IMPERSONATION_UNAVAILABLE",
2796
+ "auth_unavailable"
2797
+ ]
2798
+ }
2799
+ },
2800
+ "required": [
2801
+ "message"
2802
+ ]
2803
+ },
2687
2804
  "JobSummary": {
2688
2805
  "type": "object",
2689
2806
  "properties": {
@@ -3337,8 +3454,8 @@
3337
3454
  },
3338
3455
  "source": {
3339
3456
  "type": "string",
3340
- "description": "'manual' (a button press) or 'auto' (the resolver).",
3341
- "example": "auto"
3457
+ "example": "auto",
3458
+ "description": "'manual' (a button press) or 'auto' (the resolver)."
3342
3459
  },
3343
3460
  "excludedFromBudget": {
3344
3461
  "type": "boolean",
@@ -3402,7 +3519,8 @@
3402
3519
  ],
3403
3520
  "enum": [
3404
3521
  "away",
3405
- "no_location"
3522
+ "no_location",
3523
+ null
3406
3524
  ],
3407
3525
  "description": "Job time started by hand away from the job ('away') or with no usable location ('no_location'); null for a normal start. The office decides it."
3408
3526
  },
@@ -3421,7 +3539,8 @@
3421
3539
  "enum": [
3422
3540
  "approved",
3423
3541
  "not_approved",
3424
- "moved"
3542
+ "moved",
3543
+ null
3425
3544
  ],
3426
3545
  "description": "The office's decision on a flagged start; null while it waits. 'not_approved' stays on the timesheet but off the job's costing."
3427
3546
  },
@@ -3555,8 +3674,8 @@
3555
3674
  },
3556
3675
  "openedBy": {
3557
3676
  "type": "string",
3558
- "description": "'tech' (the button), 'admin', or 'job_clock_in'.",
3559
- "example": "tech"
3677
+ "example": "tech",
3678
+ "description": "'tech' (the button), 'admin', or 'job_clock_in'."
3560
3679
  },
3561
3680
  "closedBy": {
3562
3681
  "type": [
@@ -4121,7 +4240,8 @@
4121
4240
  "enum": [
4122
4241
  "ios",
4123
4242
  "web",
4124
- "android"
4243
+ "android",
4244
+ null
4125
4245
  ]
4126
4246
  },
4127
4247
  "autoClockEnabled": {
@@ -4176,8 +4296,8 @@
4176
4296
  "string",
4177
4297
  "null"
4178
4298
  ],
4179
- "description": "Why the batch was dropped. On 'no_consent' the client should clear its queue rather than retry.",
4180
- "example": "no_consent"
4299
+ "example": "no_consent",
4300
+ "description": "Why the batch was dropped. On 'no_consent' the client should clear its queue rather than retry."
4181
4301
  },
4182
4302
  "resolver": {
4183
4303
  "type": [
@@ -4244,13 +4364,13 @@
4244
4364
  },
4245
4365
  "dwellSeconds": {
4246
4366
  "type": "integer",
4247
- "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.",
4248
4367
  "enum": [
4249
4368
  30,
4250
4369
  60,
4251
4370
  100,
4252
4371
  120
4253
- ]
4372
+ ],
4373
+ "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."
4254
4374
  },
4255
4375
  "staleShiftHours": {
4256
4376
  "type": "integer"
@@ -4431,16 +4551,16 @@
4431
4551
  "string",
4432
4552
  "null"
4433
4553
  ],
4434
- "description": "'HH:MM' local. Null = all day, and always null for 'off' and 'pto'.",
4435
- "example": "08:00"
4554
+ "example": "08:00",
4555
+ "description": "'HH:MM' local. Null = all day, and always null for 'off' and 'pto'."
4436
4556
  },
4437
4557
  "endTime": {
4438
4558
  "type": [
4439
4559
  "string",
4440
4560
  "null"
4441
4561
  ],
4442
- "description": "An end at or before the start means the shift runs overnight into the next day.",
4443
- "example": "17:00"
4562
+ "example": "17:00",
4563
+ "description": "An end at or before the start means the shift runs overnight into the next day."
4444
4564
  },
4445
4565
  "kind": {
4446
4566
  "type": "string",
@@ -4794,19 +4914,1564 @@
4794
4914
  "requestedAt"
4795
4915
  ],
4796
4916
  "description": "The signed-in person's request to have their account deleted. pending: waiting for the company; completed: the company marked it done; cancelled: the company cancelled it (asking again starts a new one)."
4797
- }
4798
- },
4799
- "parameters": {}
4800
- },
4801
- "paths": {
4802
- "/api/version": {
4803
- "get": {
4804
- "summary": "Cross-platform version handshake manifest.",
4805
- "tags": [
4806
- "version"
4807
- ],
4808
- "responses": {
4809
- "200": {
4917
+ },
4918
+ "DoorwayEditCommand": {
4919
+ "oneOf": [
4920
+ {
4921
+ "type": "object",
4922
+ "properties": {
4923
+ "action": {
4924
+ "type": "string",
4925
+ "enum": [
4926
+ "delete"
4927
+ ]
4928
+ },
4929
+ "roomId": {
4930
+ "type": "integer",
4931
+ "exclusiveMinimum": 0
4932
+ },
4933
+ "openingId": {
4934
+ "type": "string",
4935
+ "pattern": "^[A-Za-z0-9:_-]{1,128}$"
4936
+ },
4937
+ "revision": {
4938
+ "type": "string",
4939
+ "pattern": "^[a-f0-9]{64}$"
4940
+ },
4941
+ "confirmed": {
4942
+ "type": "boolean",
4943
+ "enum": [
4944
+ true
4945
+ ]
4946
+ }
4947
+ },
4948
+ "required": [
4949
+ "action",
4950
+ "roomId",
4951
+ "openingId",
4952
+ "revision",
4953
+ "confirmed"
4954
+ ],
4955
+ "additionalProperties": false
4956
+ },
4957
+ {
4958
+ "type": "object",
4959
+ "properties": {
4960
+ "action": {
4961
+ "type": "string",
4962
+ "enum": [
4963
+ "undo"
4964
+ ]
4965
+ },
4966
+ "token": {
4967
+ "type": "string",
4968
+ "minLength": 1,
4969
+ "maxLength": 1500100
4970
+ }
4971
+ },
4972
+ "required": [
4973
+ "action",
4974
+ "token"
4975
+ ],
4976
+ "additionalProperties": false
4977
+ },
4978
+ {
4979
+ "type": "object",
4980
+ "properties": {
4981
+ "action": {
4982
+ "type": "string",
4983
+ "enum": [
4984
+ "connect"
4985
+ ]
4986
+ },
4987
+ "rooms": {
4988
+ "type": "array",
4989
+ "items": {
4990
+ "type": "object",
4991
+ "properties": {
4992
+ "id": {
4993
+ "type": "integer",
4994
+ "exclusiveMinimum": 0
4995
+ },
4996
+ "revision": {
4997
+ "type": "string",
4998
+ "pattern": "^[a-f0-9]{64}$"
4999
+ },
5000
+ "geometry": {
5001
+ "type": "object",
5002
+ "additionalProperties": {}
5003
+ }
5004
+ },
5005
+ "required": [
5006
+ "id",
5007
+ "revision",
5008
+ "geometry"
5009
+ ],
5010
+ "additionalProperties": false
5011
+ }
5012
+ },
5013
+ "links": {
5014
+ "type": "array",
5015
+ "items": {
5016
+ "type": "object",
5017
+ "properties": {
5018
+ "roomAId": {
5019
+ "type": "integer",
5020
+ "exclusiveMinimum": 0
5021
+ },
5022
+ "roomBId": {
5023
+ "type": "integer",
5024
+ "exclusiveMinimum": 0
5025
+ },
5026
+ "doorAIndex": {
5027
+ "type": "integer",
5028
+ "minimum": 0,
5029
+ "maximum": 63
5030
+ },
5031
+ "doorBIndex": {
5032
+ "type": "integer",
5033
+ "minimum": 0,
5034
+ "maximum": 63
5035
+ }
5036
+ },
5037
+ "required": [
5038
+ "roomAId",
5039
+ "roomBId",
5040
+ "doorAIndex",
5041
+ "doorBIndex"
5042
+ ],
5043
+ "additionalProperties": false
5044
+ },
5045
+ "minItems": 1,
5046
+ "maxItems": 64
5047
+ }
5048
+ },
5049
+ "required": [
5050
+ "action",
5051
+ "rooms",
5052
+ "links"
5053
+ ],
5054
+ "additionalProperties": false
5055
+ }
5056
+ ]
5057
+ },
5058
+ "DoorwayEditResult": {
5059
+ "type": "object",
5060
+ "properties": {
5061
+ "ok": {
5062
+ "type": "boolean",
5063
+ "enum": [
5064
+ true
5065
+ ]
5066
+ },
5067
+ "token": {
5068
+ "type": "string"
5069
+ },
5070
+ "roomIds": {
5071
+ "type": "array",
5072
+ "items": {
5073
+ "type": "integer"
5074
+ }
5075
+ }
5076
+ },
5077
+ "required": [
5078
+ "ok",
5079
+ "token",
5080
+ "roomIds"
5081
+ ]
5082
+ },
5083
+ "WalkthroughDraftRequest": {
5084
+ "type": "object",
5085
+ "properties": {
5086
+ "transcript": {
5087
+ "type": "array",
5088
+ "items": {
5089
+ "type": "object",
5090
+ "properties": {
5091
+ "text": {
5092
+ "type": "string",
5093
+ "minLength": 1,
5094
+ "maxLength": 1000
5095
+ },
5096
+ "startedAt": {
5097
+ "type": "string",
5098
+ "format": "date-time"
5099
+ },
5100
+ "endedAt": {
5101
+ "type": "string",
5102
+ "format": "date-time"
5103
+ }
5104
+ },
5105
+ "required": [
5106
+ "text",
5107
+ "startedAt",
5108
+ "endedAt"
5109
+ ]
5110
+ },
5111
+ "minItems": 1,
5112
+ "maxItems": 400
5113
+ },
5114
+ "photos": {
5115
+ "type": "array",
5116
+ "items": {
5117
+ "type": "object",
5118
+ "properties": {
5119
+ "assetId": {
5120
+ "type": "string",
5121
+ "minLength": 1,
5122
+ "maxLength": 64
5123
+ },
5124
+ "capturedAt": {
5125
+ "type": "string",
5126
+ "format": "date-time"
5127
+ },
5128
+ "roomHint": {
5129
+ "type": [
5130
+ "string",
5131
+ "null"
5132
+ ],
5133
+ "maxLength": 200
5134
+ },
5135
+ "sketchRoomId": {
5136
+ "type": [
5137
+ "integer",
5138
+ "null"
5139
+ ],
5140
+ "exclusiveMinimum": 0
5141
+ },
5142
+ "capturePose": {
5143
+ "type": [
5144
+ "object",
5145
+ "null"
5146
+ ],
5147
+ "properties": {
5148
+ "x": {
5149
+ "type": "number"
5150
+ },
5151
+ "y": {
5152
+ "type": "number"
5153
+ },
5154
+ "z": {
5155
+ "type": "number"
5156
+ },
5157
+ "floorId": {
5158
+ "type": [
5159
+ "integer",
5160
+ "null"
5161
+ ],
5162
+ "exclusiveMinimum": 0
5163
+ },
5164
+ "sketchId": {
5165
+ "type": [
5166
+ "integer",
5167
+ "null"
5168
+ ],
5169
+ "exclusiveMinimum": 0
5170
+ },
5171
+ "scanSessionId": {
5172
+ "type": "string",
5173
+ "maxLength": 64
5174
+ },
5175
+ "scanRoomLocalId": {
5176
+ "type": "string",
5177
+ "maxLength": 128
5178
+ },
5179
+ "scanRoomName": {
5180
+ "type": "string",
5181
+ "maxLength": 200
5182
+ }
5183
+ }
5184
+ }
5185
+ },
5186
+ "required": [
5187
+ "assetId",
5188
+ "capturedAt"
5189
+ ]
5190
+ },
5191
+ "maxItems": 200
5192
+ },
5193
+ "sections": {
5194
+ "type": "array",
5195
+ "items": {
5196
+ "type": "object",
5197
+ "properties": {
5198
+ "name": {
5199
+ "type": "string",
5200
+ "minLength": 1,
5201
+ "maxLength": 200
5202
+ },
5203
+ "startedAt": {
5204
+ "type": "string",
5205
+ "format": "date-time"
5206
+ },
5207
+ "sketchRoomId": {
5208
+ "type": [
5209
+ "integer",
5210
+ "null"
5211
+ ],
5212
+ "exclusiveMinimum": 0
5213
+ }
5214
+ },
5215
+ "required": [
5216
+ "name",
5217
+ "startedAt"
5218
+ ]
5219
+ },
5220
+ "maxItems": 100
5221
+ },
5222
+ "roomsFrom": {
5223
+ "type": "string",
5224
+ "enum": [
5225
+ "speech",
5226
+ "scan"
5227
+ ]
5228
+ }
5229
+ },
5230
+ "required": [
5231
+ "transcript"
5232
+ ]
5233
+ },
5234
+ "ChecklistDraft": {
5235
+ "type": "object",
5236
+ "properties": {
5237
+ "name": {
5238
+ "type": "string"
5239
+ },
5240
+ "sections": {
5241
+ "type": "array",
5242
+ "items": {
5243
+ "type": "object",
5244
+ "properties": {
5245
+ "key": {
5246
+ "type": "string"
5247
+ },
5248
+ "title": {
5249
+ "type": "string"
5250
+ },
5251
+ "sketchRoomId": {
5252
+ "type": [
5253
+ "integer",
5254
+ "null"
5255
+ ]
5256
+ },
5257
+ "kind": {
5258
+ "type": "string",
5259
+ "enum": [
5260
+ "room",
5261
+ "custom",
5262
+ "not_sure"
5263
+ ]
5264
+ },
5265
+ "items": {
5266
+ "type": "array",
5267
+ "items": {
5268
+ "type": "object",
5269
+ "properties": {
5270
+ "key": {
5271
+ "type": "string"
5272
+ },
5273
+ "itemText": {
5274
+ "type": "string"
5275
+ },
5276
+ "description": {
5277
+ "type": [
5278
+ "string",
5279
+ "null"
5280
+ ]
5281
+ },
5282
+ "itemType": {
5283
+ "type": "string",
5284
+ "enum": [
5285
+ "checkbox",
5286
+ "yes/no",
5287
+ "text",
5288
+ "number",
5289
+ "date",
5290
+ "dropdown",
5291
+ "multi-select",
5292
+ "photo",
5293
+ "signature"
5294
+ ]
5295
+ },
5296
+ "required": {
5297
+ "type": "boolean"
5298
+ },
5299
+ "options": {
5300
+ "type": [
5301
+ "object",
5302
+ "null"
5303
+ ],
5304
+ "additionalProperties": {}
5305
+ },
5306
+ "referenceMedia": {
5307
+ "type": "array",
5308
+ "items": {
5309
+ "type": "object",
5310
+ "properties": {
5311
+ "assetId": {
5312
+ "type": "string",
5313
+ "minLength": 1,
5314
+ "maxLength": 64
5315
+ },
5316
+ "caption": {
5317
+ "type": [
5318
+ "string",
5319
+ "null"
5320
+ ],
5321
+ "maxLength": 300
5322
+ },
5323
+ "order": {
5324
+ "type": "integer",
5325
+ "minimum": 0
5326
+ }
5327
+ },
5328
+ "required": [
5329
+ "assetId"
5330
+ ]
5331
+ },
5332
+ "maxItems": 10
5333
+ },
5334
+ "hints": {
5335
+ "type": "array",
5336
+ "items": {
5337
+ "type": "string",
5338
+ "enum": [
5339
+ "photo_required",
5340
+ "yes_no",
5341
+ "required",
5342
+ "reading"
5343
+ ]
5344
+ }
5345
+ },
5346
+ "roomSource": {
5347
+ "type": "string",
5348
+ "enum": [
5349
+ "pose",
5350
+ "named",
5351
+ "vision",
5352
+ "time",
5353
+ "none"
5354
+ ]
5355
+ },
5356
+ "spokenAt": {
5357
+ "type": "string"
5358
+ },
5359
+ "quote": {
5360
+ "type": [
5361
+ "string",
5362
+ "null"
5363
+ ]
5364
+ }
5365
+ },
5366
+ "required": [
5367
+ "key",
5368
+ "itemText",
5369
+ "description",
5370
+ "itemType",
5371
+ "required",
5372
+ "options",
5373
+ "referenceMedia",
5374
+ "hints",
5375
+ "roomSource",
5376
+ "spokenAt",
5377
+ "quote"
5378
+ ]
5379
+ }
5380
+ }
5381
+ },
5382
+ "required": [
5383
+ "key",
5384
+ "title",
5385
+ "sketchRoomId",
5386
+ "kind",
5387
+ "items"
5388
+ ]
5389
+ }
5390
+ },
5391
+ "photos": {
5392
+ "type": "array",
5393
+ "items": {
5394
+ "type": "object",
5395
+ "properties": {
5396
+ "assetId": {
5397
+ "type": "string"
5398
+ },
5399
+ "capturedAt": {
5400
+ "type": "string"
5401
+ },
5402
+ "sectionKey": {
5403
+ "type": [
5404
+ "string",
5405
+ "null"
5406
+ ]
5407
+ },
5408
+ "roomSource": {
5409
+ "type": "string",
5410
+ "enum": [
5411
+ "pose",
5412
+ "named",
5413
+ "vision",
5414
+ "time",
5415
+ "none"
5416
+ ]
5417
+ },
5418
+ "itemKeys": {
5419
+ "type": "array",
5420
+ "items": {
5421
+ "type": "string"
5422
+ }
5423
+ }
5424
+ },
5425
+ "required": [
5426
+ "assetId",
5427
+ "capturedAt",
5428
+ "sectionKey",
5429
+ "roomSource",
5430
+ "itemKeys"
5431
+ ]
5432
+ }
5433
+ },
5434
+ "visionUsed": {
5435
+ "type": "boolean"
5436
+ },
5437
+ "model": {
5438
+ "type": [
5439
+ "string",
5440
+ "null"
5441
+ ]
5442
+ },
5443
+ "promptVersion": {
5444
+ "type": "string"
5445
+ }
5446
+ },
5447
+ "required": [
5448
+ "name",
5449
+ "sections",
5450
+ "photos",
5451
+ "visionUsed",
5452
+ "model",
5453
+ "promptVersion"
5454
+ ]
5455
+ },
5456
+ "WalkthroughDraftResponse": {
5457
+ "type": "object",
5458
+ "properties": {
5459
+ "status": {
5460
+ "type": "string",
5461
+ "enum": [
5462
+ "completed",
5463
+ "running",
5464
+ "failed"
5465
+ ]
5466
+ },
5467
+ "runId": {
5468
+ "type": [
5469
+ "integer",
5470
+ "null"
5471
+ ]
5472
+ },
5473
+ "draft": {
5474
+ "type": [
5475
+ "object",
5476
+ "null"
5477
+ ],
5478
+ "properties": {
5479
+ "name": {
5480
+ "type": "string"
5481
+ },
5482
+ "sections": {
5483
+ "type": "array",
5484
+ "items": {
5485
+ "type": "object",
5486
+ "properties": {
5487
+ "key": {
5488
+ "type": "string"
5489
+ },
5490
+ "title": {
5491
+ "type": "string"
5492
+ },
5493
+ "sketchRoomId": {
5494
+ "type": [
5495
+ "integer",
5496
+ "null"
5497
+ ]
5498
+ },
5499
+ "kind": {
5500
+ "type": "string",
5501
+ "enum": [
5502
+ "room",
5503
+ "custom",
5504
+ "not_sure"
5505
+ ]
5506
+ },
5507
+ "items": {
5508
+ "type": "array",
5509
+ "items": {
5510
+ "type": "object",
5511
+ "properties": {
5512
+ "key": {
5513
+ "type": "string"
5514
+ },
5515
+ "itemText": {
5516
+ "type": "string"
5517
+ },
5518
+ "description": {
5519
+ "type": [
5520
+ "string",
5521
+ "null"
5522
+ ]
5523
+ },
5524
+ "itemType": {
5525
+ "type": "string",
5526
+ "enum": [
5527
+ "checkbox",
5528
+ "yes/no",
5529
+ "text",
5530
+ "number",
5531
+ "date",
5532
+ "dropdown",
5533
+ "multi-select",
5534
+ "photo",
5535
+ "signature"
5536
+ ]
5537
+ },
5538
+ "required": {
5539
+ "type": "boolean"
5540
+ },
5541
+ "options": {
5542
+ "type": [
5543
+ "object",
5544
+ "null"
5545
+ ],
5546
+ "additionalProperties": {}
5547
+ },
5548
+ "referenceMedia": {
5549
+ "type": "array",
5550
+ "items": {
5551
+ "type": "object",
5552
+ "properties": {
5553
+ "assetId": {
5554
+ "type": "string",
5555
+ "minLength": 1,
5556
+ "maxLength": 64
5557
+ },
5558
+ "caption": {
5559
+ "type": [
5560
+ "string",
5561
+ "null"
5562
+ ],
5563
+ "maxLength": 300
5564
+ },
5565
+ "order": {
5566
+ "type": "integer",
5567
+ "minimum": 0
5568
+ }
5569
+ },
5570
+ "required": [
5571
+ "assetId"
5572
+ ]
5573
+ },
5574
+ "maxItems": 10
5575
+ },
5576
+ "hints": {
5577
+ "type": "array",
5578
+ "items": {
5579
+ "type": "string",
5580
+ "enum": [
5581
+ "photo_required",
5582
+ "yes_no",
5583
+ "required",
5584
+ "reading"
5585
+ ]
5586
+ }
5587
+ },
5588
+ "roomSource": {
5589
+ "type": "string",
5590
+ "enum": [
5591
+ "pose",
5592
+ "named",
5593
+ "vision",
5594
+ "time",
5595
+ "none"
5596
+ ]
5597
+ },
5598
+ "spokenAt": {
5599
+ "type": "string"
5600
+ },
5601
+ "quote": {
5602
+ "type": [
5603
+ "string",
5604
+ "null"
5605
+ ]
5606
+ }
5607
+ },
5608
+ "required": [
5609
+ "key",
5610
+ "itemText",
5611
+ "description",
5612
+ "itemType",
5613
+ "required",
5614
+ "options",
5615
+ "referenceMedia",
5616
+ "hints",
5617
+ "roomSource",
5618
+ "spokenAt",
5619
+ "quote"
5620
+ ]
5621
+ }
5622
+ }
5623
+ },
5624
+ "required": [
5625
+ "key",
5626
+ "title",
5627
+ "sketchRoomId",
5628
+ "kind",
5629
+ "items"
5630
+ ]
5631
+ }
5632
+ },
5633
+ "photos": {
5634
+ "type": "array",
5635
+ "items": {
5636
+ "type": "object",
5637
+ "properties": {
5638
+ "assetId": {
5639
+ "type": "string"
5640
+ },
5641
+ "capturedAt": {
5642
+ "type": "string"
5643
+ },
5644
+ "sectionKey": {
5645
+ "type": [
5646
+ "string",
5647
+ "null"
5648
+ ]
5649
+ },
5650
+ "roomSource": {
5651
+ "type": "string",
5652
+ "enum": [
5653
+ "pose",
5654
+ "named",
5655
+ "vision",
5656
+ "time",
5657
+ "none"
5658
+ ]
5659
+ },
5660
+ "itemKeys": {
5661
+ "type": "array",
5662
+ "items": {
5663
+ "type": "string"
5664
+ }
5665
+ }
5666
+ },
5667
+ "required": [
5668
+ "assetId",
5669
+ "capturedAt",
5670
+ "sectionKey",
5671
+ "roomSource",
5672
+ "itemKeys"
5673
+ ]
5674
+ }
5675
+ },
5676
+ "visionUsed": {
5677
+ "type": "boolean"
5678
+ },
5679
+ "model": {
5680
+ "type": [
5681
+ "string",
5682
+ "null"
5683
+ ]
5684
+ },
5685
+ "promptVersion": {
5686
+ "type": "string"
5687
+ }
5688
+ },
5689
+ "required": [
5690
+ "name",
5691
+ "sections",
5692
+ "photos",
5693
+ "visionUsed",
5694
+ "model",
5695
+ "promptVersion"
5696
+ ]
5697
+ },
5698
+ "sortRun": {
5699
+ "type": [
5700
+ "object",
5701
+ "null"
5702
+ ],
5703
+ "properties": {
5704
+ "status": {
5705
+ "type": "string",
5706
+ "enum": [
5707
+ "running",
5708
+ "completed",
5709
+ "failed"
5710
+ ]
5711
+ },
5712
+ "total": {
5713
+ "type": "integer"
5714
+ },
5715
+ "done": {
5716
+ "type": "integer"
5717
+ }
5718
+ },
5719
+ "required": [
5720
+ "status",
5721
+ "total",
5722
+ "done"
5723
+ ]
5724
+ },
5725
+ "message": {
5726
+ "type": [
5727
+ "string",
5728
+ "null"
5729
+ ]
5730
+ }
5731
+ },
5732
+ "required": [
5733
+ "status",
5734
+ "runId",
5735
+ "draft",
5736
+ "sortRun",
5737
+ "message"
5738
+ ]
5739
+ },
5740
+ "ChattyListJobChecklistsArguments": {
5741
+ "type": "object",
5742
+ "properties": {
5743
+ "jobId": {
5744
+ "anyOf": [
5745
+ {
5746
+ "type": "integer",
5747
+ "exclusiveMinimum": 0
5748
+ },
5749
+ {
5750
+ "type": "string",
5751
+ "pattern": "^\\d+$"
5752
+ }
5753
+ ]
5754
+ }
5755
+ },
5756
+ "description": "`arguments` of the Chatty tool `list_job_checklists` (POST /api/chatty-voice/tools/execute)."
5757
+ },
5758
+ "ChattyListJobChecklistsData": {
5759
+ "type": "array",
5760
+ "items": {
5761
+ "type": "object",
5762
+ "properties": {
5763
+ "id": {
5764
+ "type": "integer"
5765
+ },
5766
+ "name": {
5767
+ "type": "string"
5768
+ },
5769
+ "status": {
5770
+ "type": [
5771
+ "string",
5772
+ "null"
5773
+ ]
5774
+ },
5775
+ "completionPercentage": {
5776
+ "type": "number"
5777
+ },
5778
+ "templateId": {
5779
+ "type": [
5780
+ "integer",
5781
+ "null"
5782
+ ]
5783
+ }
5784
+ },
5785
+ "required": [
5786
+ "id",
5787
+ "name",
5788
+ "status",
5789
+ "completionPercentage",
5790
+ "templateId"
5791
+ ]
5792
+ },
5793
+ "description": "`data` of a successful `list_job_checklists` call (`success: true`)."
5794
+ },
5795
+ "ChattyGetChecklistArguments": {
5796
+ "type": "object",
5797
+ "properties": {
5798
+ "checklistId": {
5799
+ "anyOf": [
5800
+ {
5801
+ "type": "integer",
5802
+ "exclusiveMinimum": 0
5803
+ },
5804
+ {
5805
+ "type": "string",
5806
+ "pattern": "^\\d+$"
5807
+ }
5808
+ ]
5809
+ },
5810
+ "checklistName": {
5811
+ "type": "string"
5812
+ },
5813
+ "jobId": {
5814
+ "anyOf": [
5815
+ {
5816
+ "type": "integer",
5817
+ "exclusiveMinimum": 0
5818
+ },
5819
+ {
5820
+ "type": "string",
5821
+ "pattern": "^\\d+$"
5822
+ }
5823
+ ]
5824
+ }
5825
+ },
5826
+ "description": "`arguments` of the Chatty tool `get_checklist` (POST /api/chatty-voice/tools/execute)."
5827
+ },
5828
+ "ChattyGetChecklistData": {
5829
+ "type": "object",
5830
+ "properties": {
5831
+ "id": {
5832
+ "type": "integer"
5833
+ },
5834
+ "name": {
5835
+ "type": "string"
5836
+ },
5837
+ "status": {
5838
+ "type": [
5839
+ "string",
5840
+ "null"
5841
+ ]
5842
+ },
5843
+ "completionPercentage": {
5844
+ "type": "number"
5845
+ },
5846
+ "jobId": {
5847
+ "type": [
5848
+ "integer",
5849
+ "null"
5850
+ ]
5851
+ },
5852
+ "items": {
5853
+ "type": "array",
5854
+ "items": {
5855
+ "type": "object",
5856
+ "properties": {
5857
+ "id": {
5858
+ "type": "integer"
5859
+ },
5860
+ "itemText": {
5861
+ "type": "string"
5862
+ },
5863
+ "itemType": {
5864
+ "type": "string"
5865
+ },
5866
+ "required": {
5867
+ "type": "boolean"
5868
+ },
5869
+ "done": {
5870
+ "type": "boolean"
5871
+ },
5872
+ "value": {
5873
+ "type": [
5874
+ "string",
5875
+ "null"
5876
+ ]
5877
+ },
5878
+ "section": {
5879
+ "type": [
5880
+ "string",
5881
+ "null"
5882
+ ]
5883
+ },
5884
+ "referencePhotos": {
5885
+ "type": "integer"
5886
+ }
5887
+ },
5888
+ "required": [
5889
+ "id",
5890
+ "itemText",
5891
+ "itemType",
5892
+ "required",
5893
+ "done",
5894
+ "value",
5895
+ "section",
5896
+ "referencePhotos"
5897
+ ]
5898
+ }
5899
+ }
5900
+ },
5901
+ "required": [
5902
+ "id",
5903
+ "name",
5904
+ "status",
5905
+ "completionPercentage",
5906
+ "jobId",
5907
+ "items"
5908
+ ],
5909
+ "description": "`data` of a successful `get_checklist` call (`success: true`)."
5910
+ },
5911
+ "ChattyCreateChecklistArguments": {
5912
+ "type": "object",
5913
+ "properties": {
5914
+ "jobId": {
5915
+ "anyOf": [
5916
+ {
5917
+ "type": "integer",
5918
+ "exclusiveMinimum": 0
5919
+ },
5920
+ {
5921
+ "type": "string",
5922
+ "pattern": "^\\d+$"
5923
+ }
5924
+ ]
5925
+ },
5926
+ "name": {
5927
+ "type": "string"
5928
+ },
5929
+ "templateId": {
5930
+ "anyOf": [
5931
+ {
5932
+ "type": "integer",
5933
+ "exclusiveMinimum": 0
5934
+ },
5935
+ {
5936
+ "type": "string",
5937
+ "pattern": "^\\d+$"
5938
+ }
5939
+ ]
5940
+ },
5941
+ "templateName": {
5942
+ "type": "string"
5943
+ },
5944
+ "notes": {
5945
+ "type": "string"
5946
+ },
5947
+ "items": {
5948
+ "type": "array",
5949
+ "items": {
5950
+ "type": "string"
5951
+ }
5952
+ }
5953
+ },
5954
+ "description": "`arguments` of the Chatty tool `create_checklist` (POST /api/chatty-voice/tools/execute)."
5955
+ },
5956
+ "ChattyCreateChecklistData": {
5957
+ "type": "object",
5958
+ "properties": {
5959
+ "id": {
5960
+ "type": "integer"
5961
+ },
5962
+ "name": {
5963
+ "type": "string"
5964
+ },
5965
+ "jobId": {
5966
+ "type": "integer"
5967
+ },
5968
+ "itemCount": {
5969
+ "type": "integer"
5970
+ },
5971
+ "templateId": {
5972
+ "type": "integer"
5973
+ },
5974
+ "sections": {
5975
+ "type": "array",
5976
+ "items": {
5977
+ "type": "object",
5978
+ "properties": {
5979
+ "title": {
5980
+ "type": "string"
5981
+ },
5982
+ "items": {
5983
+ "type": "array",
5984
+ "items": {
5985
+ "type": "object",
5986
+ "properties": {
5987
+ "itemText": {
5988
+ "type": "string"
5989
+ },
5990
+ "itemType": {
5991
+ "type": "string",
5992
+ "enum": [
5993
+ "checkbox",
5994
+ "yes/no",
5995
+ "text",
5996
+ "number",
5997
+ "date",
5998
+ "dropdown",
5999
+ "multi-select",
6000
+ "photo",
6001
+ "signature"
6002
+ ]
6003
+ },
6004
+ "required": {
6005
+ "type": "boolean"
6006
+ }
6007
+ },
6008
+ "required": [
6009
+ "itemText",
6010
+ "itemType",
6011
+ "required"
6012
+ ]
6013
+ }
6014
+ }
6015
+ },
6016
+ "required": [
6017
+ "title",
6018
+ "items"
6019
+ ]
6020
+ }
6021
+ }
6022
+ },
6023
+ "required": [
6024
+ "id",
6025
+ "name",
6026
+ "jobId",
6027
+ "itemCount"
6028
+ ],
6029
+ "description": "`data` of a successful `create_checklist` call (`success: true`)."
6030
+ },
6031
+ "ChattyAddChecklistItemArguments": {
6032
+ "type": "object",
6033
+ "properties": {
6034
+ "checklistId": {
6035
+ "anyOf": [
6036
+ {
6037
+ "type": "integer",
6038
+ "exclusiveMinimum": 0
6039
+ },
6040
+ {
6041
+ "type": "string",
6042
+ "pattern": "^\\d+$"
6043
+ }
6044
+ ]
6045
+ },
6046
+ "checklistName": {
6047
+ "type": "string"
6048
+ },
6049
+ "jobId": {
6050
+ "anyOf": [
6051
+ {
6052
+ "type": "integer",
6053
+ "exclusiveMinimum": 0
6054
+ },
6055
+ {
6056
+ "type": "string",
6057
+ "pattern": "^\\d+$"
6058
+ }
6059
+ ]
6060
+ },
6061
+ "itemText": {
6062
+ "type": "string"
6063
+ },
6064
+ "itemType": {
6065
+ "type": "string",
6066
+ "enum": [
6067
+ "checkbox",
6068
+ "yes/no",
6069
+ "text",
6070
+ "number",
6071
+ "date",
6072
+ "dropdown",
6073
+ "multi-select",
6074
+ "photo",
6075
+ "signature"
6076
+ ]
6077
+ },
6078
+ "required": {
6079
+ "type": "boolean"
6080
+ },
6081
+ "description": {
6082
+ "type": "string"
6083
+ },
6084
+ "sectionName": {
6085
+ "type": "string"
6086
+ }
6087
+ },
6088
+ "required": [
6089
+ "itemText"
6090
+ ],
6091
+ "description": "`arguments` of the Chatty tool `add_checklist_item` (POST /api/chatty-voice/tools/execute)."
6092
+ },
6093
+ "ChattyAddChecklistItemData": {
6094
+ "type": "object",
6095
+ "properties": {
6096
+ "id": {
6097
+ "type": "integer"
6098
+ },
6099
+ "checklistId": {
6100
+ "type": "integer"
6101
+ },
6102
+ "itemText": {
6103
+ "type": "string"
6104
+ },
6105
+ "itemType": {
6106
+ "type": "string",
6107
+ "enum": [
6108
+ "checkbox",
6109
+ "yes/no",
6110
+ "text",
6111
+ "number",
6112
+ "date",
6113
+ "dropdown",
6114
+ "multi-select",
6115
+ "photo",
6116
+ "signature"
6117
+ ]
6118
+ },
6119
+ "required": {
6120
+ "type": "boolean"
6121
+ },
6122
+ "section": {
6123
+ "type": [
6124
+ "string",
6125
+ "null"
6126
+ ]
6127
+ }
6128
+ },
6129
+ "required": [
6130
+ "id",
6131
+ "checklistId",
6132
+ "itemText",
6133
+ "itemType",
6134
+ "required",
6135
+ "section"
6136
+ ],
6137
+ "description": "`data` of a successful `add_checklist_item` call (`success: true`)."
6138
+ },
6139
+ "ChattyCompleteChecklistItemArguments": {
6140
+ "type": "object",
6141
+ "properties": {
6142
+ "checklistId": {
6143
+ "anyOf": [
6144
+ {
6145
+ "type": "integer",
6146
+ "exclusiveMinimum": 0
6147
+ },
6148
+ {
6149
+ "type": "string",
6150
+ "pattern": "^\\d+$"
6151
+ }
6152
+ ]
6153
+ },
6154
+ "checklistName": {
6155
+ "type": "string"
6156
+ },
6157
+ "jobId": {
6158
+ "anyOf": [
6159
+ {
6160
+ "type": "integer",
6161
+ "exclusiveMinimum": 0
6162
+ },
6163
+ {
6164
+ "type": "string",
6165
+ "pattern": "^\\d+$"
6166
+ }
6167
+ ]
6168
+ },
6169
+ "itemId": {
6170
+ "anyOf": [
6171
+ {
6172
+ "type": "integer",
6173
+ "exclusiveMinimum": 0
6174
+ },
6175
+ {
6176
+ "type": "string",
6177
+ "pattern": "^\\d+$"
6178
+ }
6179
+ ]
6180
+ },
6181
+ "itemText": {
6182
+ "type": "string"
6183
+ },
6184
+ "completed": {
6185
+ "type": "boolean"
6186
+ }
6187
+ },
6188
+ "description": "`arguments` of the Chatty tool `complete_checklist_item` (POST /api/chatty-voice/tools/execute)."
6189
+ },
6190
+ "ChattyCompleteChecklistItemData": {
6191
+ "type": "object",
6192
+ "properties": {
6193
+ "checklistId": {
6194
+ "type": "integer"
6195
+ },
6196
+ "itemId": {
6197
+ "type": "integer"
6198
+ },
6199
+ "completed": {
6200
+ "type": "boolean"
6201
+ }
6202
+ },
6203
+ "required": [
6204
+ "checklistId",
6205
+ "itemId",
6206
+ "completed"
6207
+ ],
6208
+ "description": "`data` of a successful `complete_checklist_item` call (`success: true`)."
6209
+ },
6210
+ "ChattySetChecklistItemAnswerArguments": {
6211
+ "type": "object",
6212
+ "properties": {
6213
+ "checklistId": {
6214
+ "anyOf": [
6215
+ {
6216
+ "type": "integer",
6217
+ "exclusiveMinimum": 0
6218
+ },
6219
+ {
6220
+ "type": "string",
6221
+ "pattern": "^\\d+$"
6222
+ }
6223
+ ]
6224
+ },
6225
+ "checklistName": {
6226
+ "type": "string"
6227
+ },
6228
+ "jobId": {
6229
+ "anyOf": [
6230
+ {
6231
+ "type": "integer",
6232
+ "exclusiveMinimum": 0
6233
+ },
6234
+ {
6235
+ "type": "string",
6236
+ "pattern": "^\\d+$"
6237
+ }
6238
+ ]
6239
+ },
6240
+ "itemId": {
6241
+ "anyOf": [
6242
+ {
6243
+ "type": "integer",
6244
+ "exclusiveMinimum": 0
6245
+ },
6246
+ {
6247
+ "type": "string",
6248
+ "pattern": "^\\d+$"
6249
+ }
6250
+ ]
6251
+ },
6252
+ "itemText": {
6253
+ "type": "string"
6254
+ },
6255
+ "value": {
6256
+ "type": "string"
6257
+ }
6258
+ },
6259
+ "required": [
6260
+ "value"
6261
+ ],
6262
+ "description": "`arguments` of the Chatty tool `set_checklist_item_answer` (POST /api/chatty-voice/tools/execute)."
6263
+ },
6264
+ "ChattySetChecklistItemAnswerData": {
6265
+ "type": "object",
6266
+ "properties": {
6267
+ "checklistId": {
6268
+ "type": "integer"
6269
+ },
6270
+ "itemId": {
6271
+ "type": "integer"
6272
+ },
6273
+ "value": {
6274
+ "type": "string"
6275
+ }
6276
+ },
6277
+ "required": [
6278
+ "checklistId",
6279
+ "itemId",
6280
+ "value"
6281
+ ],
6282
+ "description": "`data` of a successful `set_checklist_item_answer` call (`success: true`)."
6283
+ },
6284
+ "ChattyAttachReferencePhotoToItemArguments": {
6285
+ "type": "object",
6286
+ "properties": {
6287
+ "checklistId": {
6288
+ "anyOf": [
6289
+ {
6290
+ "type": "integer",
6291
+ "exclusiveMinimum": 0
6292
+ },
6293
+ {
6294
+ "type": "string",
6295
+ "pattern": "^\\d+$"
6296
+ }
6297
+ ]
6298
+ },
6299
+ "checklistName": {
6300
+ "type": "string"
6301
+ },
6302
+ "jobId": {
6303
+ "anyOf": [
6304
+ {
6305
+ "type": "integer",
6306
+ "exclusiveMinimum": 0
6307
+ },
6308
+ {
6309
+ "type": "string",
6310
+ "pattern": "^\\d+$"
6311
+ }
6312
+ ]
6313
+ },
6314
+ "itemId": {
6315
+ "anyOf": [
6316
+ {
6317
+ "type": "integer",
6318
+ "exclusiveMinimum": 0
6319
+ },
6320
+ {
6321
+ "type": "string",
6322
+ "pattern": "^\\d+$"
6323
+ }
6324
+ ]
6325
+ },
6326
+ "itemText": {
6327
+ "type": "string"
6328
+ },
6329
+ "photoId": {
6330
+ "anyOf": [
6331
+ {
6332
+ "type": "integer",
6333
+ "exclusiveMinimum": 0
6334
+ },
6335
+ {
6336
+ "type": "string",
6337
+ "pattern": "^\\d+$"
6338
+ }
6339
+ ]
6340
+ },
6341
+ "caption": {
6342
+ "type": "string"
6343
+ }
6344
+ },
6345
+ "required": [
6346
+ "photoId"
6347
+ ],
6348
+ "description": "`arguments` of the Chatty tool `attach_reference_photo_to_item` (POST /api/chatty-voice/tools/execute)."
6349
+ },
6350
+ "ChattyAttachReferencePhotoToItemData": {
6351
+ "type": "object",
6352
+ "properties": {
6353
+ "checklistId": {
6354
+ "type": "integer"
6355
+ },
6356
+ "itemId": {
6357
+ "type": "integer"
6358
+ },
6359
+ "referenceMedia": {
6360
+ "type": "array",
6361
+ "items": {
6362
+ "type": "object",
6363
+ "properties": {
6364
+ "assetId": {
6365
+ "type": "string",
6366
+ "minLength": 1,
6367
+ "maxLength": 64
6368
+ },
6369
+ "caption": {
6370
+ "type": [
6371
+ "string",
6372
+ "null"
6373
+ ],
6374
+ "maxLength": 300
6375
+ },
6376
+ "order": {
6377
+ "type": "integer",
6378
+ "minimum": 0
6379
+ }
6380
+ },
6381
+ "required": [
6382
+ "assetId"
6383
+ ]
6384
+ }
6385
+ }
6386
+ },
6387
+ "required": [
6388
+ "checklistId",
6389
+ "itemId",
6390
+ "referenceMedia"
6391
+ ],
6392
+ "description": "`data` of a successful `attach_reference_photo_to_item` call (`success: true`)."
6393
+ },
6394
+ "ChattyDraftChecklistFromJobPhotosArguments": {
6395
+ "type": "object",
6396
+ "properties": {
6397
+ "jobId": {
6398
+ "anyOf": [
6399
+ {
6400
+ "type": "integer",
6401
+ "exclusiveMinimum": 0
6402
+ },
6403
+ {
6404
+ "type": "string",
6405
+ "pattern": "^\\d+$"
6406
+ }
6407
+ ]
6408
+ },
6409
+ "name": {
6410
+ "type": "string"
6411
+ },
6412
+ "create": {
6413
+ "type": "boolean"
6414
+ }
6415
+ },
6416
+ "description": "`arguments` of the Chatty tool `draft_checklist_from_job_photos` (POST /api/chatty-voice/tools/execute)."
6417
+ },
6418
+ "ChattyDraftChecklistFromJobPhotosData": {
6419
+ "type": "object",
6420
+ "properties": {
6421
+ "id": {
6422
+ "type": "integer"
6423
+ },
6424
+ "jobId": {
6425
+ "type": "integer"
6426
+ },
6427
+ "name": {
6428
+ "type": "string"
6429
+ },
6430
+ "items": {
6431
+ "type": "array",
6432
+ "items": {
6433
+ "type": "object",
6434
+ "properties": {
6435
+ "itemText": {
6436
+ "type": "string"
6437
+ },
6438
+ "photos": {
6439
+ "type": "integer"
6440
+ }
6441
+ },
6442
+ "required": [
6443
+ "itemText",
6444
+ "photos"
6445
+ ]
6446
+ }
6447
+ },
6448
+ "unsortedPhotos": {
6449
+ "type": "integer"
6450
+ },
6451
+ "created": {
6452
+ "type": "boolean"
6453
+ }
6454
+ },
6455
+ "required": [
6456
+ "name",
6457
+ "items",
6458
+ "unsortedPhotos",
6459
+ "created"
6460
+ ],
6461
+ "description": "`data` of a successful `draft_checklist_from_job_photos` call (`success: true`)."
6462
+ }
6463
+ },
6464
+ "parameters": {}
6465
+ },
6466
+ "paths": {
6467
+ "/api/version": {
6468
+ "get": {
6469
+ "summary": "Cross-platform version handshake manifest.",
6470
+ "tags": [
6471
+ "version"
6472
+ ],
6473
+ "responses": {
6474
+ "200": {
4810
6475
  "description": "Always returned. Body is the canonical handshake manifest.",
4811
6476
  "content": {
4812
6477
  "application/json": {
@@ -4904,6 +6569,268 @@
4904
6569
  }
4905
6570
  }
4906
6571
  },
6572
+ "/api/impersonate/native-read-ticket": {
6573
+ "post": {
6574
+ "tags": [
6575
+ "auth"
6576
+ ],
6577
+ "summary": "Authorize an exact browser-native download URL for the current support session.",
6578
+ "description": "Requires the current typed support session and X-Support-Impersonation: 1. A narrow fixed GET allowlist only; record permissions still run on every GET. Admission lasts at most 15 minutes and never outlives the original one-hour support grant. The same browser session is required; URL alone is insufficient. GET/Range retries recheck current authority. Response is no-store with no-referrer. No user bearer is issued.",
6579
+ "security": [
6580
+ {
6581
+ "BrowserSession": []
6582
+ }
6583
+ ],
6584
+ "parameters": [
6585
+ {
6586
+ "schema": {
6587
+ "type": "string",
6588
+ "enum": [
6589
+ "1"
6590
+ ]
6591
+ },
6592
+ "required": true,
6593
+ "name": "X-Support-Impersonation",
6594
+ "in": "header"
6595
+ }
6596
+ ],
6597
+ "requestBody": {
6598
+ "required": true,
6599
+ "content": {
6600
+ "application/json": {
6601
+ "schema": {
6602
+ "type": "object",
6603
+ "properties": {
6604
+ "url": {
6605
+ "type": "string",
6606
+ "minLength": 1,
6607
+ "maxLength": 1024
6608
+ }
6609
+ },
6610
+ "required": [
6611
+ "url"
6612
+ ],
6613
+ "additionalProperties": false
6614
+ }
6615
+ }
6616
+ }
6617
+ },
6618
+ "responses": {
6619
+ "200": {
6620
+ "description": "Exact relative URL and terminal admission deadline.",
6621
+ "content": {
6622
+ "application/json": {
6623
+ "schema": {
6624
+ "type": "object",
6625
+ "properties": {
6626
+ "url": {
6627
+ "type": "string",
6628
+ "minLength": 1,
6629
+ "maxLength": 8192
6630
+ },
6631
+ "expiresAt": {
6632
+ "type": "integer",
6633
+ "exclusiveMinimum": 0
6634
+ }
6635
+ },
6636
+ "required": [
6637
+ "url",
6638
+ "expiresAt"
6639
+ ],
6640
+ "additionalProperties": false
6641
+ }
6642
+ }
6643
+ }
6644
+ },
6645
+ "400": {
6646
+ "description": "Unsupported destination or malformed request."
6647
+ },
6648
+ "401": {
6649
+ "description": "No authenticated session."
6650
+ },
6651
+ "403": {
6652
+ "description": "Support authority unavailable; explicit exit required.",
6653
+ "content": {
6654
+ "application/json": {
6655
+ "schema": {
6656
+ "$ref": "#/components/schemas/StreamAuthenticationError"
6657
+ }
6658
+ }
6659
+ }
6660
+ },
6661
+ "503": {
6662
+ "description": "Authentication lookup temporarily unavailable.",
6663
+ "content": {
6664
+ "application/json": {
6665
+ "schema": {
6666
+ "$ref": "#/components/schemas/StreamAuthenticationError"
6667
+ }
6668
+ }
6669
+ }
6670
+ }
6671
+ }
6672
+ }
6673
+ },
6674
+ "/api/events/ticket": {
6675
+ "post": {
6676
+ "tags": [
6677
+ "auth"
6678
+ ],
6679
+ "summary": "Create a short-lived stream-only admission ticket from the current authenticated actor.",
6680
+ "description": "Connect within at most 60 seconds using GET /api/events?ticket=.... The ticket preserves the real principal, effective target and original login/impersonation deadlines; it cannot authenticate ordinary API requests. It is never a target-user bearer token. Response Cache-Control is no-store. An invalid temporary context returns 403 and must be explicitly exited, without dropping the real login.",
6681
+ "security": [
6682
+ {
6683
+ "UserBearer": []
6684
+ },
6685
+ {
6686
+ "BrowserSession": []
6687
+ },
6688
+ {
6689
+ "BrowserAuthCookie": []
6690
+ }
6691
+ ],
6692
+ "requestBody": {
6693
+ "required": false,
6694
+ "content": {
6695
+ "application/json": {
6696
+ "schema": {
6697
+ "type": "object",
6698
+ "properties": {},
6699
+ "additionalProperties": false
6700
+ }
6701
+ }
6702
+ }
6703
+ },
6704
+ "responses": {
6705
+ "200": {
6706
+ "description": "Opaque ticket, usable only for short-lived stream admission.",
6707
+ "content": {
6708
+ "application/json": {
6709
+ "schema": {
6710
+ "$ref": "#/components/schemas/EventStreamTicketResponse"
6711
+ }
6712
+ }
6713
+ }
6714
+ },
6715
+ "400": {
6716
+ "description": "Unexpected request fields.",
6717
+ "content": {
6718
+ "application/json": {
6719
+ "schema": {
6720
+ "type": "object",
6721
+ "properties": {
6722
+ "message": {
6723
+ "type": "string"
6724
+ },
6725
+ "errors": {
6726
+ "type": "array",
6727
+ "items": {}
6728
+ }
6729
+ },
6730
+ "required": [
6731
+ "message",
6732
+ "errors"
6733
+ ]
6734
+ }
6735
+ }
6736
+ }
6737
+ },
6738
+ "401": {
6739
+ "description": "Real principal is missing, deleted, revoked or expired.",
6740
+ "content": {
6741
+ "application/json": {
6742
+ "schema": {
6743
+ "$ref": "#/components/schemas/StreamAuthenticationError"
6744
+ }
6745
+ }
6746
+ }
6747
+ },
6748
+ "403": {
6749
+ "description": "Impersonation is unavailable; explicitly exit before continuing as the real principal.",
6750
+ "content": {
6751
+ "application/json": {
6752
+ "schema": {
6753
+ "$ref": "#/components/schemas/StreamAuthenticationError"
6754
+ }
6755
+ }
6756
+ }
6757
+ },
6758
+ "503": {
6759
+ "description": "Authentication lookup unavailable; retry without discarding credentials.",
6760
+ "content": {
6761
+ "application/json": {
6762
+ "schema": {
6763
+ "$ref": "#/components/schemas/StreamAuthenticationError"
6764
+ }
6765
+ }
6766
+ }
6767
+ }
6768
+ }
6769
+ }
6770
+ },
6771
+ "/api/events": {
6772
+ "get": {
6773
+ "tags": [
6774
+ "auth"
6775
+ ],
6776
+ "summary": "Open the authenticated server-sent event stream.",
6777
+ "description": "New clients use a stream-only ticket in the ticket query parameter. Legacy authenticated connections remain supported. A supplied invalid ticket never falls back to another credential. Current user, session and revocation state is rechecked before admission and each delivery batch/heartbeat. Original authority deadlines also close idle or blocked sockets. A stream:resync hint means refresh authorized snapshots; this stream is not a replay log.",
6778
+ "parameters": [
6779
+ {
6780
+ "schema": {
6781
+ "type": "string",
6782
+ "minLength": 1,
6783
+ "maxLength": 8192
6784
+ },
6785
+ "required": false,
6786
+ "name": "ticket",
6787
+ "in": "query"
6788
+ }
6789
+ ],
6790
+ "responses": {
6791
+ "200": {
6792
+ "description": "Bounded SSE hints and heartbeat comments until disconnect or authority expiry.",
6793
+ "content": {
6794
+ "text/event-stream": {
6795
+ "schema": {
6796
+ "type": "string"
6797
+ }
6798
+ }
6799
+ }
6800
+ },
6801
+ "401": {
6802
+ "description": "Ticket/source authentication expired, revoked or invalid.",
6803
+ "content": {
6804
+ "application/json": {
6805
+ "schema": {
6806
+ "$ref": "#/components/schemas/StreamAuthenticationError"
6807
+ }
6808
+ }
6809
+ }
6810
+ },
6811
+ "403": {
6812
+ "description": "Legacy temporary context is unavailable.",
6813
+ "content": {
6814
+ "application/json": {
6815
+ "schema": {
6816
+ "$ref": "#/components/schemas/StreamAuthenticationError"
6817
+ }
6818
+ }
6819
+ }
6820
+ },
6821
+ "503": {
6822
+ "description": "Authorization temporarily unavailable; retry with backoff.",
6823
+ "content": {
6824
+ "application/json": {
6825
+ "schema": {
6826
+ "$ref": "#/components/schemas/StreamAuthenticationError"
6827
+ }
6828
+ }
6829
+ }
6830
+ }
6831
+ }
6832
+ }
6833
+ },
4907
6834
  "/api/jobs/{jobId}/scans/mobile-upload": {
4908
6835
  "post": {
4909
6836
  "summary": "iOS LiDAR scan upload. v2 fields are additive — v1 payloads remain byte-identical.",
@@ -5709,9 +7636,6 @@
5709
7636
  {
5710
7637
  "type": "array",
5711
7638
  "items": {}
5712
- },
5713
- {
5714
- "type": "null"
5715
7639
  }
5716
7640
  ]
5717
7641
  },
@@ -5719,7 +7643,8 @@
5719
7643
  "type": [
5720
7644
  "string",
5721
7645
  "null"
5722
- ]
7646
+ ],
7647
+ "format": "date-time"
5723
7648
  },
5724
7649
  "parentContactId": {
5725
7650
  "type": [
@@ -5881,7 +7806,8 @@
5881
7806
  "type": [
5882
7807
  "string",
5883
7808
  "null"
5884
- ]
7809
+ ],
7810
+ "format": "date-time"
5885
7811
  },
5886
7812
  "qboLastPushedHash": {
5887
7813
  "type": [
@@ -5899,7 +7825,8 @@
5899
7825
  "type": [
5900
7826
  "string",
5901
7827
  "null"
5902
- ]
7828
+ ],
7829
+ "format": "date-time"
5903
7830
  },
5904
7831
  "deletedBy": {
5905
7832
  "type": [
@@ -6038,9 +7965,6 @@
6038
7965
  {
6039
7966
  "type": "array",
6040
7967
  "items": {}
6041
- },
6042
- {
6043
- "type": "null"
6044
7968
  }
6045
7969
  ]
6046
7970
  },
@@ -6048,7 +7972,8 @@
6048
7972
  "type": [
6049
7973
  "string",
6050
7974
  "null"
6051
- ]
7975
+ ],
7976
+ "format": "date-time"
6052
7977
  },
6053
7978
  "parentContactId": {
6054
7979
  "type": [
@@ -6210,7 +8135,8 @@
6210
8135
  "type": [
6211
8136
  "string",
6212
8137
  "null"
6213
- ]
8138
+ ],
8139
+ "format": "date-time"
6214
8140
  },
6215
8141
  "qboLastPushedHash": {
6216
8142
  "type": [
@@ -6228,7 +8154,8 @@
6228
8154
  "type": [
6229
8155
  "string",
6230
8156
  "null"
6231
- ]
8157
+ ],
8158
+ "format": "date-time"
6232
8159
  },
6233
8160
  "deletedBy": {
6234
8161
  "type": [
@@ -6328,9 +8255,6 @@
6328
8255
  {
6329
8256
  "type": "array",
6330
8257
  "items": {}
6331
- },
6332
- {
6333
- "type": "null"
6334
8258
  }
6335
8259
  ]
6336
8260
  },
@@ -6338,7 +8262,8 @@
6338
8262
  "type": [
6339
8263
  "string",
6340
8264
  "null"
6341
- ]
8265
+ ],
8266
+ "format": "date-time"
6342
8267
  },
6343
8268
  "parentContactId": {
6344
8269
  "type": [
@@ -6500,7 +8425,8 @@
6500
8425
  "type": [
6501
8426
  "string",
6502
8427
  "null"
6503
- ]
8428
+ ],
8429
+ "format": "date-time"
6504
8430
  },
6505
8431
  "qboLastPushedHash": {
6506
8432
  "type": [
@@ -6518,7 +8444,8 @@
6518
8444
  "type": [
6519
8445
  "string",
6520
8446
  "null"
6521
- ]
8447
+ ],
8448
+ "format": "date-time"
6522
8449
  },
6523
8450
  "deletedBy": {
6524
8451
  "type": [
@@ -6764,6 +8691,11 @@
6764
8691
  "type": "number",
6765
8692
  "exclusiveMinimum": 0
6766
8693
  },
8694
+ "sillHeightM": {
8695
+ "type": "number",
8696
+ "minimum": 0,
8697
+ "maximum": 50
8698
+ },
6767
8699
  "hostWallIndex": {
6768
8700
  "type": [
6769
8701
  "integer",
@@ -6846,6 +8778,11 @@
6846
8778
  "type": "number",
6847
8779
  "exclusiveMinimum": 0
6848
8780
  },
8781
+ "sillHeightM": {
8782
+ "type": "number",
8783
+ "minimum": 0,
8784
+ "maximum": 50
8785
+ },
6849
8786
  "hostWallIndex": {
6850
8787
  "type": [
6851
8788
  "integer",
@@ -6909,6 +8846,11 @@
6909
8846
  "type": "number",
6910
8847
  "exclusiveMinimum": 0
6911
8848
  },
8849
+ "sillHeightM": {
8850
+ "type": "number",
8851
+ "minimum": 0,
8852
+ "maximum": 50
8853
+ },
6912
8854
  "hostWallIndex": {
6913
8855
  "type": [
6914
8856
  "integer",
@@ -7151,7 +9093,12 @@
7151
9093
  },
7152
9094
  "height": {
7153
9095
  "type": "number",
7154
- "exclusiveMinimum": 0
9096
+ "exclusiveMinimum": 0
9097
+ },
9098
+ "sillHeightM": {
9099
+ "type": "number",
9100
+ "minimum": 0,
9101
+ "maximum": 50
7155
9102
  },
7156
9103
  "hostWallIndex": {
7157
9104
  "type": [
@@ -7235,6 +9182,11 @@
7235
9182
  "type": "number",
7236
9183
  "exclusiveMinimum": 0
7237
9184
  },
9185
+ "sillHeightM": {
9186
+ "type": "number",
9187
+ "minimum": 0,
9188
+ "maximum": 50
9189
+ },
7238
9190
  "hostWallIndex": {
7239
9191
  "type": [
7240
9192
  "integer",
@@ -7298,6 +9250,11 @@
7298
9250
  "type": "number",
7299
9251
  "exclusiveMinimum": 0
7300
9252
  },
9253
+ "sillHeightM": {
9254
+ "type": "number",
9255
+ "minimum": 0,
9256
+ "maximum": 50
9257
+ },
7301
9258
  "hostWallIndex": {
7302
9259
  "type": [
7303
9260
  "integer",
@@ -8313,7 +10270,8 @@
8313
10270
  "enum": [
8314
10271
  "ios",
8315
10272
  "web",
8316
- "android"
10273
+ "android",
10274
+ null
8317
10275
  ]
8318
10276
  },
8319
10277
  "occurredAt": {
@@ -8517,7 +10475,8 @@
8517
10475
  "enum": [
8518
10476
  "ios",
8519
10477
  "web",
8520
- "android"
10478
+ "android",
10479
+ null
8521
10480
  ]
8522
10481
  },
8523
10482
  "occurredAt": {
@@ -9276,7 +11235,8 @@
9276
11235
  "enum": [
9277
11236
  "ios",
9278
11237
  "web",
9279
- "android"
11238
+ "android",
11239
+ null
9280
11240
  ]
9281
11241
  },
9282
11242
  "canConfirmAway": {
@@ -9519,7 +11479,8 @@
9519
11479
  "enum": [
9520
11480
  "ios",
9521
11481
  "web",
9522
- "android"
11482
+ "android",
11483
+ null
9523
11484
  ]
9524
11485
  },
9525
11486
  "tapElapsedMs": {
@@ -9991,7 +11952,8 @@
9991
11952
  "enum": [
9992
11953
  "ios",
9993
11954
  "web",
9994
- "android"
11955
+ "android",
11956
+ null
9995
11957
  ]
9996
11958
  }
9997
11959
  },
@@ -10189,7 +12151,8 @@
10189
12151
  "enum": [
10190
12152
  "ios",
10191
12153
  "web",
10192
- "android"
12154
+ "android",
12155
+ null
10193
12156
  ]
10194
12157
  }
10195
12158
  },
@@ -10740,6 +12703,317 @@
10740
12703
  }
10741
12704
  }
10742
12705
  }
12706
+ },
12707
+ "/api/sketches/{sketchId}/doorway-edits": {
12708
+ "post": {
12709
+ "tags": [
12710
+ "lidar"
12711
+ ],
12712
+ "summary": "Save a confirmed shared-doorway edit atomically, or undo it.",
12713
+ "description": "Requires authenticated organization and job access. Delete removes both linked openings and only their doorway connection. Undo receipts are user/scope-bound, expire after eight hours, and reject intervening geometry or connection changes. Connect is supported for sketches only. Room reads provide openingId and openingRevision; existing records are not backfilled.",
12714
+ "parameters": [
12715
+ {
12716
+ "name": "sketchId",
12717
+ "in": "path",
12718
+ "required": true,
12719
+ "schema": {
12720
+ "type": "integer",
12721
+ "minimum": 1
12722
+ }
12723
+ }
12724
+ ],
12725
+ "requestBody": {
12726
+ "required": true,
12727
+ "content": {
12728
+ "application/json": {
12729
+ "schema": {
12730
+ "$ref": "#/components/schemas/DoorwayEditCommand"
12731
+ }
12732
+ }
12733
+ }
12734
+ },
12735
+ "responses": {
12736
+ "200": {
12737
+ "description": "Committed both rooms and their connection changes; token reverses this action.",
12738
+ "content": {
12739
+ "application/json": {
12740
+ "schema": {
12741
+ "$ref": "#/components/schemas/DoorwayEditResult"
12742
+ }
12743
+ }
12744
+ }
12745
+ },
12746
+ "400": {
12747
+ "description": "Invalid command or missing explicit confirmation.",
12748
+ "content": {
12749
+ "application/json": {
12750
+ "schema": {
12751
+ "$ref": "#/components/schemas/ErrorEnvelope"
12752
+ }
12753
+ }
12754
+ }
12755
+ },
12756
+ "401": {
12757
+ "description": "Authentication required."
12758
+ },
12759
+ "403": {
12760
+ "description": "Job access denied."
12761
+ },
12762
+ "404": {
12763
+ "description": "Scope not found in the organization."
12764
+ },
12765
+ "409": {
12766
+ "description": "Stale or ambiguous opening identity, or undo would overwrite newer work.",
12767
+ "content": {
12768
+ "application/json": {
12769
+ "schema": {
12770
+ "$ref": "#/components/schemas/ErrorEnvelope"
12771
+ }
12772
+ }
12773
+ }
12774
+ }
12775
+ }
12776
+ }
12777
+ },
12778
+ "/api/floors/{floorId}/doorway-edits": {
12779
+ "post": {
12780
+ "tags": [
12781
+ "lidar"
12782
+ ],
12783
+ "summary": "Save a confirmed shared-doorway edit atomically, or undo it.",
12784
+ "description": "Requires authenticated organization and job access. Delete removes both linked openings and only their doorway connection. Undo receipts are user/scope-bound, expire after eight hours, and reject intervening geometry or connection changes. Connect is supported for sketches only. Room reads provide openingId and openingRevision; existing records are not backfilled.",
12785
+ "parameters": [
12786
+ {
12787
+ "name": "floorId",
12788
+ "in": "path",
12789
+ "required": true,
12790
+ "schema": {
12791
+ "type": "integer",
12792
+ "minimum": 1
12793
+ }
12794
+ }
12795
+ ],
12796
+ "requestBody": {
12797
+ "required": true,
12798
+ "content": {
12799
+ "application/json": {
12800
+ "schema": {
12801
+ "$ref": "#/components/schemas/DoorwayEditCommand"
12802
+ }
12803
+ }
12804
+ }
12805
+ },
12806
+ "responses": {
12807
+ "200": {
12808
+ "description": "Committed both rooms and their connection changes; token reverses this action.",
12809
+ "content": {
12810
+ "application/json": {
12811
+ "schema": {
12812
+ "$ref": "#/components/schemas/DoorwayEditResult"
12813
+ }
12814
+ }
12815
+ }
12816
+ },
12817
+ "400": {
12818
+ "description": "Invalid command or missing explicit confirmation.",
12819
+ "content": {
12820
+ "application/json": {
12821
+ "schema": {
12822
+ "$ref": "#/components/schemas/ErrorEnvelope"
12823
+ }
12824
+ }
12825
+ }
12826
+ },
12827
+ "401": {
12828
+ "description": "Authentication required."
12829
+ },
12830
+ "403": {
12831
+ "description": "Job access denied."
12832
+ },
12833
+ "404": {
12834
+ "description": "Scope not found in the organization."
12835
+ },
12836
+ "409": {
12837
+ "description": "Stale or ambiguous opening identity, or undo would overwrite newer work.",
12838
+ "content": {
12839
+ "application/json": {
12840
+ "schema": {
12841
+ "$ref": "#/components/schemas/ErrorEnvelope"
12842
+ }
12843
+ }
12844
+ }
12845
+ }
12846
+ }
12847
+ }
12848
+ },
12849
+ "/api/projects/{projectId}/documents/draft-from-walkthrough": {
12850
+ "post": {
12851
+ "tags": [
12852
+ "checklists"
12853
+ ],
12854
+ "summary": "Turn a voice walk-through (timed transcript + photos) into a checklist draft.",
12855
+ "description": "Returns a DRAFT only; nothing is saved to the job. The phone's review screen creates the checklist with POST /api/projects/{projectId}/documents (custom `items`, carrying `referenceMedia`). Sections come from, in order: the camera pose of a scan, the room the tech named (the item's words, the photo's room chip, a section opened), the AI Image Sort's room for the item's photo (add-on), then the last room mentioned; items with no signal go to Not sure. GPS is never used. Item types come from spoken hints only (checkbox unless \"photo required\", \"yes or no\" or a reading; \"required\" sets the flag). Reference photos are the shots taken from 10 s before a sentence to 20 s after it. 200 when the draft is ready at once; 202 with `runId` while the photos go through an AI vision sort (poll the runs route). Requires the job (404 otherwise) and the Create Checklists permission (403). Photos that are not the organization's assets are left out.",
12856
+ "parameters": [
12857
+ {
12858
+ "name": "projectId",
12859
+ "in": "path",
12860
+ "required": true,
12861
+ "schema": {
12862
+ "type": "integer",
12863
+ "minimum": 1
12864
+ }
12865
+ }
12866
+ ],
12867
+ "requestBody": {
12868
+ "required": true,
12869
+ "content": {
12870
+ "application/json": {
12871
+ "schema": {
12872
+ "$ref": "#/components/schemas/WalkthroughDraftRequest"
12873
+ }
12874
+ }
12875
+ }
12876
+ },
12877
+ "responses": {
12878
+ "200": {
12879
+ "description": "The draft (status completed).",
12880
+ "content": {
12881
+ "application/json": {
12882
+ "schema": {
12883
+ "$ref": "#/components/schemas/WalkthroughDraftResponse"
12884
+ }
12885
+ }
12886
+ }
12887
+ },
12888
+ "202": {
12889
+ "description": "A vision sort runs first (status running); poll GET …/draft-from-walkthrough/runs/{runId}.",
12890
+ "content": {
12891
+ "application/json": {
12892
+ "schema": {
12893
+ "$ref": "#/components/schemas/WalkthroughDraftResponse"
12894
+ }
12895
+ }
12896
+ }
12897
+ },
12898
+ "400": {
12899
+ "description": "No transcript, or a wrong shape.",
12900
+ "content": {
12901
+ "application/json": {
12902
+ "schema": {
12903
+ "$ref": "#/components/schemas/ErrorEnvelope"
12904
+ }
12905
+ }
12906
+ }
12907
+ },
12908
+ "401": {
12909
+ "description": "Authentication required."
12910
+ },
12911
+ "403": {
12912
+ "description": "The role cannot create checklists.",
12913
+ "content": {
12914
+ "application/json": {
12915
+ "schema": {
12916
+ "$ref": "#/components/schemas/ErrorEnvelope"
12917
+ }
12918
+ }
12919
+ }
12920
+ },
12921
+ "404": {
12922
+ "description": "Job not found.",
12923
+ "content": {
12924
+ "application/json": {
12925
+ "schema": {
12926
+ "$ref": "#/components/schemas/ErrorEnvelope"
12927
+ }
12928
+ }
12929
+ }
12930
+ },
12931
+ "502": {
12932
+ "description": "The AI's answer could not be used.",
12933
+ "content": {
12934
+ "application/json": {
12935
+ "schema": {
12936
+ "$ref": "#/components/schemas/ErrorEnvelope"
12937
+ }
12938
+ }
12939
+ }
12940
+ },
12941
+ "503": {
12942
+ "description": "AI is not configured.",
12943
+ "content": {
12944
+ "application/json": {
12945
+ "schema": {
12946
+ "$ref": "#/components/schemas/ErrorEnvelope"
12947
+ }
12948
+ }
12949
+ }
12950
+ }
12951
+ }
12952
+ }
12953
+ },
12954
+ "/api/projects/{projectId}/documents/draft-from-walkthrough/runs/{runId}": {
12955
+ "get": {
12956
+ "tags": [
12957
+ "checklists"
12958
+ ],
12959
+ "summary": "Poll a walk-through draft that waits for an AI vision sort.",
12960
+ "description": "Status `running` (with the sort's progress in `sortRun`) until the draft is ready, then `completed` with `draft`, or `failed` with `message`. A run that stops making progress is marked failed on the next poll and never re-run.",
12961
+ "parameters": [
12962
+ {
12963
+ "name": "projectId",
12964
+ "in": "path",
12965
+ "required": true,
12966
+ "schema": {
12967
+ "type": "integer",
12968
+ "minimum": 1
12969
+ }
12970
+ },
12971
+ {
12972
+ "name": "runId",
12973
+ "in": "path",
12974
+ "required": true,
12975
+ "schema": {
12976
+ "type": "integer",
12977
+ "minimum": 1
12978
+ }
12979
+ }
12980
+ ],
12981
+ "responses": {
12982
+ "200": {
12983
+ "description": "The run's envelope.",
12984
+ "content": {
12985
+ "application/json": {
12986
+ "schema": {
12987
+ "$ref": "#/components/schemas/WalkthroughDraftResponse"
12988
+ }
12989
+ }
12990
+ }
12991
+ },
12992
+ "401": {
12993
+ "description": "Authentication required."
12994
+ },
12995
+ "403": {
12996
+ "description": "The role cannot create checklists.",
12997
+ "content": {
12998
+ "application/json": {
12999
+ "schema": {
13000
+ "$ref": "#/components/schemas/ErrorEnvelope"
13001
+ }
13002
+ }
13003
+ }
13004
+ },
13005
+ "404": {
13006
+ "description": "Job or run not found.",
13007
+ "content": {
13008
+ "application/json": {
13009
+ "schema": {
13010
+ "$ref": "#/components/schemas/ErrorEnvelope"
13011
+ }
13012
+ }
13013
+ }
13014
+ }
13015
+ }
13016
+ }
10743
13017
  }
10744
13018
  },
10745
13019
  "webhooks": {}