@serviceai/api-spec 1.0.7

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.
package/openapi.json ADDED
@@ -0,0 +1,4384 @@
1
+ {
2
+ "openapi": "3.1.0",
3
+ "info": {
4
+ "title": "ServiceAi API",
5
+ "version": "1.0.7",
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
+ "license": {
8
+ "name": "Proprietary"
9
+ }
10
+ },
11
+ "servers": [
12
+ {
13
+ "url": "https://app.serviceai.com",
14
+ "description": "Production"
15
+ },
16
+ {
17
+ "url": "http://localhost:5000",
18
+ "description": "Local development"
19
+ }
20
+ ],
21
+ "tags": [
22
+ {
23
+ "name": "version",
24
+ "description": "Handshake + diagnostics."
25
+ },
26
+ {
27
+ "name": "lidar",
28
+ "description": "iOS LiDAR / RoomPlan ingest + floor management."
29
+ },
30
+ {
31
+ "name": "auth",
32
+ "description": "Authentication + capability discovery."
33
+ },
34
+ {
35
+ "name": "features",
36
+ "description": "Feature flag surface."
37
+ },
38
+ {
39
+ "name": "crm",
40
+ "description": "Contacts + CRM surface."
41
+ },
42
+ {
43
+ "name": "photos",
44
+ "description": "Photo / asset upload surface."
45
+ },
46
+ {
47
+ "name": "jobs",
48
+ "description": "Job (project) lifecycle surface."
49
+ },
50
+ {
51
+ "name": "chatty",
52
+ "description": "Realtime voice-enabled AI assistant."
53
+ }
54
+ ],
55
+ "components": {
56
+ "schemas": {
57
+ "ErrorEnvelope": {
58
+ "type": "object",
59
+ "properties": {
60
+ "error": {
61
+ "type": "string",
62
+ "example": "validation_failed"
63
+ },
64
+ "message": {
65
+ "type": "string",
66
+ "example": "roomPlanJSON is required"
67
+ },
68
+ "details": {}
69
+ },
70
+ "required": [
71
+ "error",
72
+ "message"
73
+ ],
74
+ "description": "Structured error response returned by every endpoint on failure."
75
+ },
76
+ "UpgradeRequiredEnvelope": {
77
+ "type": "object",
78
+ "properties": {
79
+ "error": {
80
+ "type": "string",
81
+ "enum": [
82
+ "upgrade_required"
83
+ ]
84
+ },
85
+ "message": {
86
+ "type": "string"
87
+ },
88
+ "apiVersion": {
89
+ "type": "string"
90
+ },
91
+ "minClientVersion": {
92
+ "type": "string"
93
+ },
94
+ "deprecatedBelow": {
95
+ "type": "string"
96
+ },
97
+ "clientVersion": {
98
+ "type": "string"
99
+ }
100
+ },
101
+ "required": [
102
+ "error",
103
+ "message",
104
+ "apiVersion",
105
+ "minClientVersion",
106
+ "deprecatedBelow",
107
+ "clientVersion"
108
+ ],
109
+ "description": "Returned with HTTP 426 when the X-Client-Version major is below minClientVersion."
110
+ },
111
+ "VersionManifest": {
112
+ "type": "object",
113
+ "properties": {
114
+ "apiVersion": {
115
+ "type": "string",
116
+ "example": "1.0.7"
117
+ },
118
+ "minClientVersion": {
119
+ "type": "string",
120
+ "example": "1.0.0"
121
+ },
122
+ "deprecatedBelow": {
123
+ "type": "string",
124
+ "example": "1.0.0"
125
+ },
126
+ "version": {
127
+ "type": "string",
128
+ "description": "Legacy alias for apiVersion."
129
+ },
130
+ "buildTime": {
131
+ "type": "string"
132
+ },
133
+ "env": {
134
+ "type": [
135
+ "string",
136
+ "null"
137
+ ]
138
+ },
139
+ "gitCommit": {
140
+ "type": "string"
141
+ }
142
+ },
143
+ "required": [
144
+ "apiVersion",
145
+ "minClientVersion",
146
+ "deprecatedBelow",
147
+ "version",
148
+ "buildTime",
149
+ "gitCommit"
150
+ ],
151
+ "description": "Cross-platform version handshake manifest. See docs/api-versioning.md."
152
+ },
153
+ "InsertContact": {
154
+ "type": "object",
155
+ "properties": {
156
+ "userId": {
157
+ "type": [
158
+ "string",
159
+ "null"
160
+ ]
161
+ },
162
+ "organizationId": {
163
+ "type": [
164
+ "string",
165
+ "null"
166
+ ]
167
+ },
168
+ "customerNumber": {
169
+ "type": [
170
+ "integer",
171
+ "null"
172
+ ],
173
+ "minimum": -2147483648,
174
+ "maximum": 2147483647
175
+ },
176
+ "name": {
177
+ "type": "string"
178
+ },
179
+ "email": {
180
+ "type": [
181
+ "string",
182
+ "null"
183
+ ]
184
+ },
185
+ "phone": {
186
+ "type": [
187
+ "string",
188
+ "null"
189
+ ]
190
+ },
191
+ "company": {
192
+ "type": [
193
+ "string",
194
+ "null"
195
+ ]
196
+ },
197
+ "address": {
198
+ "type": [
199
+ "string",
200
+ "null"
201
+ ]
202
+ },
203
+ "status": {
204
+ "type": [
205
+ "string",
206
+ "null"
207
+ ]
208
+ },
209
+ "customFields": {
210
+ "anyOf": [
211
+ {
212
+ "type": "string"
213
+ },
214
+ {
215
+ "type": "number"
216
+ },
217
+ {
218
+ "type": "boolean"
219
+ },
220
+ {
221
+ "type": "null"
222
+ },
223
+ {
224
+ "type": "object",
225
+ "additionalProperties": {}
226
+ },
227
+ {
228
+ "type": "array",
229
+ "items": {}
230
+ },
231
+ {
232
+ "type": "null"
233
+ }
234
+ ]
235
+ },
236
+ "lastContactDate": {
237
+ "type": [
238
+ "string",
239
+ "null"
240
+ ]
241
+ },
242
+ "parentContactId": {
243
+ "type": [
244
+ "integer",
245
+ "null"
246
+ ],
247
+ "minimum": -2147483648,
248
+ "maximum": 2147483647
249
+ },
250
+ "relationship": {
251
+ "type": [
252
+ "string",
253
+ "null"
254
+ ]
255
+ },
256
+ "role": {
257
+ "type": [
258
+ "string",
259
+ "null"
260
+ ]
261
+ },
262
+ "firstName": {
263
+ "type": [
264
+ "string",
265
+ "null"
266
+ ]
267
+ },
268
+ "lastName": {
269
+ "type": [
270
+ "string",
271
+ "null"
272
+ ]
273
+ },
274
+ "serviceAddress": {
275
+ "type": [
276
+ "string",
277
+ "null"
278
+ ]
279
+ },
280
+ "billingAddress": {
281
+ "type": [
282
+ "string",
283
+ "null"
284
+ ]
285
+ },
286
+ "isCommercial": {
287
+ "type": [
288
+ "boolean",
289
+ "null"
290
+ ]
291
+ },
292
+ "pointOfContactName": {
293
+ "type": [
294
+ "string",
295
+ "null"
296
+ ]
297
+ },
298
+ "pointOfContactTitle": {
299
+ "type": [
300
+ "string",
301
+ "null"
302
+ ]
303
+ },
304
+ "pointOfContactPhone": {
305
+ "type": [
306
+ "string",
307
+ "null"
308
+ ]
309
+ },
310
+ "primaryEmail": {
311
+ "type": [
312
+ "string",
313
+ "null"
314
+ ]
315
+ },
316
+ "propertyAddress": {
317
+ "type": [
318
+ "string",
319
+ "null"
320
+ ]
321
+ },
322
+ "leadSource": {
323
+ "type": [
324
+ "string",
325
+ "null"
326
+ ]
327
+ },
328
+ "leadSourceReferralId": {
329
+ "type": [
330
+ "integer",
331
+ "null"
332
+ ],
333
+ "minimum": -2147483648,
334
+ "maximum": 2147483647
335
+ },
336
+ "leadSourceReferralUserId": {
337
+ "type": [
338
+ "string",
339
+ "null"
340
+ ]
341
+ },
342
+ "leadSourceOther": {
343
+ "type": [
344
+ "string",
345
+ "null"
346
+ ]
347
+ },
348
+ "googleDriveCustomerFolderId": {
349
+ "type": [
350
+ "string",
351
+ "null"
352
+ ]
353
+ },
354
+ "cachedLat": {
355
+ "type": [
356
+ "number",
357
+ "null"
358
+ ],
359
+ "minimum": -140737488355328,
360
+ "maximum": 140737488355327
361
+ },
362
+ "cachedLng": {
363
+ "type": [
364
+ "number",
365
+ "null"
366
+ ],
367
+ "minimum": -140737488355328,
368
+ "maximum": 140737488355327
369
+ },
370
+ "qboId": {
371
+ "type": [
372
+ "string",
373
+ "null"
374
+ ]
375
+ },
376
+ "qboSyncToken": {
377
+ "type": [
378
+ "string",
379
+ "null"
380
+ ]
381
+ },
382
+ "qboLastPushedAt": {
383
+ "type": [
384
+ "string",
385
+ "null"
386
+ ]
387
+ },
388
+ "qboLastPushedHash": {
389
+ "type": [
390
+ "string",
391
+ "null"
392
+ ]
393
+ },
394
+ "qboLastError": {
395
+ "type": [
396
+ "string",
397
+ "null"
398
+ ]
399
+ },
400
+ "deletedAt": {
401
+ "type": [
402
+ "string",
403
+ "null"
404
+ ]
405
+ },
406
+ "deletedBy": {
407
+ "type": [
408
+ "string",
409
+ "null"
410
+ ]
411
+ }
412
+ },
413
+ "required": [
414
+ "name"
415
+ ]
416
+ },
417
+ "InsertProperty": {
418
+ "type": "object",
419
+ "properties": {
420
+ "organizationId": {
421
+ "type": "string"
422
+ },
423
+ "jobId": {
424
+ "type": [
425
+ "integer",
426
+ "null"
427
+ ],
428
+ "minimum": -2147483648,
429
+ "maximum": 2147483647
430
+ },
431
+ "name": {
432
+ "type": "string"
433
+ },
434
+ "address1": {
435
+ "type": [
436
+ "string",
437
+ "null"
438
+ ]
439
+ },
440
+ "address2": {
441
+ "type": [
442
+ "string",
443
+ "null"
444
+ ]
445
+ },
446
+ "city": {
447
+ "type": [
448
+ "string",
449
+ "null"
450
+ ]
451
+ },
452
+ "state": {
453
+ "type": [
454
+ "string",
455
+ "null"
456
+ ]
457
+ },
458
+ "postalCode": {
459
+ "type": [
460
+ "string",
461
+ "null"
462
+ ]
463
+ },
464
+ "country": {
465
+ "type": [
466
+ "string",
467
+ "null"
468
+ ]
469
+ },
470
+ "notes": {
471
+ "type": [
472
+ "string",
473
+ "null"
474
+ ]
475
+ },
476
+ "createdBy": {
477
+ "type": [
478
+ "string",
479
+ "null"
480
+ ]
481
+ }
482
+ },
483
+ "required": [
484
+ "organizationId",
485
+ "name"
486
+ ]
487
+ },
488
+ "InsertFloor": {
489
+ "type": "object",
490
+ "properties": {
491
+ "propertyId": {
492
+ "type": "integer",
493
+ "minimum": -2147483648,
494
+ "maximum": 2147483647
495
+ },
496
+ "organizationId": {
497
+ "type": "string"
498
+ },
499
+ "name": {
500
+ "type": "string"
501
+ },
502
+ "level": {
503
+ "type": "integer",
504
+ "minimum": -2147483648,
505
+ "maximum": 2147483647
506
+ },
507
+ "sortOrder": {
508
+ "type": "integer",
509
+ "minimum": -2147483648,
510
+ "maximum": 2147483647
511
+ },
512
+ "notes": {
513
+ "type": [
514
+ "string",
515
+ "null"
516
+ ]
517
+ }
518
+ },
519
+ "required": [
520
+ "propertyId",
521
+ "organizationId"
522
+ ]
523
+ },
524
+ "InsertScan": {
525
+ "type": "object",
526
+ "properties": {
527
+ "jobId": {
528
+ "type": "integer",
529
+ "minimum": -2147483648,
530
+ "maximum": 2147483647
531
+ },
532
+ "organizationId": {
533
+ "type": "string"
534
+ },
535
+ "assetId": {
536
+ "type": [
537
+ "string",
538
+ "null"
539
+ ]
540
+ },
541
+ "name": {
542
+ "type": "string"
543
+ },
544
+ "description": {
545
+ "type": [
546
+ "string",
547
+ "null"
548
+ ]
549
+ },
550
+ "fileType": {
551
+ "type": "string"
552
+ },
553
+ "scaleFactor": {
554
+ "type": [
555
+ "number",
556
+ "null"
557
+ ],
558
+ "minimum": -140737488355328,
559
+ "maximum": 140737488355327
560
+ },
561
+ "scaleCalibrated": {
562
+ "type": [
563
+ "boolean",
564
+ "null"
565
+ ]
566
+ },
567
+ "pageNumber": {
568
+ "type": [
569
+ "integer",
570
+ "null"
571
+ ],
572
+ "minimum": -2147483648,
573
+ "maximum": 2147483647
574
+ },
575
+ "totalPages": {
576
+ "type": [
577
+ "integer",
578
+ "null"
579
+ ],
580
+ "minimum": -2147483648,
581
+ "maximum": 2147483647
582
+ },
583
+ "processingStatus": {
584
+ "type": "string"
585
+ },
586
+ "aiAnalysis": {
587
+ "anyOf": [
588
+ {
589
+ "type": "string"
590
+ },
591
+ {
592
+ "type": "number"
593
+ },
594
+ {
595
+ "type": "boolean"
596
+ },
597
+ {
598
+ "type": "null"
599
+ },
600
+ {
601
+ "type": "object",
602
+ "additionalProperties": {}
603
+ },
604
+ {
605
+ "type": "array",
606
+ "items": {}
607
+ },
608
+ {
609
+ "type": "null"
610
+ }
611
+ ]
612
+ },
613
+ "featureTier": {
614
+ "type": [
615
+ "string",
616
+ "null"
617
+ ]
618
+ },
619
+ "xactimateMetadata": {
620
+ "anyOf": [
621
+ {
622
+ "type": "string"
623
+ },
624
+ {
625
+ "type": "number"
626
+ },
627
+ {
628
+ "type": "boolean"
629
+ },
630
+ {
631
+ "type": "null"
632
+ },
633
+ {
634
+ "type": "object",
635
+ "additionalProperties": {}
636
+ },
637
+ {
638
+ "type": "array",
639
+ "items": {}
640
+ },
641
+ {
642
+ "type": "null"
643
+ }
644
+ ]
645
+ },
646
+ "parentScanId": {
647
+ "type": [
648
+ "integer",
649
+ "null"
650
+ ],
651
+ "minimum": -2147483648,
652
+ "maximum": 2147483647
653
+ },
654
+ "doorwayLinks": {
655
+ "anyOf": [
656
+ {
657
+ "type": "string"
658
+ },
659
+ {
660
+ "type": "number"
661
+ },
662
+ {
663
+ "type": "boolean"
664
+ },
665
+ {
666
+ "type": "null"
667
+ },
668
+ {
669
+ "type": "object",
670
+ "additionalProperties": {}
671
+ },
672
+ {
673
+ "type": "array",
674
+ "items": {}
675
+ },
676
+ {
677
+ "type": "null"
678
+ }
679
+ ]
680
+ },
681
+ "rawRoomPlanData": {
682
+ "anyOf": [
683
+ {
684
+ "type": "string"
685
+ },
686
+ {
687
+ "type": "number"
688
+ },
689
+ {
690
+ "type": "boolean"
691
+ },
692
+ {
693
+ "type": "null"
694
+ },
695
+ {
696
+ "type": "object",
697
+ "additionalProperties": {}
698
+ },
699
+ {
700
+ "type": "array",
701
+ "items": {}
702
+ },
703
+ {
704
+ "type": "null"
705
+ }
706
+ ]
707
+ },
708
+ "cleanupPipelineVersion": {
709
+ "type": [
710
+ "string",
711
+ "null"
712
+ ]
713
+ },
714
+ "clientCleanupPipelineVersion": {
715
+ "type": [
716
+ "string",
717
+ "null"
718
+ ]
719
+ },
720
+ "userOrientationHint": {
721
+ "type": [
722
+ "string",
723
+ "null"
724
+ ]
725
+ },
726
+ "wallConfidences": {
727
+ "anyOf": [
728
+ {
729
+ "type": "string"
730
+ },
731
+ {
732
+ "type": "number"
733
+ },
734
+ {
735
+ "type": "boolean"
736
+ },
737
+ {
738
+ "type": "null"
739
+ },
740
+ {
741
+ "type": "object",
742
+ "additionalProperties": {}
743
+ },
744
+ {
745
+ "type": "array",
746
+ "items": {}
747
+ },
748
+ {
749
+ "type": "null"
750
+ }
751
+ ]
752
+ },
753
+ "deviceTrack": {
754
+ "anyOf": [
755
+ {
756
+ "type": "string"
757
+ },
758
+ {
759
+ "type": "number"
760
+ },
761
+ {
762
+ "type": "boolean"
763
+ },
764
+ {
765
+ "type": "null"
766
+ },
767
+ {
768
+ "type": "object",
769
+ "additionalProperties": {}
770
+ },
771
+ {
772
+ "type": "array",
773
+ "items": {}
774
+ },
775
+ {
776
+ "type": "null"
777
+ }
778
+ ]
779
+ },
780
+ "referencePhotoKeys": {
781
+ "type": [
782
+ "array",
783
+ "null"
784
+ ],
785
+ "items": {
786
+ "type": [
787
+ "string",
788
+ "null"
789
+ ]
790
+ }
791
+ },
792
+ "metricCorrection": {
793
+ "type": [
794
+ "number",
795
+ "null"
796
+ ],
797
+ "minimum": -140737488355328,
798
+ "maximum": 140737488355327
799
+ },
800
+ "squareUpAngleRad": {
801
+ "type": [
802
+ "number",
803
+ "null"
804
+ ],
805
+ "minimum": -140737488355328,
806
+ "maximum": 140737488355327
807
+ },
808
+ "clientMirrorApplied": {
809
+ "type": [
810
+ "boolean",
811
+ "null"
812
+ ]
813
+ },
814
+ "deviceModel": {
815
+ "type": [
816
+ "string",
817
+ "null"
818
+ ],
819
+ "maxLength": 64
820
+ },
821
+ "appVersion": {
822
+ "type": [
823
+ "string",
824
+ "null"
825
+ ],
826
+ "maxLength": 64
827
+ },
828
+ "captureTimestamp": {
829
+ "type": [
830
+ "string",
831
+ "null"
832
+ ]
833
+ },
834
+ "captureMode": {
835
+ "type": [
836
+ "string",
837
+ "null"
838
+ ],
839
+ "maxLength": 16
840
+ },
841
+ "sessionId": {
842
+ "type": [
843
+ "string",
844
+ "null"
845
+ ],
846
+ "maxLength": 64
847
+ },
848
+ "sessionCapturedAt": {
849
+ "type": [
850
+ "string",
851
+ "null"
852
+ ]
853
+ },
854
+ "floorPlanAssetId": {
855
+ "type": [
856
+ "string",
857
+ "null"
858
+ ]
859
+ },
860
+ "floorPlanPixelsPerMeter": {
861
+ "type": [
862
+ "number",
863
+ "null"
864
+ ],
865
+ "minimum": -140737488355328,
866
+ "maximum": 140737488355327
867
+ },
868
+ "floorPlanOffsetX": {
869
+ "type": [
870
+ "number",
871
+ "null"
872
+ ],
873
+ "minimum": -140737488355328,
874
+ "maximum": 140737488355327
875
+ },
876
+ "floorPlanOffsetY": {
877
+ "type": [
878
+ "number",
879
+ "null"
880
+ ],
881
+ "minimum": -140737488355328,
882
+ "maximum": 140737488355327
883
+ },
884
+ "floorPlanWidthPx": {
885
+ "type": [
886
+ "integer",
887
+ "null"
888
+ ],
889
+ "minimum": -2147483648,
890
+ "maximum": 2147483647
891
+ },
892
+ "floorPlanHeightPx": {
893
+ "type": [
894
+ "integer",
895
+ "null"
896
+ ],
897
+ "minimum": -2147483648,
898
+ "maximum": 2147483647
899
+ },
900
+ "floorId": {
901
+ "type": [
902
+ "integer",
903
+ "null"
904
+ ],
905
+ "minimum": -2147483648,
906
+ "maximum": 2147483647
907
+ },
908
+ "createdBy": {
909
+ "type": [
910
+ "string",
911
+ "null"
912
+ ]
913
+ },
914
+ "deletedAt": {
915
+ "type": [
916
+ "string",
917
+ "null"
918
+ ]
919
+ },
920
+ "deletedBy": {
921
+ "type": [
922
+ "string",
923
+ "null"
924
+ ]
925
+ }
926
+ },
927
+ "required": [
928
+ "jobId",
929
+ "organizationId",
930
+ "name"
931
+ ]
932
+ },
933
+ "InsertTask": {
934
+ "type": "object",
935
+ "properties": {
936
+ "userId": {
937
+ "type": [
938
+ "string",
939
+ "null"
940
+ ]
941
+ },
942
+ "organizationId": {
943
+ "type": [
944
+ "string",
945
+ "null"
946
+ ]
947
+ },
948
+ "contactId": {
949
+ "type": [
950
+ "integer",
951
+ "null"
952
+ ],
953
+ "minimum": -2147483648,
954
+ "maximum": 2147483647
955
+ },
956
+ "projectId": {
957
+ "type": [
958
+ "integer",
959
+ "null"
960
+ ],
961
+ "minimum": -2147483648,
962
+ "maximum": 2147483647
963
+ },
964
+ "agentId": {
965
+ "type": [
966
+ "integer",
967
+ "null"
968
+ ],
969
+ "minimum": -2147483648,
970
+ "maximum": 2147483647
971
+ },
972
+ "title": {
973
+ "type": "string"
974
+ },
975
+ "description": {
976
+ "type": [
977
+ "string",
978
+ "null"
979
+ ]
980
+ },
981
+ "taskType": {
982
+ "type": [
983
+ "string",
984
+ "null"
985
+ ]
986
+ },
987
+ "location": {
988
+ "type": [
989
+ "string",
990
+ "null"
991
+ ]
992
+ },
993
+ "startDate": {
994
+ "type": [
995
+ "string",
996
+ "null"
997
+ ]
998
+ },
999
+ "dueDate": {
1000
+ "type": [
1001
+ "string",
1002
+ "null"
1003
+ ]
1004
+ },
1005
+ "allDay": {
1006
+ "type": [
1007
+ "boolean",
1008
+ "null"
1009
+ ]
1010
+ },
1011
+ "status": {
1012
+ "type": [
1013
+ "string",
1014
+ "null"
1015
+ ]
1016
+ },
1017
+ "priority": {
1018
+ "type": [
1019
+ "string",
1020
+ "null"
1021
+ ]
1022
+ },
1023
+ "assignedTo": {
1024
+ "anyOf": [
1025
+ {
1026
+ "type": "string"
1027
+ },
1028
+ {
1029
+ "type": "number"
1030
+ },
1031
+ {
1032
+ "type": "boolean"
1033
+ },
1034
+ {
1035
+ "type": "null"
1036
+ },
1037
+ {
1038
+ "type": "object",
1039
+ "additionalProperties": {}
1040
+ },
1041
+ {
1042
+ "type": "array",
1043
+ "items": {}
1044
+ },
1045
+ {
1046
+ "type": "null"
1047
+ }
1048
+ ]
1049
+ },
1050
+ "assignedContactIds": {
1051
+ "anyOf": [
1052
+ {
1053
+ "type": "string"
1054
+ },
1055
+ {
1056
+ "type": "number"
1057
+ },
1058
+ {
1059
+ "type": "boolean"
1060
+ },
1061
+ {
1062
+ "type": "null"
1063
+ },
1064
+ {
1065
+ "type": "object",
1066
+ "additionalProperties": {}
1067
+ },
1068
+ {
1069
+ "type": "array",
1070
+ "items": {}
1071
+ },
1072
+ {
1073
+ "type": "null"
1074
+ }
1075
+ ]
1076
+ },
1077
+ "progress": {
1078
+ "type": [
1079
+ "integer",
1080
+ "null"
1081
+ ],
1082
+ "minimum": -2147483648,
1083
+ "maximum": 2147483647
1084
+ },
1085
+ "checklist": {
1086
+ "anyOf": [
1087
+ {
1088
+ "type": "string"
1089
+ },
1090
+ {
1091
+ "type": "number"
1092
+ },
1093
+ {
1094
+ "type": "boolean"
1095
+ },
1096
+ {
1097
+ "type": "null"
1098
+ },
1099
+ {
1100
+ "type": "object",
1101
+ "additionalProperties": {}
1102
+ },
1103
+ {
1104
+ "type": "array",
1105
+ "items": {}
1106
+ },
1107
+ {
1108
+ "type": "null"
1109
+ }
1110
+ ]
1111
+ },
1112
+ "relatedFiles": {
1113
+ "anyOf": [
1114
+ {
1115
+ "type": "string"
1116
+ },
1117
+ {
1118
+ "type": "number"
1119
+ },
1120
+ {
1121
+ "type": "boolean"
1122
+ },
1123
+ {
1124
+ "type": "null"
1125
+ },
1126
+ {
1127
+ "type": "object",
1128
+ "additionalProperties": {}
1129
+ },
1130
+ {
1131
+ "type": "array",
1132
+ "items": {}
1133
+ },
1134
+ {
1135
+ "type": "null"
1136
+ }
1137
+ ]
1138
+ },
1139
+ "reminder": {
1140
+ "type": [
1141
+ "string",
1142
+ "null"
1143
+ ],
1144
+ "enum": [
1145
+ "none",
1146
+ "when_due",
1147
+ "15_min",
1148
+ "30_min",
1149
+ "1_hour",
1150
+ "1_day"
1151
+ ]
1152
+ },
1153
+ "reminderNotificationId": {
1154
+ "type": [
1155
+ "integer",
1156
+ "null"
1157
+ ],
1158
+ "minimum": -2147483648,
1159
+ "maximum": 2147483647
1160
+ },
1161
+ "isRecurring": {
1162
+ "type": [
1163
+ "boolean",
1164
+ "null"
1165
+ ]
1166
+ },
1167
+ "recurrencePattern": {
1168
+ "type": [
1169
+ "string",
1170
+ "null"
1171
+ ]
1172
+ },
1173
+ "recurrenceInterval": {
1174
+ "type": [
1175
+ "integer",
1176
+ "null"
1177
+ ],
1178
+ "minimum": -2147483648,
1179
+ "maximum": 2147483647
1180
+ },
1181
+ "recurrenceEndDate": {
1182
+ "type": [
1183
+ "string",
1184
+ "null"
1185
+ ]
1186
+ },
1187
+ "parentTaskId": {
1188
+ "type": [
1189
+ "integer",
1190
+ "null"
1191
+ ],
1192
+ "minimum": -2147483648,
1193
+ "maximum": 2147483647
1194
+ },
1195
+ "recurrenceDate": {
1196
+ "type": [
1197
+ "string",
1198
+ "null"
1199
+ ]
1200
+ },
1201
+ "isRecurrenceException": {
1202
+ "type": [
1203
+ "boolean",
1204
+ "null"
1205
+ ]
1206
+ },
1207
+ "excludedDates": {
1208
+ "anyOf": [
1209
+ {
1210
+ "type": "string"
1211
+ },
1212
+ {
1213
+ "type": "number"
1214
+ },
1215
+ {
1216
+ "type": "boolean"
1217
+ },
1218
+ {
1219
+ "type": "null"
1220
+ },
1221
+ {
1222
+ "type": "object",
1223
+ "additionalProperties": {}
1224
+ },
1225
+ {
1226
+ "type": "array",
1227
+ "items": {}
1228
+ },
1229
+ {
1230
+ "type": "null"
1231
+ }
1232
+ ]
1233
+ },
1234
+ "displayIndex": {
1235
+ "type": [
1236
+ "integer",
1237
+ "null"
1238
+ ],
1239
+ "minimum": -2147483648,
1240
+ "maximum": 2147483647
1241
+ },
1242
+ "deletedAt": {
1243
+ "type": [
1244
+ "string",
1245
+ "null"
1246
+ ]
1247
+ },
1248
+ "deletedBy": {
1249
+ "type": [
1250
+ "string",
1251
+ "null"
1252
+ ]
1253
+ }
1254
+ },
1255
+ "required": [
1256
+ "title"
1257
+ ]
1258
+ },
1259
+ "InsertNote": {
1260
+ "type": "object",
1261
+ "properties": {
1262
+ "userId": {
1263
+ "type": [
1264
+ "string",
1265
+ "null"
1266
+ ]
1267
+ },
1268
+ "organizationId": {
1269
+ "type": [
1270
+ "string",
1271
+ "null"
1272
+ ]
1273
+ },
1274
+ "contactId": {
1275
+ "type": [
1276
+ "integer",
1277
+ "null"
1278
+ ],
1279
+ "minimum": -2147483648,
1280
+ "maximum": 2147483647
1281
+ },
1282
+ "content": {
1283
+ "type": "string"
1284
+ },
1285
+ "userName": {
1286
+ "type": [
1287
+ "string",
1288
+ "null"
1289
+ ]
1290
+ },
1291
+ "userEmail": {
1292
+ "type": [
1293
+ "string",
1294
+ "null"
1295
+ ]
1296
+ },
1297
+ "deletedAt": {
1298
+ "type": [
1299
+ "string",
1300
+ "null"
1301
+ ]
1302
+ },
1303
+ "deletedBy": {
1304
+ "type": [
1305
+ "string",
1306
+ "null"
1307
+ ]
1308
+ }
1309
+ },
1310
+ "required": [
1311
+ "content"
1312
+ ]
1313
+ },
1314
+ "InsertFile": {
1315
+ "type": "object",
1316
+ "properties": {
1317
+ "organizationId": {
1318
+ "type": "string"
1319
+ },
1320
+ "folderId": {
1321
+ "type": "integer",
1322
+ "minimum": -2147483648,
1323
+ "maximum": 2147483647
1324
+ },
1325
+ "name": {
1326
+ "type": "string"
1327
+ },
1328
+ "originalName": {
1329
+ "type": "string"
1330
+ },
1331
+ "mimeType": {
1332
+ "type": [
1333
+ "string",
1334
+ "null"
1335
+ ]
1336
+ },
1337
+ "size": {
1338
+ "type": [
1339
+ "integer",
1340
+ "null"
1341
+ ],
1342
+ "minimum": -9007199254740991,
1343
+ "maximum": 9007199254740991
1344
+ },
1345
+ "storageUrl": {
1346
+ "type": [
1347
+ "string",
1348
+ "null"
1349
+ ]
1350
+ },
1351
+ "assetId": {
1352
+ "type": [
1353
+ "string",
1354
+ "null"
1355
+ ]
1356
+ },
1357
+ "thumbnailUrl": {
1358
+ "type": [
1359
+ "string",
1360
+ "null"
1361
+ ]
1362
+ },
1363
+ "metadata": {
1364
+ "anyOf": [
1365
+ {
1366
+ "type": "string"
1367
+ },
1368
+ {
1369
+ "type": "number"
1370
+ },
1371
+ {
1372
+ "type": "boolean"
1373
+ },
1374
+ {
1375
+ "type": "null"
1376
+ },
1377
+ {
1378
+ "type": "object",
1379
+ "additionalProperties": {}
1380
+ },
1381
+ {
1382
+ "type": "array",
1383
+ "items": {}
1384
+ },
1385
+ {
1386
+ "type": "null"
1387
+ }
1388
+ ]
1389
+ },
1390
+ "uploadedBy": {
1391
+ "type": [
1392
+ "string",
1393
+ "null"
1394
+ ]
1395
+ },
1396
+ "deletedAt": {
1397
+ "type": [
1398
+ "string",
1399
+ "null"
1400
+ ]
1401
+ },
1402
+ "deletedBy": {
1403
+ "type": [
1404
+ "string",
1405
+ "null"
1406
+ ]
1407
+ }
1408
+ },
1409
+ "required": [
1410
+ "organizationId",
1411
+ "folderId",
1412
+ "name",
1413
+ "originalName"
1414
+ ]
1415
+ },
1416
+ "InsertFolder": {
1417
+ "type": "object",
1418
+ "properties": {
1419
+ "organizationId": {
1420
+ "type": "string"
1421
+ },
1422
+ "name": {
1423
+ "type": "string"
1424
+ },
1425
+ "parentId": {
1426
+ "type": [
1427
+ "integer",
1428
+ "null"
1429
+ ],
1430
+ "minimum": -2147483648,
1431
+ "maximum": 2147483647
1432
+ },
1433
+ "isSystemFolder": {
1434
+ "type": [
1435
+ "boolean",
1436
+ "null"
1437
+ ]
1438
+ },
1439
+ "folderType": {
1440
+ "type": [
1441
+ "string",
1442
+ "null"
1443
+ ]
1444
+ },
1445
+ "createdBy": {
1446
+ "type": [
1447
+ "string",
1448
+ "null"
1449
+ ]
1450
+ },
1451
+ "deletedAt": {
1452
+ "type": [
1453
+ "string",
1454
+ "null"
1455
+ ]
1456
+ },
1457
+ "deletedBy": {
1458
+ "type": [
1459
+ "string",
1460
+ "null"
1461
+ ]
1462
+ }
1463
+ },
1464
+ "required": [
1465
+ "organizationId",
1466
+ "name"
1467
+ ]
1468
+ },
1469
+ "InsertEstimate": {
1470
+ "type": "object",
1471
+ "properties": {
1472
+ "jobId": {
1473
+ "type": "integer",
1474
+ "minimum": -2147483648,
1475
+ "maximum": 2147483647
1476
+ },
1477
+ "estimateNumber": {
1478
+ "type": "string"
1479
+ },
1480
+ "name": {
1481
+ "type": [
1482
+ "string",
1483
+ "null"
1484
+ ]
1485
+ },
1486
+ "version": {
1487
+ "type": [
1488
+ "integer",
1489
+ "null"
1490
+ ],
1491
+ "minimum": -2147483648,
1492
+ "maximum": 2147483647
1493
+ },
1494
+ "templateId": {
1495
+ "type": [
1496
+ "integer",
1497
+ "null"
1498
+ ],
1499
+ "minimum": -2147483648,
1500
+ "maximum": 2147483647
1501
+ },
1502
+ "pricelistId": {
1503
+ "type": [
1504
+ "integer",
1505
+ "null"
1506
+ ],
1507
+ "minimum": -2147483648,
1508
+ "maximum": 2147483647
1509
+ },
1510
+ "status": {
1511
+ "type": [
1512
+ "string",
1513
+ "null"
1514
+ ]
1515
+ },
1516
+ "subtotal": {
1517
+ "type": [
1518
+ "string",
1519
+ "null"
1520
+ ]
1521
+ },
1522
+ "taxAmount": {
1523
+ "type": [
1524
+ "string",
1525
+ "null"
1526
+ ]
1527
+ },
1528
+ "discountAmount": {
1529
+ "type": [
1530
+ "string",
1531
+ "null"
1532
+ ]
1533
+ },
1534
+ "discountPercent": {
1535
+ "type": [
1536
+ "string",
1537
+ "null"
1538
+ ]
1539
+ },
1540
+ "overheadEnabled": {
1541
+ "type": [
1542
+ "boolean",
1543
+ "null"
1544
+ ]
1545
+ },
1546
+ "overheadPercent": {
1547
+ "type": [
1548
+ "string",
1549
+ "null"
1550
+ ]
1551
+ },
1552
+ "profitEnabled": {
1553
+ "type": [
1554
+ "boolean",
1555
+ "null"
1556
+ ]
1557
+ },
1558
+ "profitPercent": {
1559
+ "type": [
1560
+ "string",
1561
+ "null"
1562
+ ]
1563
+ },
1564
+ "totalAmount": {
1565
+ "type": [
1566
+ "string",
1567
+ "null"
1568
+ ]
1569
+ },
1570
+ "description": {
1571
+ "type": [
1572
+ "string",
1573
+ "null"
1574
+ ]
1575
+ },
1576
+ "sections": {
1577
+ "anyOf": [
1578
+ {
1579
+ "type": "string"
1580
+ },
1581
+ {
1582
+ "type": "number"
1583
+ },
1584
+ {
1585
+ "type": "boolean"
1586
+ },
1587
+ {
1588
+ "type": "null"
1589
+ },
1590
+ {
1591
+ "type": "object",
1592
+ "additionalProperties": {}
1593
+ },
1594
+ {
1595
+ "type": "array",
1596
+ "items": {}
1597
+ },
1598
+ {
1599
+ "type": "null"
1600
+ }
1601
+ ]
1602
+ },
1603
+ "terms": {
1604
+ "type": [
1605
+ "string",
1606
+ "null"
1607
+ ]
1608
+ },
1609
+ "notes": {
1610
+ "type": [
1611
+ "string",
1612
+ "null"
1613
+ ]
1614
+ },
1615
+ "internalNotes": {
1616
+ "type": [
1617
+ "string",
1618
+ "null"
1619
+ ]
1620
+ },
1621
+ "validUntil": {
1622
+ "type": [
1623
+ "string",
1624
+ "null"
1625
+ ]
1626
+ },
1627
+ "sentAt": {
1628
+ "type": [
1629
+ "string",
1630
+ "null"
1631
+ ]
1632
+ },
1633
+ "approvedAt": {
1634
+ "type": [
1635
+ "string",
1636
+ "null"
1637
+ ]
1638
+ },
1639
+ "approvedBy": {
1640
+ "type": [
1641
+ "string",
1642
+ "null"
1643
+ ]
1644
+ },
1645
+ "rejectedAt": {
1646
+ "type": [
1647
+ "string",
1648
+ "null"
1649
+ ]
1650
+ },
1651
+ "rejectionReason": {
1652
+ "type": [
1653
+ "string",
1654
+ "null"
1655
+ ]
1656
+ },
1657
+ "signedAt": {
1658
+ "type": [
1659
+ "string",
1660
+ "null"
1661
+ ]
1662
+ },
1663
+ "signatureMeta": {
1664
+ "anyOf": [
1665
+ {
1666
+ "type": "string"
1667
+ },
1668
+ {
1669
+ "type": "number"
1670
+ },
1671
+ {
1672
+ "type": "boolean"
1673
+ },
1674
+ {
1675
+ "type": "null"
1676
+ },
1677
+ {
1678
+ "type": "object",
1679
+ "additionalProperties": {}
1680
+ },
1681
+ {
1682
+ "type": "array",
1683
+ "items": {}
1684
+ },
1685
+ {
1686
+ "type": "null"
1687
+ }
1688
+ ]
1689
+ },
1690
+ "signatureProvider": {
1691
+ "type": [
1692
+ "string",
1693
+ "null"
1694
+ ]
1695
+ },
1696
+ "signatureId": {
1697
+ "type": [
1698
+ "string",
1699
+ "null"
1700
+ ]
1701
+ },
1702
+ "signerName": {
1703
+ "type": [
1704
+ "string",
1705
+ "null"
1706
+ ]
1707
+ },
1708
+ "signerEmail": {
1709
+ "type": [
1710
+ "string",
1711
+ "null"
1712
+ ]
1713
+ },
1714
+ "signerIp": {
1715
+ "type": [
1716
+ "string",
1717
+ "null"
1718
+ ]
1719
+ },
1720
+ "revisionRequestedAt": {
1721
+ "type": [
1722
+ "string",
1723
+ "null"
1724
+ ]
1725
+ },
1726
+ "revisionNotes": {
1727
+ "type": [
1728
+ "string",
1729
+ "null"
1730
+ ]
1731
+ },
1732
+ "customerResponseAt": {
1733
+ "type": [
1734
+ "string",
1735
+ "null"
1736
+ ]
1737
+ },
1738
+ "convertedToInvoiceId": {
1739
+ "type": [
1740
+ "integer",
1741
+ "null"
1742
+ ],
1743
+ "minimum": -2147483648,
1744
+ "maximum": 2147483647
1745
+ },
1746
+ "voidedAt": {
1747
+ "type": [
1748
+ "string",
1749
+ "null"
1750
+ ]
1751
+ },
1752
+ "voidedBy": {
1753
+ "type": [
1754
+ "string",
1755
+ "null"
1756
+ ]
1757
+ },
1758
+ "voidedReason": {
1759
+ "type": [
1760
+ "string",
1761
+ "null"
1762
+ ]
1763
+ },
1764
+ "signatureLocked": {
1765
+ "type": [
1766
+ "boolean",
1767
+ "null"
1768
+ ]
1769
+ },
1770
+ "signNowStatus": {
1771
+ "type": [
1772
+ "string",
1773
+ "null"
1774
+ ]
1775
+ },
1776
+ "signNowRoleId": {
1777
+ "type": [
1778
+ "string",
1779
+ "null"
1780
+ ]
1781
+ },
1782
+ "signNowRoleName": {
1783
+ "type": [
1784
+ "string",
1785
+ "null"
1786
+ ]
1787
+ },
1788
+ "portalVisible": {
1789
+ "type": [
1790
+ "boolean",
1791
+ "null"
1792
+ ]
1793
+ },
1794
+ "signedCompletionEmailSentAt": {
1795
+ "type": [
1796
+ "string",
1797
+ "null"
1798
+ ]
1799
+ },
1800
+ "qboId": {
1801
+ "type": [
1802
+ "string",
1803
+ "null"
1804
+ ]
1805
+ },
1806
+ "qboSyncToken": {
1807
+ "type": [
1808
+ "string",
1809
+ "null"
1810
+ ]
1811
+ },
1812
+ "qboLastPushedAt": {
1813
+ "type": [
1814
+ "string",
1815
+ "null"
1816
+ ]
1817
+ },
1818
+ "qboLastPushedHash": {
1819
+ "type": [
1820
+ "string",
1821
+ "null"
1822
+ ]
1823
+ },
1824
+ "qboLastError": {
1825
+ "type": [
1826
+ "string",
1827
+ "null"
1828
+ ]
1829
+ },
1830
+ "createdBy": {
1831
+ "type": [
1832
+ "string",
1833
+ "null"
1834
+ ]
1835
+ },
1836
+ "deletedAt": {
1837
+ "type": [
1838
+ "string",
1839
+ "null"
1840
+ ]
1841
+ },
1842
+ "deletedBy": {
1843
+ "type": [
1844
+ "string",
1845
+ "null"
1846
+ ]
1847
+ }
1848
+ },
1849
+ "required": [
1850
+ "jobId",
1851
+ "estimateNumber"
1852
+ ]
1853
+ },
1854
+ "PhotoUploadResponse": {
1855
+ "type": "object",
1856
+ "properties": {
1857
+ "id": {
1858
+ "type": "integer"
1859
+ },
1860
+ "url": {
1861
+ "type": "string"
1862
+ },
1863
+ "s3Key": {
1864
+ "type": "string"
1865
+ },
1866
+ "contentType": {
1867
+ "type": "string"
1868
+ },
1869
+ "width": {
1870
+ "type": [
1871
+ "integer",
1872
+ "null"
1873
+ ]
1874
+ },
1875
+ "height": {
1876
+ "type": [
1877
+ "integer",
1878
+ "null"
1879
+ ]
1880
+ },
1881
+ "watermarked": {
1882
+ "type": "boolean"
1883
+ }
1884
+ },
1885
+ "required": [
1886
+ "id",
1887
+ "url",
1888
+ "s3Key",
1889
+ "contentType",
1890
+ "watermarked"
1891
+ ]
1892
+ },
1893
+ "MeResponse": {
1894
+ "type": "object",
1895
+ "properties": {
1896
+ "id": {
1897
+ "type": "integer"
1898
+ },
1899
+ "email": {
1900
+ "type": "string",
1901
+ "format": "email"
1902
+ },
1903
+ "name": {
1904
+ "type": [
1905
+ "string",
1906
+ "null"
1907
+ ]
1908
+ },
1909
+ "orgId": {
1910
+ "type": "integer"
1911
+ },
1912
+ "role": {
1913
+ "type": "string"
1914
+ }
1915
+ },
1916
+ "required": [
1917
+ "id",
1918
+ "email",
1919
+ "orgId",
1920
+ "role"
1921
+ ]
1922
+ },
1923
+ "MobileUploadRequest": {
1924
+ "type": "object",
1925
+ "properties": {
1926
+ "roomPlanJSON": {
1927
+ "type": "object",
1928
+ "properties": {
1929
+ "rooms": {
1930
+ "type": "array",
1931
+ "items": {
1932
+ "type": "object",
1933
+ "properties": {
1934
+ "label": {
1935
+ "type": "string"
1936
+ },
1937
+ "name": {
1938
+ "type": "string"
1939
+ },
1940
+ "category": {
1941
+ "type": "string"
1942
+ },
1943
+ "walls": {
1944
+ "type": "array",
1945
+ "items": {}
1946
+ },
1947
+ "doors": {
1948
+ "type": "array",
1949
+ "items": {
1950
+ "type": "object",
1951
+ "properties": {
1952
+ "position": {
1953
+ "type": "object",
1954
+ "properties": {
1955
+ "x": {
1956
+ "type": "number"
1957
+ },
1958
+ "y": {
1959
+ "type": "number"
1960
+ },
1961
+ "z": {
1962
+ "type": "number"
1963
+ }
1964
+ },
1965
+ "required": [
1966
+ "x",
1967
+ "y",
1968
+ "z"
1969
+ ]
1970
+ },
1971
+ "width": {
1972
+ "type": "number",
1973
+ "exclusiveMinimum": 0
1974
+ },
1975
+ "height": {
1976
+ "type": "number",
1977
+ "exclusiveMinimum": 0
1978
+ },
1979
+ "hostWallIndex": {
1980
+ "type": [
1981
+ "integer",
1982
+ "null"
1983
+ ],
1984
+ "minimum": 0
1985
+ },
1986
+ "wallIndex": {
1987
+ "type": "integer",
1988
+ "minimum": 0
1989
+ },
1990
+ "swingHand": {
1991
+ "type": "string",
1992
+ "enum": [
1993
+ "left",
1994
+ "right",
1995
+ "none"
1996
+ ]
1997
+ }
1998
+ },
1999
+ "required": [
2000
+ "position",
2001
+ "width",
2002
+ "height"
2003
+ ]
2004
+ }
2005
+ },
2006
+ "windows": {
2007
+ "type": "array",
2008
+ "items": {
2009
+ "type": "object",
2010
+ "properties": {
2011
+ "position": {
2012
+ "type": "object",
2013
+ "properties": {
2014
+ "x": {
2015
+ "type": "number"
2016
+ },
2017
+ "y": {
2018
+ "type": "number"
2019
+ },
2020
+ "z": {
2021
+ "type": "number"
2022
+ }
2023
+ },
2024
+ "required": [
2025
+ "x",
2026
+ "y",
2027
+ "z"
2028
+ ]
2029
+ },
2030
+ "width": {
2031
+ "type": "number",
2032
+ "exclusiveMinimum": 0
2033
+ },
2034
+ "height": {
2035
+ "type": "number",
2036
+ "exclusiveMinimum": 0
2037
+ },
2038
+ "hostWallIndex": {
2039
+ "type": [
2040
+ "integer",
2041
+ "null"
2042
+ ],
2043
+ "minimum": 0
2044
+ },
2045
+ "wallIndex": {
2046
+ "type": "integer",
2047
+ "minimum": 0
2048
+ },
2049
+ "swingHand": {
2050
+ "type": "string",
2051
+ "enum": [
2052
+ "left",
2053
+ "right",
2054
+ "none"
2055
+ ]
2056
+ }
2057
+ },
2058
+ "required": [
2059
+ "position",
2060
+ "width",
2061
+ "height"
2062
+ ]
2063
+ }
2064
+ },
2065
+ "openings": {
2066
+ "type": "array",
2067
+ "items": {
2068
+ "type": "object",
2069
+ "properties": {
2070
+ "position": {
2071
+ "type": "object",
2072
+ "properties": {
2073
+ "x": {
2074
+ "type": "number"
2075
+ },
2076
+ "y": {
2077
+ "type": "number"
2078
+ },
2079
+ "z": {
2080
+ "type": "number"
2081
+ }
2082
+ },
2083
+ "required": [
2084
+ "x",
2085
+ "y",
2086
+ "z"
2087
+ ]
2088
+ },
2089
+ "width": {
2090
+ "type": "number",
2091
+ "exclusiveMinimum": 0
2092
+ },
2093
+ "height": {
2094
+ "type": "number",
2095
+ "exclusiveMinimum": 0
2096
+ },
2097
+ "hostWallIndex": {
2098
+ "type": [
2099
+ "integer",
2100
+ "null"
2101
+ ],
2102
+ "minimum": 0
2103
+ },
2104
+ "wallIndex": {
2105
+ "type": "integer",
2106
+ "minimum": 0
2107
+ },
2108
+ "swingHand": {
2109
+ "type": "string",
2110
+ "enum": [
2111
+ "left",
2112
+ "right",
2113
+ "none"
2114
+ ]
2115
+ }
2116
+ },
2117
+ "required": [
2118
+ "position",
2119
+ "width",
2120
+ "height"
2121
+ ]
2122
+ }
2123
+ }
2124
+ }
2125
+ }
2126
+ }
2127
+ },
2128
+ "required": [
2129
+ "rooms"
2130
+ ]
2131
+ },
2132
+ "name": {
2133
+ "type": "string",
2134
+ "minLength": 1
2135
+ },
2136
+ "description": {
2137
+ "type": "string"
2138
+ },
2139
+ "assetId": {
2140
+ "type": "string"
2141
+ },
2142
+ "autoMeasure": {
2143
+ "type": "boolean"
2144
+ },
2145
+ "cleanupPipelineVersion": {
2146
+ "type": "string"
2147
+ },
2148
+ "userOrientationHint": {
2149
+ "type": "string"
2150
+ },
2151
+ "wallConfidences": {
2152
+ "type": "array",
2153
+ "items": {}
2154
+ },
2155
+ "deviceTrack": {},
2156
+ "referencePhotos": {
2157
+ "type": "array",
2158
+ "items": {}
2159
+ },
2160
+ "propertyId": {
2161
+ "type": "integer",
2162
+ "exclusiveMinimum": 0
2163
+ },
2164
+ "floorId": {
2165
+ "type": "integer",
2166
+ "exclusiveMinimum": 0
2167
+ },
2168
+ "floorName": {
2169
+ "type": "string",
2170
+ "minLength": 1,
2171
+ "maxLength": 128
2172
+ },
2173
+ "floorLevel": {
2174
+ "type": "integer"
2175
+ },
2176
+ "roomLocalId": {
2177
+ "type": "string",
2178
+ "maxLength": 128
2179
+ },
2180
+ "roomNotes": {
2181
+ "type": "array",
2182
+ "items": {
2183
+ "type": "object",
2184
+ "properties": {
2185
+ "roomLocalId": {
2186
+ "type": "string"
2187
+ },
2188
+ "note": {
2189
+ "type": "string",
2190
+ "maxLength": 4096
2191
+ }
2192
+ },
2193
+ "required": [
2194
+ "roomLocalId",
2195
+ "note"
2196
+ ]
2197
+ }
2198
+ },
2199
+ "deviceModel": {
2200
+ "type": "string",
2201
+ "maxLength": 64
2202
+ },
2203
+ "appVersion": {
2204
+ "type": "string",
2205
+ "maxLength": 64
2206
+ },
2207
+ "captureTimestamp": {
2208
+ "type": "string"
2209
+ },
2210
+ "manualMeasurements": {
2211
+ "type": "array",
2212
+ "items": {
2213
+ "type": "object",
2214
+ "properties": {
2215
+ "id": {
2216
+ "type": "string"
2217
+ },
2218
+ "roomLocalId": {
2219
+ "type": "string"
2220
+ },
2221
+ "wallIndex": {
2222
+ "type": "integer",
2223
+ "minimum": 0
2224
+ },
2225
+ "valueMeters": {
2226
+ "type": "number",
2227
+ "exclusiveMinimum": 0,
2228
+ "maximum": 100
2229
+ },
2230
+ "capturedAt": {
2231
+ "type": "string"
2232
+ },
2233
+ "capturedBy": {
2234
+ "type": [
2235
+ "string",
2236
+ "null"
2237
+ ]
2238
+ }
2239
+ },
2240
+ "required": [
2241
+ "id",
2242
+ "roomLocalId",
2243
+ "wallIndex",
2244
+ "valueMeters",
2245
+ "capturedAt"
2246
+ ]
2247
+ }
2248
+ },
2249
+ "captureMode": {
2250
+ "type": "string",
2251
+ "enum": [
2252
+ "session",
2253
+ "view"
2254
+ ]
2255
+ },
2256
+ "sessionId": {
2257
+ "type": "string"
2258
+ },
2259
+ "sessionCapturedAt": {
2260
+ "type": "string"
2261
+ },
2262
+ "clientMirrorApplied": {
2263
+ "type": "boolean"
2264
+ }
2265
+ },
2266
+ "required": [
2267
+ "roomPlanJSON",
2268
+ "name"
2269
+ ],
2270
+ "description": "POST /api/jobs/:jobId/scans/mobile-upload body. Required: `roomPlanJSON` (object with nested `rooms` array) + `name`. All other fields are additive / optional per the v2 contract."
2271
+ },
2272
+ "MobileUploadResponse": {
2273
+ "type": "object",
2274
+ "properties": {
2275
+ "scanId": {
2276
+ "type": "integer"
2277
+ },
2278
+ "processingStatus": {
2279
+ "type": "string"
2280
+ },
2281
+ "floorId": {
2282
+ "type": [
2283
+ "integer",
2284
+ "null"
2285
+ ]
2286
+ },
2287
+ "manualMeasurementCount": {
2288
+ "type": "integer"
2289
+ },
2290
+ "message": {
2291
+ "type": "string"
2292
+ },
2293
+ "parentScanId": {
2294
+ "type": "integer"
2295
+ },
2296
+ "appendRoom": {
2297
+ "type": "boolean",
2298
+ "enum": [
2299
+ true
2300
+ ]
2301
+ },
2302
+ "idempotent": {
2303
+ "type": "boolean"
2304
+ }
2305
+ },
2306
+ "required": [
2307
+ "scanId",
2308
+ "processingStatus",
2309
+ "floorId",
2310
+ "manualMeasurementCount",
2311
+ "message"
2312
+ ]
2313
+ },
2314
+ "FeatureFlags": {
2315
+ "type": "object",
2316
+ "properties": {
2317
+ "floorPlanPivot": {
2318
+ "type": "boolean"
2319
+ },
2320
+ "manualMeasurements": {
2321
+ "type": "boolean"
2322
+ },
2323
+ "scanRoomSnapshots": {
2324
+ "type": "boolean"
2325
+ },
2326
+ "roomConnections": {
2327
+ "type": "boolean"
2328
+ }
2329
+ },
2330
+ "required": [
2331
+ "floorPlanPivot",
2332
+ "manualMeasurements",
2333
+ "scanRoomSnapshots",
2334
+ "roomConnections"
2335
+ ]
2336
+ },
2337
+ "JobSummary": {
2338
+ "type": "object",
2339
+ "properties": {
2340
+ "id": {
2341
+ "type": "integer"
2342
+ },
2343
+ "name": {
2344
+ "type": "string"
2345
+ },
2346
+ "description": {
2347
+ "type": [
2348
+ "string",
2349
+ "null"
2350
+ ]
2351
+ },
2352
+ "status": {
2353
+ "type": "string"
2354
+ },
2355
+ "propertyId": {
2356
+ "type": [
2357
+ "integer",
2358
+ "null"
2359
+ ]
2360
+ },
2361
+ "contactId": {
2362
+ "type": [
2363
+ "integer",
2364
+ "null"
2365
+ ]
2366
+ },
2367
+ "workflowId": {
2368
+ "type": [
2369
+ "integer",
2370
+ "null"
2371
+ ]
2372
+ },
2373
+ "workflowStage": {
2374
+ "type": [
2375
+ "string",
2376
+ "null"
2377
+ ]
2378
+ },
2379
+ "startDate": {
2380
+ "type": [
2381
+ "string",
2382
+ "null"
2383
+ ]
2384
+ },
2385
+ "dueDate": {
2386
+ "type": [
2387
+ "string",
2388
+ "null"
2389
+ ]
2390
+ },
2391
+ "createdAt": {
2392
+ "type": "string"
2393
+ }
2394
+ },
2395
+ "required": [
2396
+ "id",
2397
+ "name",
2398
+ "status",
2399
+ "createdAt"
2400
+ ]
2401
+ },
2402
+ "JobCreateRequest": {
2403
+ "type": "object",
2404
+ "properties": {
2405
+ "name": {
2406
+ "type": "string",
2407
+ "minLength": 1
2408
+ },
2409
+ "description": {
2410
+ "type": "string"
2411
+ },
2412
+ "propertyId": {
2413
+ "type": "integer",
2414
+ "exclusiveMinimum": 0
2415
+ },
2416
+ "contactId": {
2417
+ "type": "integer",
2418
+ "exclusiveMinimum": 0
2419
+ },
2420
+ "workflowId": {
2421
+ "type": "integer",
2422
+ "exclusiveMinimum": 0
2423
+ },
2424
+ "startDate": {
2425
+ "type": "string"
2426
+ },
2427
+ "dueDate": {
2428
+ "type": "string"
2429
+ }
2430
+ },
2431
+ "required": [
2432
+ "name"
2433
+ ]
2434
+ },
2435
+ "ChattySessionStartRequest": {
2436
+ "type": "object",
2437
+ "properties": {
2438
+ "jobId": {
2439
+ "type": "integer",
2440
+ "exclusiveMinimum": 0
2441
+ },
2442
+ "contactId": {
2443
+ "type": "integer",
2444
+ "exclusiveMinimum": 0
2445
+ },
2446
+ "locale": {
2447
+ "type": "string",
2448
+ "example": "en-US"
2449
+ },
2450
+ "voice": {
2451
+ "type": "string",
2452
+ "example": "alloy"
2453
+ },
2454
+ "systemPromptOverride": {
2455
+ "type": "string"
2456
+ }
2457
+ }
2458
+ },
2459
+ "ChattySessionToken": {
2460
+ "type": "object",
2461
+ "properties": {
2462
+ "sessionId": {
2463
+ "type": "string"
2464
+ },
2465
+ "expiresAt": {
2466
+ "type": "string"
2467
+ },
2468
+ "websocketUrl": {
2469
+ "type": "string"
2470
+ },
2471
+ "ephemeralToken": {
2472
+ "type": "string"
2473
+ },
2474
+ "model": {
2475
+ "type": "string"
2476
+ },
2477
+ "voice": {
2478
+ "type": "string"
2479
+ }
2480
+ },
2481
+ "required": [
2482
+ "sessionId",
2483
+ "expiresAt",
2484
+ "websocketUrl",
2485
+ "ephemeralToken",
2486
+ "model",
2487
+ "voice"
2488
+ ]
2489
+ },
2490
+ "ChattyMessage": {
2491
+ "type": "object",
2492
+ "properties": {
2493
+ "id": {
2494
+ "type": "string"
2495
+ },
2496
+ "role": {
2497
+ "type": "string",
2498
+ "enum": [
2499
+ "user",
2500
+ "assistant",
2501
+ "tool"
2502
+ ]
2503
+ },
2504
+ "text": {
2505
+ "type": [
2506
+ "string",
2507
+ "null"
2508
+ ]
2509
+ },
2510
+ "audioUrl": {
2511
+ "type": [
2512
+ "string",
2513
+ "null"
2514
+ ]
2515
+ },
2516
+ "createdAt": {
2517
+ "type": "string"
2518
+ },
2519
+ "toolCalls": {
2520
+ "type": "array",
2521
+ "items": {
2522
+ "type": "object",
2523
+ "properties": {
2524
+ "name": {
2525
+ "type": "string"
2526
+ },
2527
+ "arguments": {}
2528
+ },
2529
+ "required": [
2530
+ "name"
2531
+ ]
2532
+ }
2533
+ }
2534
+ },
2535
+ "required": [
2536
+ "id",
2537
+ "role",
2538
+ "createdAt"
2539
+ ]
2540
+ }
2541
+ },
2542
+ "parameters": {}
2543
+ },
2544
+ "paths": {
2545
+ "/api/version": {
2546
+ "get": {
2547
+ "summary": "Cross-platform version handshake manifest.",
2548
+ "tags": [
2549
+ "version"
2550
+ ],
2551
+ "responses": {
2552
+ "200": {
2553
+ "description": "Always returned. Body is the canonical handshake manifest.",
2554
+ "content": {
2555
+ "application/json": {
2556
+ "schema": {
2557
+ "$ref": "#/components/schemas/VersionManifest"
2558
+ }
2559
+ }
2560
+ }
2561
+ }
2562
+ }
2563
+ }
2564
+ },
2565
+ "/api/ping": {
2566
+ "get": {
2567
+ "summary": "Liveness probe. Bypasses the version handshake.",
2568
+ "tags": [
2569
+ "version"
2570
+ ],
2571
+ "responses": {
2572
+ "200": {
2573
+ "description": "Static OK envelope.",
2574
+ "content": {
2575
+ "application/json": {
2576
+ "schema": {
2577
+ "type": "object",
2578
+ "properties": {
2579
+ "status": {
2580
+ "type": "string",
2581
+ "enum": [
2582
+ "ok"
2583
+ ]
2584
+ },
2585
+ "timestamp": {
2586
+ "type": "string"
2587
+ },
2588
+ "env": {
2589
+ "type": [
2590
+ "string",
2591
+ "null"
2592
+ ]
2593
+ }
2594
+ },
2595
+ "required": [
2596
+ "status",
2597
+ "timestamp",
2598
+ "env"
2599
+ ]
2600
+ }
2601
+ }
2602
+ }
2603
+ }
2604
+ }
2605
+ }
2606
+ },
2607
+ "/api/me/features": {
2608
+ "get": {
2609
+ "summary": "Capability discovery — iOS calls on launch to gate UI affordances.",
2610
+ "tags": [
2611
+ "auth",
2612
+ "features"
2613
+ ],
2614
+ "parameters": [
2615
+ {
2616
+ "name": "X-Client-Version",
2617
+ "in": "header",
2618
+ "required": false,
2619
+ "schema": {
2620
+ "type": "string",
2621
+ "example": "1.0.0"
2622
+ },
2623
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
2624
+ }
2625
+ ],
2626
+ "responses": {
2627
+ "200": {
2628
+ "description": "Flat feature map. Unknown / missing flags must be treated as false by the client.",
2629
+ "content": {
2630
+ "application/json": {
2631
+ "schema": {
2632
+ "$ref": "#/components/schemas/FeatureFlags"
2633
+ }
2634
+ }
2635
+ }
2636
+ },
2637
+ "426": {
2638
+ "description": "Client major below minClientVersion.",
2639
+ "content": {
2640
+ "application/json": {
2641
+ "schema": {
2642
+ "$ref": "#/components/schemas/UpgradeRequiredEnvelope"
2643
+ }
2644
+ }
2645
+ }
2646
+ }
2647
+ }
2648
+ }
2649
+ },
2650
+ "/api/jobs/{jobId}/scans/mobile-upload": {
2651
+ "post": {
2652
+ "summary": "iOS LiDAR scan upload. v2 fields are additive — v1 payloads remain byte-identical.",
2653
+ "tags": [
2654
+ "lidar"
2655
+ ],
2656
+ "parameters": [
2657
+ {
2658
+ "name": "X-Client-Version",
2659
+ "in": "header",
2660
+ "required": false,
2661
+ "schema": {
2662
+ "type": "string",
2663
+ "example": "1.0.0"
2664
+ },
2665
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
2666
+ },
2667
+ {
2668
+ "schema": {
2669
+ "type": "integer",
2670
+ "exclusiveMinimum": 0
2671
+ },
2672
+ "required": true,
2673
+ "name": "jobId",
2674
+ "in": "path"
2675
+ }
2676
+ ],
2677
+ "requestBody": {
2678
+ "content": {
2679
+ "application/json": {
2680
+ "schema": {
2681
+ "$ref": "#/components/schemas/MobileUploadRequest"
2682
+ }
2683
+ }
2684
+ }
2685
+ },
2686
+ "responses": {
2687
+ "200": {
2688
+ "description": "Idempotent replay of a session-mode upload (same sessionId).",
2689
+ "content": {
2690
+ "application/json": {
2691
+ "schema": {
2692
+ "$ref": "#/components/schemas/MobileUploadResponse"
2693
+ }
2694
+ }
2695
+ }
2696
+ },
2697
+ "201": {
2698
+ "description": "Scan created (or composite child appended via parentScanId+entryDoorwayId).",
2699
+ "content": {
2700
+ "application/json": {
2701
+ "schema": {
2702
+ "$ref": "#/components/schemas/MobileUploadResponse"
2703
+ }
2704
+ }
2705
+ }
2706
+ },
2707
+ "400": {
2708
+ "description": "Validation failure.",
2709
+ "content": {
2710
+ "application/json": {
2711
+ "schema": {
2712
+ "$ref": "#/components/schemas/ErrorEnvelope"
2713
+ }
2714
+ }
2715
+ }
2716
+ },
2717
+ "403": {
2718
+ "description": "Caller not assigned to this job.",
2719
+ "content": {
2720
+ "application/json": {
2721
+ "schema": {
2722
+ "$ref": "#/components/schemas/ErrorEnvelope"
2723
+ }
2724
+ }
2725
+ }
2726
+ },
2727
+ "404": {
2728
+ "description": "Job not found.",
2729
+ "content": {
2730
+ "application/json": {
2731
+ "schema": {
2732
+ "$ref": "#/components/schemas/ErrorEnvelope"
2733
+ }
2734
+ }
2735
+ }
2736
+ },
2737
+ "426": {
2738
+ "description": "Client major below minClientVersion.",
2739
+ "content": {
2740
+ "application/json": {
2741
+ "schema": {
2742
+ "$ref": "#/components/schemas/UpgradeRequiredEnvelope"
2743
+ }
2744
+ }
2745
+ }
2746
+ }
2747
+ }
2748
+ }
2749
+ },
2750
+ "/api/jobs/{jobId}/floors": {
2751
+ "post": {
2752
+ "summary": "Idempotent floor mint. Lookup is case-/whitespace-insensitive on name.",
2753
+ "tags": [
2754
+ "lidar"
2755
+ ],
2756
+ "parameters": [
2757
+ {
2758
+ "name": "X-Client-Version",
2759
+ "in": "header",
2760
+ "required": false,
2761
+ "schema": {
2762
+ "type": "string",
2763
+ "example": "1.0.0"
2764
+ },
2765
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
2766
+ },
2767
+ {
2768
+ "schema": {
2769
+ "type": "integer",
2770
+ "exclusiveMinimum": 0
2771
+ },
2772
+ "required": true,
2773
+ "name": "jobId",
2774
+ "in": "path"
2775
+ }
2776
+ ],
2777
+ "requestBody": {
2778
+ "content": {
2779
+ "application/json": {
2780
+ "schema": {
2781
+ "type": "object",
2782
+ "properties": {
2783
+ "name": {
2784
+ "type": "string",
2785
+ "minLength": 1,
2786
+ "maxLength": 128
2787
+ },
2788
+ "level": {
2789
+ "type": "integer"
2790
+ },
2791
+ "sortOrder": {
2792
+ "type": "integer"
2793
+ },
2794
+ "notes": {
2795
+ "type": "string"
2796
+ },
2797
+ "propertyId": {
2798
+ "type": "integer",
2799
+ "exclusiveMinimum": 0
2800
+ }
2801
+ },
2802
+ "required": [
2803
+ "name"
2804
+ ]
2805
+ }
2806
+ }
2807
+ }
2808
+ },
2809
+ "responses": {
2810
+ "200": {
2811
+ "description": "Existing floor matched by normalized name.",
2812
+ "content": {
2813
+ "application/json": {
2814
+ "schema": {
2815
+ "type": "object",
2816
+ "properties": {
2817
+ "created": {
2818
+ "type": "boolean",
2819
+ "enum": [
2820
+ false
2821
+ ]
2822
+ },
2823
+ "floor": {
2824
+ "type": "object",
2825
+ "properties": {
2826
+ "propertyId": {
2827
+ "type": "integer",
2828
+ "minimum": -2147483648,
2829
+ "maximum": 2147483647
2830
+ },
2831
+ "organizationId": {
2832
+ "type": "string"
2833
+ },
2834
+ "name": {
2835
+ "type": "string"
2836
+ },
2837
+ "level": {
2838
+ "type": "integer",
2839
+ "minimum": -2147483648,
2840
+ "maximum": 2147483647
2841
+ },
2842
+ "sortOrder": {
2843
+ "type": "integer",
2844
+ "minimum": -2147483648,
2845
+ "maximum": 2147483647
2846
+ },
2847
+ "notes": {
2848
+ "type": [
2849
+ "string",
2850
+ "null"
2851
+ ]
2852
+ },
2853
+ "id": {
2854
+ "type": "number"
2855
+ }
2856
+ },
2857
+ "required": [
2858
+ "propertyId",
2859
+ "organizationId",
2860
+ "id"
2861
+ ]
2862
+ }
2863
+ },
2864
+ "required": [
2865
+ "created",
2866
+ "floor"
2867
+ ]
2868
+ }
2869
+ }
2870
+ }
2871
+ },
2872
+ "201": {
2873
+ "description": "Fresh floor inserted.",
2874
+ "content": {
2875
+ "application/json": {
2876
+ "schema": {
2877
+ "type": "object",
2878
+ "properties": {
2879
+ "created": {
2880
+ "type": "boolean",
2881
+ "enum": [
2882
+ true
2883
+ ]
2884
+ },
2885
+ "floor": {
2886
+ "type": "object",
2887
+ "properties": {
2888
+ "propertyId": {
2889
+ "type": "integer",
2890
+ "minimum": -2147483648,
2891
+ "maximum": 2147483647
2892
+ },
2893
+ "organizationId": {
2894
+ "type": "string"
2895
+ },
2896
+ "name": {
2897
+ "type": "string"
2898
+ },
2899
+ "level": {
2900
+ "type": "integer",
2901
+ "minimum": -2147483648,
2902
+ "maximum": 2147483647
2903
+ },
2904
+ "sortOrder": {
2905
+ "type": "integer",
2906
+ "minimum": -2147483648,
2907
+ "maximum": 2147483647
2908
+ },
2909
+ "notes": {
2910
+ "type": [
2911
+ "string",
2912
+ "null"
2913
+ ]
2914
+ },
2915
+ "id": {
2916
+ "type": "number"
2917
+ }
2918
+ },
2919
+ "required": [
2920
+ "propertyId",
2921
+ "organizationId",
2922
+ "id"
2923
+ ]
2924
+ }
2925
+ },
2926
+ "required": [
2927
+ "created",
2928
+ "floor"
2929
+ ]
2930
+ }
2931
+ }
2932
+ }
2933
+ },
2934
+ "400": {
2935
+ "description": "Validation failure.",
2936
+ "content": {
2937
+ "application/json": {
2938
+ "schema": {
2939
+ "$ref": "#/components/schemas/ErrorEnvelope"
2940
+ }
2941
+ }
2942
+ }
2943
+ },
2944
+ "403": {
2945
+ "description": "Caller cannot reach this job.",
2946
+ "content": {
2947
+ "application/json": {
2948
+ "schema": {
2949
+ "$ref": "#/components/schemas/ErrorEnvelope"
2950
+ }
2951
+ }
2952
+ }
2953
+ }
2954
+ }
2955
+ }
2956
+ },
2957
+ "/api/jobs": {
2958
+ "get": {
2959
+ "summary": "List jobs (projects) visible to the caller.",
2960
+ "tags": [
2961
+ "jobs"
2962
+ ],
2963
+ "parameters": [
2964
+ {
2965
+ "name": "X-Client-Version",
2966
+ "in": "header",
2967
+ "required": false,
2968
+ "schema": {
2969
+ "type": "string",
2970
+ "example": "1.0.0"
2971
+ },
2972
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
2973
+ }
2974
+ ],
2975
+ "responses": {
2976
+ "200": {
2977
+ "description": "Array of jobs.",
2978
+ "content": {
2979
+ "application/json": {
2980
+ "schema": {
2981
+ "type": "array",
2982
+ "items": {
2983
+ "$ref": "#/components/schemas/JobSummary"
2984
+ }
2985
+ }
2986
+ }
2987
+ }
2988
+ },
2989
+ "401": {
2990
+ "description": "Unauthenticated.",
2991
+ "content": {
2992
+ "application/json": {
2993
+ "schema": {
2994
+ "$ref": "#/components/schemas/ErrorEnvelope"
2995
+ }
2996
+ }
2997
+ }
2998
+ }
2999
+ }
3000
+ },
3001
+ "post": {
3002
+ "summary": "Create a job.",
3003
+ "tags": [
3004
+ "jobs"
3005
+ ],
3006
+ "parameters": [
3007
+ {
3008
+ "name": "X-Client-Version",
3009
+ "in": "header",
3010
+ "required": false,
3011
+ "schema": {
3012
+ "type": "string",
3013
+ "example": "1.0.0"
3014
+ },
3015
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
3016
+ }
3017
+ ],
3018
+ "requestBody": {
3019
+ "content": {
3020
+ "application/json": {
3021
+ "schema": {
3022
+ "$ref": "#/components/schemas/JobCreateRequest"
3023
+ }
3024
+ }
3025
+ }
3026
+ },
3027
+ "responses": {
3028
+ "201": {
3029
+ "description": "Job created.",
3030
+ "content": {
3031
+ "application/json": {
3032
+ "schema": {
3033
+ "$ref": "#/components/schemas/JobSummary"
3034
+ }
3035
+ }
3036
+ }
3037
+ },
3038
+ "400": {
3039
+ "description": "Validation failure.",
3040
+ "content": {
3041
+ "application/json": {
3042
+ "schema": {
3043
+ "$ref": "#/components/schemas/ErrorEnvelope"
3044
+ }
3045
+ }
3046
+ }
3047
+ }
3048
+ }
3049
+ }
3050
+ },
3051
+ "/api/jobs/{jobId}": {
3052
+ "get": {
3053
+ "summary": "Fetch a single job by id.",
3054
+ "tags": [
3055
+ "jobs"
3056
+ ],
3057
+ "parameters": [
3058
+ {
3059
+ "name": "X-Client-Version",
3060
+ "in": "header",
3061
+ "required": false,
3062
+ "schema": {
3063
+ "type": "string",
3064
+ "example": "1.0.0"
3065
+ },
3066
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
3067
+ },
3068
+ {
3069
+ "schema": {
3070
+ "type": "integer",
3071
+ "exclusiveMinimum": 0
3072
+ },
3073
+ "required": true,
3074
+ "name": "jobId",
3075
+ "in": "path"
3076
+ }
3077
+ ],
3078
+ "responses": {
3079
+ "200": {
3080
+ "description": "Job detail.",
3081
+ "content": {
3082
+ "application/json": {
3083
+ "schema": {
3084
+ "$ref": "#/components/schemas/JobSummary"
3085
+ }
3086
+ }
3087
+ }
3088
+ },
3089
+ "403": {
3090
+ "description": "Caller cannot reach this job.",
3091
+ "content": {
3092
+ "application/json": {
3093
+ "schema": {
3094
+ "$ref": "#/components/schemas/ErrorEnvelope"
3095
+ }
3096
+ }
3097
+ }
3098
+ },
3099
+ "404": {
3100
+ "description": "Job not found.",
3101
+ "content": {
3102
+ "application/json": {
3103
+ "schema": {
3104
+ "$ref": "#/components/schemas/ErrorEnvelope"
3105
+ }
3106
+ }
3107
+ }
3108
+ }
3109
+ }
3110
+ }
3111
+ },
3112
+ "/api/chatty/sessions": {
3113
+ "post": {
3114
+ "summary": "Mint an ephemeral realtime voice session token.",
3115
+ "tags": [
3116
+ "chatty"
3117
+ ],
3118
+ "parameters": [
3119
+ {
3120
+ "name": "X-Client-Version",
3121
+ "in": "header",
3122
+ "required": false,
3123
+ "schema": {
3124
+ "type": "string",
3125
+ "example": "1.0.0"
3126
+ },
3127
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
3128
+ }
3129
+ ],
3130
+ "requestBody": {
3131
+ "content": {
3132
+ "application/json": {
3133
+ "schema": {
3134
+ "$ref": "#/components/schemas/ChattySessionStartRequest"
3135
+ }
3136
+ }
3137
+ }
3138
+ },
3139
+ "responses": {
3140
+ "201": {
3141
+ "description": "Session created. Connect to websocketUrl with ephemeralToken in the Authorization header.",
3142
+ "content": {
3143
+ "application/json": {
3144
+ "schema": {
3145
+ "$ref": "#/components/schemas/ChattySessionToken"
3146
+ }
3147
+ }
3148
+ }
3149
+ },
3150
+ "400": {
3151
+ "description": "Validation failure.",
3152
+ "content": {
3153
+ "application/json": {
3154
+ "schema": {
3155
+ "$ref": "#/components/schemas/ErrorEnvelope"
3156
+ }
3157
+ }
3158
+ }
3159
+ },
3160
+ "401": {
3161
+ "description": "Unauthenticated.",
3162
+ "content": {
3163
+ "application/json": {
3164
+ "schema": {
3165
+ "$ref": "#/components/schemas/ErrorEnvelope"
3166
+ }
3167
+ }
3168
+ }
3169
+ },
3170
+ "426": {
3171
+ "description": "Client major below minClientVersion.",
3172
+ "content": {
3173
+ "application/json": {
3174
+ "schema": {
3175
+ "$ref": "#/components/schemas/UpgradeRequiredEnvelope"
3176
+ }
3177
+ }
3178
+ }
3179
+ }
3180
+ }
3181
+ }
3182
+ },
3183
+ "/api/chatty/sessions/{sessionId}/messages": {
3184
+ "get": {
3185
+ "summary": "List transcript messages for a Chatty session.",
3186
+ "tags": [
3187
+ "chatty"
3188
+ ],
3189
+ "parameters": [
3190
+ {
3191
+ "name": "X-Client-Version",
3192
+ "in": "header",
3193
+ "required": false,
3194
+ "schema": {
3195
+ "type": "string",
3196
+ "example": "1.0.0"
3197
+ },
3198
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
3199
+ },
3200
+ {
3201
+ "schema": {
3202
+ "type": "string"
3203
+ },
3204
+ "required": true,
3205
+ "name": "sessionId",
3206
+ "in": "path"
3207
+ }
3208
+ ],
3209
+ "responses": {
3210
+ "200": {
3211
+ "description": "Ordered transcript.",
3212
+ "content": {
3213
+ "application/json": {
3214
+ "schema": {
3215
+ "type": "array",
3216
+ "items": {
3217
+ "$ref": "#/components/schemas/ChattyMessage"
3218
+ }
3219
+ }
3220
+ }
3221
+ }
3222
+ },
3223
+ "403": {
3224
+ "description": "Session does not belong to caller.",
3225
+ "content": {
3226
+ "application/json": {
3227
+ "schema": {
3228
+ "$ref": "#/components/schemas/ErrorEnvelope"
3229
+ }
3230
+ }
3231
+ }
3232
+ },
3233
+ "404": {
3234
+ "description": "Session not found.",
3235
+ "content": {
3236
+ "application/json": {
3237
+ "schema": {
3238
+ "$ref": "#/components/schemas/ErrorEnvelope"
3239
+ }
3240
+ }
3241
+ }
3242
+ }
3243
+ }
3244
+ }
3245
+ },
3246
+ "/api/chatty/sessions/{sessionId}": {
3247
+ "delete": {
3248
+ "summary": "End a Chatty session and revoke its ephemeral token.",
3249
+ "tags": [
3250
+ "chatty"
3251
+ ],
3252
+ "parameters": [
3253
+ {
3254
+ "name": "X-Client-Version",
3255
+ "in": "header",
3256
+ "required": false,
3257
+ "schema": {
3258
+ "type": "string",
3259
+ "example": "1.0.0"
3260
+ },
3261
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
3262
+ },
3263
+ {
3264
+ "schema": {
3265
+ "type": "string"
3266
+ },
3267
+ "required": true,
3268
+ "name": "sessionId",
3269
+ "in": "path"
3270
+ }
3271
+ ],
3272
+ "responses": {
3273
+ "204": {
3274
+ "description": "Session terminated."
3275
+ },
3276
+ "403": {
3277
+ "description": "Session does not belong to caller.",
3278
+ "content": {
3279
+ "application/json": {
3280
+ "schema": {
3281
+ "$ref": "#/components/schemas/ErrorEnvelope"
3282
+ }
3283
+ }
3284
+ }
3285
+ },
3286
+ "404": {
3287
+ "description": "Session not found.",
3288
+ "content": {
3289
+ "application/json": {
3290
+ "schema": {
3291
+ "$ref": "#/components/schemas/ErrorEnvelope"
3292
+ }
3293
+ }
3294
+ }
3295
+ }
3296
+ }
3297
+ }
3298
+ },
3299
+ "/api/me": {
3300
+ "get": {
3301
+ "summary": "Current session principal. iOS calls this immediately after /api/version on launch.",
3302
+ "tags": [
3303
+ "auth"
3304
+ ],
3305
+ "parameters": [
3306
+ {
3307
+ "name": "X-Client-Version",
3308
+ "in": "header",
3309
+ "required": false,
3310
+ "schema": {
3311
+ "type": "string",
3312
+ "example": "1.0.0"
3313
+ },
3314
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
3315
+ }
3316
+ ],
3317
+ "responses": {
3318
+ "200": {
3319
+ "description": "Authenticated user.",
3320
+ "content": {
3321
+ "application/json": {
3322
+ "schema": {
3323
+ "$ref": "#/components/schemas/MeResponse"
3324
+ }
3325
+ }
3326
+ }
3327
+ },
3328
+ "401": {
3329
+ "description": "No active session.",
3330
+ "content": {
3331
+ "application/json": {
3332
+ "schema": {
3333
+ "$ref": "#/components/schemas/ErrorEnvelope"
3334
+ }
3335
+ }
3336
+ }
3337
+ },
3338
+ "426": {
3339
+ "description": "Client major below minClientVersion.",
3340
+ "content": {
3341
+ "application/json": {
3342
+ "schema": {
3343
+ "$ref": "#/components/schemas/UpgradeRequiredEnvelope"
3344
+ }
3345
+ }
3346
+ }
3347
+ }
3348
+ }
3349
+ }
3350
+ },
3351
+ "/api/contacts": {
3352
+ "get": {
3353
+ "summary": "List contacts in the caller's org.",
3354
+ "tags": [
3355
+ "crm"
3356
+ ],
3357
+ "parameters": [
3358
+ {
3359
+ "name": "X-Client-Version",
3360
+ "in": "header",
3361
+ "required": false,
3362
+ "schema": {
3363
+ "type": "string",
3364
+ "example": "1.0.0"
3365
+ },
3366
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
3367
+ }
3368
+ ],
3369
+ "responses": {
3370
+ "200": {
3371
+ "description": "Array of contacts.",
3372
+ "content": {
3373
+ "application/json": {
3374
+ "schema": {
3375
+ "type": "array",
3376
+ "items": {
3377
+ "allOf": [
3378
+ {
3379
+ "type": "object",
3380
+ "properties": {
3381
+ "userId": {
3382
+ "type": [
3383
+ "string",
3384
+ "null"
3385
+ ]
3386
+ },
3387
+ "organizationId": {
3388
+ "type": [
3389
+ "string",
3390
+ "null"
3391
+ ]
3392
+ },
3393
+ "customerNumber": {
3394
+ "type": [
3395
+ "integer",
3396
+ "null"
3397
+ ],
3398
+ "minimum": -2147483648,
3399
+ "maximum": 2147483647
3400
+ },
3401
+ "name": {
3402
+ "type": "string"
3403
+ },
3404
+ "email": {
3405
+ "type": [
3406
+ "string",
3407
+ "null"
3408
+ ]
3409
+ },
3410
+ "phone": {
3411
+ "type": [
3412
+ "string",
3413
+ "null"
3414
+ ]
3415
+ },
3416
+ "company": {
3417
+ "type": [
3418
+ "string",
3419
+ "null"
3420
+ ]
3421
+ },
3422
+ "address": {
3423
+ "type": [
3424
+ "string",
3425
+ "null"
3426
+ ]
3427
+ },
3428
+ "status": {
3429
+ "type": [
3430
+ "string",
3431
+ "null"
3432
+ ]
3433
+ },
3434
+ "customFields": {
3435
+ "anyOf": [
3436
+ {
3437
+ "type": "string"
3438
+ },
3439
+ {
3440
+ "type": "number"
3441
+ },
3442
+ {
3443
+ "type": "boolean"
3444
+ },
3445
+ {
3446
+ "type": "null"
3447
+ },
3448
+ {
3449
+ "type": "object",
3450
+ "additionalProperties": {}
3451
+ },
3452
+ {
3453
+ "type": "array",
3454
+ "items": {}
3455
+ },
3456
+ {
3457
+ "type": "null"
3458
+ }
3459
+ ]
3460
+ },
3461
+ "lastContactDate": {
3462
+ "type": [
3463
+ "string",
3464
+ "null"
3465
+ ]
3466
+ },
3467
+ "parentContactId": {
3468
+ "type": [
3469
+ "integer",
3470
+ "null"
3471
+ ],
3472
+ "minimum": -2147483648,
3473
+ "maximum": 2147483647
3474
+ },
3475
+ "relationship": {
3476
+ "type": [
3477
+ "string",
3478
+ "null"
3479
+ ]
3480
+ },
3481
+ "role": {
3482
+ "type": [
3483
+ "string",
3484
+ "null"
3485
+ ]
3486
+ },
3487
+ "firstName": {
3488
+ "type": [
3489
+ "string",
3490
+ "null"
3491
+ ]
3492
+ },
3493
+ "lastName": {
3494
+ "type": [
3495
+ "string",
3496
+ "null"
3497
+ ]
3498
+ },
3499
+ "serviceAddress": {
3500
+ "type": [
3501
+ "string",
3502
+ "null"
3503
+ ]
3504
+ },
3505
+ "billingAddress": {
3506
+ "type": [
3507
+ "string",
3508
+ "null"
3509
+ ]
3510
+ },
3511
+ "isCommercial": {
3512
+ "type": [
3513
+ "boolean",
3514
+ "null"
3515
+ ]
3516
+ },
3517
+ "pointOfContactName": {
3518
+ "type": [
3519
+ "string",
3520
+ "null"
3521
+ ]
3522
+ },
3523
+ "pointOfContactTitle": {
3524
+ "type": [
3525
+ "string",
3526
+ "null"
3527
+ ]
3528
+ },
3529
+ "pointOfContactPhone": {
3530
+ "type": [
3531
+ "string",
3532
+ "null"
3533
+ ]
3534
+ },
3535
+ "primaryEmail": {
3536
+ "type": [
3537
+ "string",
3538
+ "null"
3539
+ ]
3540
+ },
3541
+ "propertyAddress": {
3542
+ "type": [
3543
+ "string",
3544
+ "null"
3545
+ ]
3546
+ },
3547
+ "leadSource": {
3548
+ "type": [
3549
+ "string",
3550
+ "null"
3551
+ ]
3552
+ },
3553
+ "leadSourceReferralId": {
3554
+ "type": [
3555
+ "integer",
3556
+ "null"
3557
+ ],
3558
+ "minimum": -2147483648,
3559
+ "maximum": 2147483647
3560
+ },
3561
+ "leadSourceReferralUserId": {
3562
+ "type": [
3563
+ "string",
3564
+ "null"
3565
+ ]
3566
+ },
3567
+ "leadSourceOther": {
3568
+ "type": [
3569
+ "string",
3570
+ "null"
3571
+ ]
3572
+ },
3573
+ "googleDriveCustomerFolderId": {
3574
+ "type": [
3575
+ "string",
3576
+ "null"
3577
+ ]
3578
+ },
3579
+ "cachedLat": {
3580
+ "type": [
3581
+ "number",
3582
+ "null"
3583
+ ],
3584
+ "minimum": -140737488355328,
3585
+ "maximum": 140737488355327
3586
+ },
3587
+ "cachedLng": {
3588
+ "type": [
3589
+ "number",
3590
+ "null"
3591
+ ],
3592
+ "minimum": -140737488355328,
3593
+ "maximum": 140737488355327
3594
+ },
3595
+ "qboId": {
3596
+ "type": [
3597
+ "string",
3598
+ "null"
3599
+ ]
3600
+ },
3601
+ "qboSyncToken": {
3602
+ "type": [
3603
+ "string",
3604
+ "null"
3605
+ ]
3606
+ },
3607
+ "qboLastPushedAt": {
3608
+ "type": [
3609
+ "string",
3610
+ "null"
3611
+ ]
3612
+ },
3613
+ "qboLastPushedHash": {
3614
+ "type": [
3615
+ "string",
3616
+ "null"
3617
+ ]
3618
+ },
3619
+ "qboLastError": {
3620
+ "type": [
3621
+ "string",
3622
+ "null"
3623
+ ]
3624
+ },
3625
+ "deletedAt": {
3626
+ "type": [
3627
+ "string",
3628
+ "null"
3629
+ ]
3630
+ },
3631
+ "deletedBy": {
3632
+ "type": [
3633
+ "string",
3634
+ "null"
3635
+ ]
3636
+ }
3637
+ },
3638
+ "required": [
3639
+ "name"
3640
+ ]
3641
+ },
3642
+ {
3643
+ "type": "object",
3644
+ "properties": {
3645
+ "id": {
3646
+ "type": "number"
3647
+ }
3648
+ },
3649
+ "required": [
3650
+ "id"
3651
+ ]
3652
+ }
3653
+ ]
3654
+ }
3655
+ }
3656
+ }
3657
+ }
3658
+ },
3659
+ "401": {
3660
+ "description": "Unauthenticated.",
3661
+ "content": {
3662
+ "application/json": {
3663
+ "schema": {
3664
+ "$ref": "#/components/schemas/ErrorEnvelope"
3665
+ }
3666
+ }
3667
+ }
3668
+ }
3669
+ }
3670
+ },
3671
+ "post": {
3672
+ "summary": "Create a contact.",
3673
+ "tags": [
3674
+ "crm"
3675
+ ],
3676
+ "parameters": [
3677
+ {
3678
+ "name": "X-Client-Version",
3679
+ "in": "header",
3680
+ "required": false,
3681
+ "schema": {
3682
+ "type": "string",
3683
+ "example": "1.0.0"
3684
+ },
3685
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
3686
+ }
3687
+ ],
3688
+ "requestBody": {
3689
+ "content": {
3690
+ "application/json": {
3691
+ "schema": {
3692
+ "type": "object",
3693
+ "properties": {
3694
+ "userId": {
3695
+ "type": [
3696
+ "string",
3697
+ "null"
3698
+ ]
3699
+ },
3700
+ "organizationId": {
3701
+ "type": [
3702
+ "string",
3703
+ "null"
3704
+ ]
3705
+ },
3706
+ "customerNumber": {
3707
+ "type": [
3708
+ "integer",
3709
+ "null"
3710
+ ],
3711
+ "minimum": -2147483648,
3712
+ "maximum": 2147483647
3713
+ },
3714
+ "name": {
3715
+ "type": "string"
3716
+ },
3717
+ "email": {
3718
+ "type": [
3719
+ "string",
3720
+ "null"
3721
+ ]
3722
+ },
3723
+ "phone": {
3724
+ "type": [
3725
+ "string",
3726
+ "null"
3727
+ ]
3728
+ },
3729
+ "company": {
3730
+ "type": [
3731
+ "string",
3732
+ "null"
3733
+ ]
3734
+ },
3735
+ "address": {
3736
+ "type": [
3737
+ "string",
3738
+ "null"
3739
+ ]
3740
+ },
3741
+ "status": {
3742
+ "type": [
3743
+ "string",
3744
+ "null"
3745
+ ]
3746
+ },
3747
+ "customFields": {
3748
+ "anyOf": [
3749
+ {
3750
+ "type": "string"
3751
+ },
3752
+ {
3753
+ "type": "number"
3754
+ },
3755
+ {
3756
+ "type": "boolean"
3757
+ },
3758
+ {
3759
+ "type": "null"
3760
+ },
3761
+ {
3762
+ "type": "object",
3763
+ "additionalProperties": {}
3764
+ },
3765
+ {
3766
+ "type": "array",
3767
+ "items": {}
3768
+ },
3769
+ {
3770
+ "type": "null"
3771
+ }
3772
+ ]
3773
+ },
3774
+ "lastContactDate": {
3775
+ "type": [
3776
+ "string",
3777
+ "null"
3778
+ ]
3779
+ },
3780
+ "parentContactId": {
3781
+ "type": [
3782
+ "integer",
3783
+ "null"
3784
+ ],
3785
+ "minimum": -2147483648,
3786
+ "maximum": 2147483647
3787
+ },
3788
+ "relationship": {
3789
+ "type": [
3790
+ "string",
3791
+ "null"
3792
+ ]
3793
+ },
3794
+ "role": {
3795
+ "type": [
3796
+ "string",
3797
+ "null"
3798
+ ]
3799
+ },
3800
+ "firstName": {
3801
+ "type": [
3802
+ "string",
3803
+ "null"
3804
+ ]
3805
+ },
3806
+ "lastName": {
3807
+ "type": [
3808
+ "string",
3809
+ "null"
3810
+ ]
3811
+ },
3812
+ "serviceAddress": {
3813
+ "type": [
3814
+ "string",
3815
+ "null"
3816
+ ]
3817
+ },
3818
+ "billingAddress": {
3819
+ "type": [
3820
+ "string",
3821
+ "null"
3822
+ ]
3823
+ },
3824
+ "isCommercial": {
3825
+ "type": [
3826
+ "boolean",
3827
+ "null"
3828
+ ]
3829
+ },
3830
+ "pointOfContactName": {
3831
+ "type": [
3832
+ "string",
3833
+ "null"
3834
+ ]
3835
+ },
3836
+ "pointOfContactTitle": {
3837
+ "type": [
3838
+ "string",
3839
+ "null"
3840
+ ]
3841
+ },
3842
+ "pointOfContactPhone": {
3843
+ "type": [
3844
+ "string",
3845
+ "null"
3846
+ ]
3847
+ },
3848
+ "primaryEmail": {
3849
+ "type": [
3850
+ "string",
3851
+ "null"
3852
+ ]
3853
+ },
3854
+ "propertyAddress": {
3855
+ "type": [
3856
+ "string",
3857
+ "null"
3858
+ ]
3859
+ },
3860
+ "leadSource": {
3861
+ "type": [
3862
+ "string",
3863
+ "null"
3864
+ ]
3865
+ },
3866
+ "leadSourceReferralId": {
3867
+ "type": [
3868
+ "integer",
3869
+ "null"
3870
+ ],
3871
+ "minimum": -2147483648,
3872
+ "maximum": 2147483647
3873
+ },
3874
+ "leadSourceReferralUserId": {
3875
+ "type": [
3876
+ "string",
3877
+ "null"
3878
+ ]
3879
+ },
3880
+ "leadSourceOther": {
3881
+ "type": [
3882
+ "string",
3883
+ "null"
3884
+ ]
3885
+ },
3886
+ "googleDriveCustomerFolderId": {
3887
+ "type": [
3888
+ "string",
3889
+ "null"
3890
+ ]
3891
+ },
3892
+ "cachedLat": {
3893
+ "type": [
3894
+ "number",
3895
+ "null"
3896
+ ],
3897
+ "minimum": -140737488355328,
3898
+ "maximum": 140737488355327
3899
+ },
3900
+ "cachedLng": {
3901
+ "type": [
3902
+ "number",
3903
+ "null"
3904
+ ],
3905
+ "minimum": -140737488355328,
3906
+ "maximum": 140737488355327
3907
+ },
3908
+ "qboId": {
3909
+ "type": [
3910
+ "string",
3911
+ "null"
3912
+ ]
3913
+ },
3914
+ "qboSyncToken": {
3915
+ "type": [
3916
+ "string",
3917
+ "null"
3918
+ ]
3919
+ },
3920
+ "qboLastPushedAt": {
3921
+ "type": [
3922
+ "string",
3923
+ "null"
3924
+ ]
3925
+ },
3926
+ "qboLastPushedHash": {
3927
+ "type": [
3928
+ "string",
3929
+ "null"
3930
+ ]
3931
+ },
3932
+ "qboLastError": {
3933
+ "type": [
3934
+ "string",
3935
+ "null"
3936
+ ]
3937
+ },
3938
+ "deletedAt": {
3939
+ "type": [
3940
+ "string",
3941
+ "null"
3942
+ ]
3943
+ },
3944
+ "deletedBy": {
3945
+ "type": [
3946
+ "string",
3947
+ "null"
3948
+ ]
3949
+ }
3950
+ },
3951
+ "required": [
3952
+ "name"
3953
+ ]
3954
+ }
3955
+ }
3956
+ }
3957
+ },
3958
+ "responses": {
3959
+ "201": {
3960
+ "description": "Created.",
3961
+ "content": {
3962
+ "application/json": {
3963
+ "schema": {
3964
+ "allOf": [
3965
+ {
3966
+ "type": "object",
3967
+ "properties": {
3968
+ "userId": {
3969
+ "type": [
3970
+ "string",
3971
+ "null"
3972
+ ]
3973
+ },
3974
+ "organizationId": {
3975
+ "type": [
3976
+ "string",
3977
+ "null"
3978
+ ]
3979
+ },
3980
+ "customerNumber": {
3981
+ "type": [
3982
+ "integer",
3983
+ "null"
3984
+ ],
3985
+ "minimum": -2147483648,
3986
+ "maximum": 2147483647
3987
+ },
3988
+ "name": {
3989
+ "type": "string"
3990
+ },
3991
+ "email": {
3992
+ "type": [
3993
+ "string",
3994
+ "null"
3995
+ ]
3996
+ },
3997
+ "phone": {
3998
+ "type": [
3999
+ "string",
4000
+ "null"
4001
+ ]
4002
+ },
4003
+ "company": {
4004
+ "type": [
4005
+ "string",
4006
+ "null"
4007
+ ]
4008
+ },
4009
+ "address": {
4010
+ "type": [
4011
+ "string",
4012
+ "null"
4013
+ ]
4014
+ },
4015
+ "status": {
4016
+ "type": [
4017
+ "string",
4018
+ "null"
4019
+ ]
4020
+ },
4021
+ "customFields": {
4022
+ "anyOf": [
4023
+ {
4024
+ "type": "string"
4025
+ },
4026
+ {
4027
+ "type": "number"
4028
+ },
4029
+ {
4030
+ "type": "boolean"
4031
+ },
4032
+ {
4033
+ "type": "null"
4034
+ },
4035
+ {
4036
+ "type": "object",
4037
+ "additionalProperties": {}
4038
+ },
4039
+ {
4040
+ "type": "array",
4041
+ "items": {}
4042
+ },
4043
+ {
4044
+ "type": "null"
4045
+ }
4046
+ ]
4047
+ },
4048
+ "lastContactDate": {
4049
+ "type": [
4050
+ "string",
4051
+ "null"
4052
+ ]
4053
+ },
4054
+ "parentContactId": {
4055
+ "type": [
4056
+ "integer",
4057
+ "null"
4058
+ ],
4059
+ "minimum": -2147483648,
4060
+ "maximum": 2147483647
4061
+ },
4062
+ "relationship": {
4063
+ "type": [
4064
+ "string",
4065
+ "null"
4066
+ ]
4067
+ },
4068
+ "role": {
4069
+ "type": [
4070
+ "string",
4071
+ "null"
4072
+ ]
4073
+ },
4074
+ "firstName": {
4075
+ "type": [
4076
+ "string",
4077
+ "null"
4078
+ ]
4079
+ },
4080
+ "lastName": {
4081
+ "type": [
4082
+ "string",
4083
+ "null"
4084
+ ]
4085
+ },
4086
+ "serviceAddress": {
4087
+ "type": [
4088
+ "string",
4089
+ "null"
4090
+ ]
4091
+ },
4092
+ "billingAddress": {
4093
+ "type": [
4094
+ "string",
4095
+ "null"
4096
+ ]
4097
+ },
4098
+ "isCommercial": {
4099
+ "type": [
4100
+ "boolean",
4101
+ "null"
4102
+ ]
4103
+ },
4104
+ "pointOfContactName": {
4105
+ "type": [
4106
+ "string",
4107
+ "null"
4108
+ ]
4109
+ },
4110
+ "pointOfContactTitle": {
4111
+ "type": [
4112
+ "string",
4113
+ "null"
4114
+ ]
4115
+ },
4116
+ "pointOfContactPhone": {
4117
+ "type": [
4118
+ "string",
4119
+ "null"
4120
+ ]
4121
+ },
4122
+ "primaryEmail": {
4123
+ "type": [
4124
+ "string",
4125
+ "null"
4126
+ ]
4127
+ },
4128
+ "propertyAddress": {
4129
+ "type": [
4130
+ "string",
4131
+ "null"
4132
+ ]
4133
+ },
4134
+ "leadSource": {
4135
+ "type": [
4136
+ "string",
4137
+ "null"
4138
+ ]
4139
+ },
4140
+ "leadSourceReferralId": {
4141
+ "type": [
4142
+ "integer",
4143
+ "null"
4144
+ ],
4145
+ "minimum": -2147483648,
4146
+ "maximum": 2147483647
4147
+ },
4148
+ "leadSourceReferralUserId": {
4149
+ "type": [
4150
+ "string",
4151
+ "null"
4152
+ ]
4153
+ },
4154
+ "leadSourceOther": {
4155
+ "type": [
4156
+ "string",
4157
+ "null"
4158
+ ]
4159
+ },
4160
+ "googleDriveCustomerFolderId": {
4161
+ "type": [
4162
+ "string",
4163
+ "null"
4164
+ ]
4165
+ },
4166
+ "cachedLat": {
4167
+ "type": [
4168
+ "number",
4169
+ "null"
4170
+ ],
4171
+ "minimum": -140737488355328,
4172
+ "maximum": 140737488355327
4173
+ },
4174
+ "cachedLng": {
4175
+ "type": [
4176
+ "number",
4177
+ "null"
4178
+ ],
4179
+ "minimum": -140737488355328,
4180
+ "maximum": 140737488355327
4181
+ },
4182
+ "qboId": {
4183
+ "type": [
4184
+ "string",
4185
+ "null"
4186
+ ]
4187
+ },
4188
+ "qboSyncToken": {
4189
+ "type": [
4190
+ "string",
4191
+ "null"
4192
+ ]
4193
+ },
4194
+ "qboLastPushedAt": {
4195
+ "type": [
4196
+ "string",
4197
+ "null"
4198
+ ]
4199
+ },
4200
+ "qboLastPushedHash": {
4201
+ "type": [
4202
+ "string",
4203
+ "null"
4204
+ ]
4205
+ },
4206
+ "qboLastError": {
4207
+ "type": [
4208
+ "string",
4209
+ "null"
4210
+ ]
4211
+ },
4212
+ "deletedAt": {
4213
+ "type": [
4214
+ "string",
4215
+ "null"
4216
+ ]
4217
+ },
4218
+ "deletedBy": {
4219
+ "type": [
4220
+ "string",
4221
+ "null"
4222
+ ]
4223
+ }
4224
+ },
4225
+ "required": [
4226
+ "name"
4227
+ ]
4228
+ },
4229
+ {
4230
+ "type": "object",
4231
+ "properties": {
4232
+ "id": {
4233
+ "type": "number"
4234
+ }
4235
+ },
4236
+ "required": [
4237
+ "id"
4238
+ ]
4239
+ }
4240
+ ]
4241
+ }
4242
+ }
4243
+ }
4244
+ },
4245
+ "400": {
4246
+ "description": "Validation failure.",
4247
+ "content": {
4248
+ "application/json": {
4249
+ "schema": {
4250
+ "$ref": "#/components/schemas/ErrorEnvelope"
4251
+ }
4252
+ }
4253
+ }
4254
+ }
4255
+ }
4256
+ }
4257
+ },
4258
+ "/api/jobs/{jobId}/photos": {
4259
+ "post": {
4260
+ "summary": "Upload a project photo (raw-first; server watermarks).",
4261
+ "tags": [
4262
+ "photos"
4263
+ ],
4264
+ "parameters": [
4265
+ {
4266
+ "name": "X-Client-Version",
4267
+ "in": "header",
4268
+ "required": false,
4269
+ "schema": {
4270
+ "type": "string",
4271
+ "example": "1.0.0"
4272
+ },
4273
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
4274
+ },
4275
+ {
4276
+ "schema": {
4277
+ "type": "integer",
4278
+ "exclusiveMinimum": 0
4279
+ },
4280
+ "required": true,
4281
+ "name": "jobId",
4282
+ "in": "path"
4283
+ }
4284
+ ],
4285
+ "requestBody": {
4286
+ "content": {
4287
+ "multipart/form-data": {
4288
+ "schema": {
4289
+ "type": "object",
4290
+ "properties": {
4291
+ "file": {
4292
+ "type": "string",
4293
+ "format": "binary"
4294
+ }
4295
+ },
4296
+ "required": [
4297
+ "file"
4298
+ ]
4299
+ }
4300
+ }
4301
+ }
4302
+ },
4303
+ "responses": {
4304
+ "201": {
4305
+ "description": "Photo created.",
4306
+ "content": {
4307
+ "application/json": {
4308
+ "schema": {
4309
+ "$ref": "#/components/schemas/PhotoUploadResponse"
4310
+ }
4311
+ }
4312
+ }
4313
+ },
4314
+ "400": {
4315
+ "description": "Validation failure.",
4316
+ "content": {
4317
+ "application/json": {
4318
+ "schema": {
4319
+ "$ref": "#/components/schemas/ErrorEnvelope"
4320
+ }
4321
+ }
4322
+ }
4323
+ },
4324
+ "403": {
4325
+ "description": "Caller not assigned to this job.",
4326
+ "content": {
4327
+ "application/json": {
4328
+ "schema": {
4329
+ "$ref": "#/components/schemas/ErrorEnvelope"
4330
+ }
4331
+ }
4332
+ }
4333
+ }
4334
+ }
4335
+ }
4336
+ },
4337
+ "/api/jobs/{jobId}/scans": {
4338
+ "get": {
4339
+ "summary": "List every scan on a job. Augments each row with floor placement echo.",
4340
+ "tags": [
4341
+ "lidar"
4342
+ ],
4343
+ "parameters": [
4344
+ {
4345
+ "name": "X-Client-Version",
4346
+ "in": "header",
4347
+ "required": false,
4348
+ "schema": {
4349
+ "type": "string",
4350
+ "example": "1.0.0"
4351
+ },
4352
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
4353
+ },
4354
+ {
4355
+ "schema": {
4356
+ "type": "integer",
4357
+ "exclusiveMinimum": 0
4358
+ },
4359
+ "required": true,
4360
+ "name": "jobId",
4361
+ "in": "path"
4362
+ }
4363
+ ],
4364
+ "responses": {
4365
+ "200": {
4366
+ "description": "Sorted by COALESCE(captureTimestamp, createdAt) DESC.",
4367
+ "content": {
4368
+ "application/json": {
4369
+ "schema": {
4370
+ "type": "array",
4371
+ "items": {
4372
+ "type": "object",
4373
+ "additionalProperties": {}
4374
+ }
4375
+ }
4376
+ }
4377
+ }
4378
+ }
4379
+ }
4380
+ }
4381
+ }
4382
+ },
4383
+ "webhooks": {}
4384
+ }