@serviceai/api-spec 1.0.15 → 1.0.18

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 CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "ServiceAi API",
5
- "version": "1.0.15",
5
+ "version": "1.0.18",
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",
@@ -114,7 +114,7 @@
114
114
  "properties": {
115
115
  "apiVersion": {
116
116
  "type": "string",
117
- "example": "1.0.15"
117
+ "example": "1.0.18"
118
118
  },
119
119
  "minClientVersion": {
120
120
  "type": "string",
@@ -2538,6 +2538,42 @@
2538
2538
  "role",
2539
2539
  "createdAt"
2540
2540
  ]
2541
+ },
2542
+ "FloorPlanFrame": {
2543
+ "type": "object",
2544
+ "properties": {
2545
+ "pixelsPerMeter": {
2546
+ "type": "number"
2547
+ },
2548
+ "offsetX": {
2549
+ "type": "number"
2550
+ },
2551
+ "offsetY": {
2552
+ "type": "number"
2553
+ },
2554
+ "widthPx": {
2555
+ "type": "integer"
2556
+ },
2557
+ "heightPx": {
2558
+ "type": "integer"
2559
+ },
2560
+ "squareUpAngleRad": {
2561
+ "type": "number"
2562
+ },
2563
+ "mirrorX": {
2564
+ "type": "boolean"
2565
+ }
2566
+ },
2567
+ "required": [
2568
+ "pixelsPerMeter",
2569
+ "offsetX",
2570
+ "offsetY",
2571
+ "widthPx",
2572
+ "heightPx",
2573
+ "squareUpAngleRad",
2574
+ "mirrorX"
2575
+ ],
2576
+ "description": "Persisted floor-plan PNG render-frame. `mirrorX` is derived from `scans.client_mirror_applied` (negate when false). Null sentinel: ANY required column NULL → `null` is emitted instead of a partial frame."
2541
2577
  }
2542
2578
  },
2543
2579
  "parameters": {}
@@ -4379,6 +4415,287 @@
4379
4415
  }
4380
4416
  }
4381
4417
  }
4418
+ },
4419
+ "/api/jobs/{jobId}/scans/{scanId}/rooms": {
4420
+ "get": {
4421
+ "summary": "List scan_rooms for a scan. Each room carries the shared `floorPlanFrame`.",
4422
+ "tags": [
4423
+ "lidar"
4424
+ ],
4425
+ "parameters": [
4426
+ {
4427
+ "name": "X-Client-Version",
4428
+ "in": "header",
4429
+ "required": false,
4430
+ "schema": {
4431
+ "type": "string",
4432
+ "example": "1.0.0"
4433
+ },
4434
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
4435
+ },
4436
+ {
4437
+ "schema": {
4438
+ "type": "integer",
4439
+ "exclusiveMinimum": 0
4440
+ },
4441
+ "required": true,
4442
+ "name": "jobId",
4443
+ "in": "path"
4444
+ },
4445
+ {
4446
+ "schema": {
4447
+ "type": "integer",
4448
+ "exclusiveMinimum": 0
4449
+ },
4450
+ "required": true,
4451
+ "name": "scanId",
4452
+ "in": "path"
4453
+ }
4454
+ ],
4455
+ "responses": {
4456
+ "200": {
4457
+ "description": "Rooms array; each room carries `floorPlanFrame` (identical across rooms — frame is per-scan).",
4458
+ "content": {
4459
+ "application/json": {
4460
+ "schema": {
4461
+ "type": "array",
4462
+ "items": {
4463
+ "allOf": [
4464
+ {
4465
+ "type": "object",
4466
+ "additionalProperties": {}
4467
+ },
4468
+ {
4469
+ "type": "object",
4470
+ "properties": {
4471
+ "floorPlanFrame": {
4472
+ "type": [
4473
+ "object",
4474
+ "null"
4475
+ ],
4476
+ "properties": {
4477
+ "pixelsPerMeter": {
4478
+ "type": "number"
4479
+ },
4480
+ "offsetX": {
4481
+ "type": "number"
4482
+ },
4483
+ "offsetY": {
4484
+ "type": "number"
4485
+ },
4486
+ "widthPx": {
4487
+ "type": "integer"
4488
+ },
4489
+ "heightPx": {
4490
+ "type": "integer"
4491
+ },
4492
+ "squareUpAngleRad": {
4493
+ "type": "number"
4494
+ },
4495
+ "mirrorX": {
4496
+ "type": "boolean"
4497
+ }
4498
+ },
4499
+ "required": [
4500
+ "pixelsPerMeter",
4501
+ "offsetX",
4502
+ "offsetY",
4503
+ "widthPx",
4504
+ "heightPx",
4505
+ "squareUpAngleRad",
4506
+ "mirrorX"
4507
+ ]
4508
+ }
4509
+ },
4510
+ "required": [
4511
+ "floorPlanFrame"
4512
+ ]
4513
+ }
4514
+ ]
4515
+ }
4516
+ }
4517
+ }
4518
+ }
4519
+ },
4520
+ "403": {
4521
+ "description": "Caller not assigned to this job.",
4522
+ "content": {
4523
+ "application/json": {
4524
+ "schema": {
4525
+ "$ref": "#/components/schemas/ErrorEnvelope"
4526
+ }
4527
+ }
4528
+ }
4529
+ },
4530
+ "404": {
4531
+ "description": "Scan not found.",
4532
+ "content": {
4533
+ "application/json": {
4534
+ "schema": {
4535
+ "$ref": "#/components/schemas/ErrorEnvelope"
4536
+ }
4537
+ }
4538
+ }
4539
+ }
4540
+ }
4541
+ }
4542
+ },
4543
+ "/api/floors/{floorId}/rooms": {
4544
+ "get": {
4545
+ "summary": "Bundled scans + rooms for a floor. Each scan carries `floorPlanFrame`.",
4546
+ "tags": [
4547
+ "lidar"
4548
+ ],
4549
+ "parameters": [
4550
+ {
4551
+ "name": "X-Client-Version",
4552
+ "in": "header",
4553
+ "required": false,
4554
+ "schema": {
4555
+ "type": "string",
4556
+ "example": "1.0.0"
4557
+ },
4558
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
4559
+ },
4560
+ {
4561
+ "schema": {
4562
+ "type": "integer",
4563
+ "exclusiveMinimum": 0
4564
+ },
4565
+ "required": true,
4566
+ "name": "floorId",
4567
+ "in": "path"
4568
+ }
4569
+ ],
4570
+ "responses": {
4571
+ "200": {
4572
+ "description": "Scans, rooms, and implicit session edges.",
4573
+ "content": {
4574
+ "application/json": {
4575
+ "schema": {
4576
+ "type": "object",
4577
+ "properties": {
4578
+ "scans": {
4579
+ "type": "array",
4580
+ "items": {
4581
+ "allOf": [
4582
+ {
4583
+ "type": "object",
4584
+ "additionalProperties": {}
4585
+ },
4586
+ {
4587
+ "type": "object",
4588
+ "properties": {
4589
+ "floorPlanFrame": {
4590
+ "type": [
4591
+ "object",
4592
+ "null"
4593
+ ],
4594
+ "properties": {
4595
+ "pixelsPerMeter": {
4596
+ "type": "number"
4597
+ },
4598
+ "offsetX": {
4599
+ "type": "number"
4600
+ },
4601
+ "offsetY": {
4602
+ "type": "number"
4603
+ },
4604
+ "widthPx": {
4605
+ "type": "integer"
4606
+ },
4607
+ "heightPx": {
4608
+ "type": "integer"
4609
+ },
4610
+ "squareUpAngleRad": {
4611
+ "type": "number"
4612
+ },
4613
+ "mirrorX": {
4614
+ "type": "boolean"
4615
+ }
4616
+ },
4617
+ "required": [
4618
+ "pixelsPerMeter",
4619
+ "offsetX",
4620
+ "offsetY",
4621
+ "widthPx",
4622
+ "heightPx",
4623
+ "squareUpAngleRad",
4624
+ "mirrorX"
4625
+ ]
4626
+ }
4627
+ },
4628
+ "required": [
4629
+ "floorPlanFrame"
4630
+ ]
4631
+ }
4632
+ ]
4633
+ }
4634
+ },
4635
+ "rooms": {
4636
+ "type": "array",
4637
+ "items": {
4638
+ "type": "object",
4639
+ "additionalProperties": {}
4640
+ }
4641
+ },
4642
+ "sessionConnections": {
4643
+ "type": "array",
4644
+ "items": {
4645
+ "type": "object",
4646
+ "properties": {
4647
+ "sessionId": {
4648
+ "type": "string"
4649
+ },
4650
+ "scanId": {
4651
+ "type": "integer"
4652
+ },
4653
+ "roomIds": {
4654
+ "type": "array",
4655
+ "items": {
4656
+ "type": "integer"
4657
+ }
4658
+ }
4659
+ },
4660
+ "required": [
4661
+ "sessionId",
4662
+ "scanId",
4663
+ "roomIds"
4664
+ ]
4665
+ }
4666
+ }
4667
+ },
4668
+ "required": [
4669
+ "scans",
4670
+ "rooms",
4671
+ "sessionConnections"
4672
+ ]
4673
+ }
4674
+ }
4675
+ }
4676
+ },
4677
+ "403": {
4678
+ "description": "Caller cannot reach this floor.",
4679
+ "content": {
4680
+ "application/json": {
4681
+ "schema": {
4682
+ "$ref": "#/components/schemas/ErrorEnvelope"
4683
+ }
4684
+ }
4685
+ }
4686
+ },
4687
+ "404": {
4688
+ "description": "Floor not found.",
4689
+ "content": {
4690
+ "application/json": {
4691
+ "schema": {
4692
+ "$ref": "#/components/schemas/ErrorEnvelope"
4693
+ }
4694
+ }
4695
+ }
4696
+ }
4697
+ }
4698
+ }
4382
4699
  }
4383
4700
  },
4384
4701
  "webhooks": {}
package/openapi.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  openapi: 3.1.0
2
2
  info:
3
3
  title: ServiceAi API
4
- version: 1.0.15
4
+ version: 1.0.18
5
5
  description: >-
6
6
  Source-of-truth API contract for ServiceAi. Generated from `shared/schema.ts` Zod schemas by
7
7
  `scripts/build-api-spec.ts`. Versioning policy: see `docs/api-versioning.md`. The hand-written docs/ios-contract.md
@@ -78,7 +78,7 @@ components:
78
78
  properties:
79
79
  apiVersion:
80
80
  type: string
81
- example: 1.0.15
81
+ example: 1.0.18
82
82
  minClientVersion:
83
83
  type: string
84
84
  example: 1.0.0
@@ -1639,6 +1639,34 @@ components:
1639
1639
  - id
1640
1640
  - role
1641
1641
  - createdAt
1642
+ FloorPlanFrame:
1643
+ type: object
1644
+ properties:
1645
+ pixelsPerMeter:
1646
+ type: number
1647
+ offsetX:
1648
+ type: number
1649
+ offsetY:
1650
+ type: number
1651
+ widthPx:
1652
+ type: integer
1653
+ heightPx:
1654
+ type: integer
1655
+ squareUpAngleRad:
1656
+ type: number
1657
+ mirrorX:
1658
+ type: boolean
1659
+ required:
1660
+ - pixelsPerMeter
1661
+ - offsetX
1662
+ - offsetY
1663
+ - widthPx
1664
+ - heightPx
1665
+ - squareUpAngleRad
1666
+ - mirrorX
1667
+ description: >-
1668
+ Persisted floor-plan PNG render-frame. `mirrorX` is derived from `scans.client_mirror_applied` (negate when
1669
+ false). Null sentinel: ANY required column NULL → `null` is emitted instead of a partial frame.
1642
1670
  parameters: {}
1643
1671
  paths:
1644
1672
  /api/version:
@@ -2733,4 +2761,173 @@ paths:
2733
2761
  items:
2734
2762
  type: object
2735
2763
  additionalProperties: {}
2764
+ /api/jobs/{jobId}/scans/{scanId}/rooms:
2765
+ get:
2766
+ summary: List scan_rooms for a scan. Each room carries the shared `floorPlanFrame`.
2767
+ tags:
2768
+ - lidar
2769
+ parameters:
2770
+ - *ref_1
2771
+ - schema:
2772
+ type: integer
2773
+ exclusiveMinimum: 0
2774
+ required: true
2775
+ name: jobId
2776
+ in: path
2777
+ - schema:
2778
+ type: integer
2779
+ exclusiveMinimum: 0
2780
+ required: true
2781
+ name: scanId
2782
+ in: path
2783
+ responses:
2784
+ '200':
2785
+ description: Rooms array; each room carries `floorPlanFrame` (identical across rooms — frame is per-scan).
2786
+ content:
2787
+ application/json:
2788
+ schema:
2789
+ type: array
2790
+ items:
2791
+ allOf:
2792
+ - type: object
2793
+ additionalProperties: {}
2794
+ - type: object
2795
+ properties:
2796
+ floorPlanFrame:
2797
+ type:
2798
+ - object
2799
+ - 'null'
2800
+ properties:
2801
+ pixelsPerMeter:
2802
+ type: number
2803
+ offsetX:
2804
+ type: number
2805
+ offsetY:
2806
+ type: number
2807
+ widthPx:
2808
+ type: integer
2809
+ heightPx:
2810
+ type: integer
2811
+ squareUpAngleRad:
2812
+ type: number
2813
+ mirrorX:
2814
+ type: boolean
2815
+ required:
2816
+ - pixelsPerMeter
2817
+ - offsetX
2818
+ - offsetY
2819
+ - widthPx
2820
+ - heightPx
2821
+ - squareUpAngleRad
2822
+ - mirrorX
2823
+ required:
2824
+ - floorPlanFrame
2825
+ '403':
2826
+ description: Caller not assigned to this job.
2827
+ content:
2828
+ application/json:
2829
+ schema:
2830
+ $ref: '#/components/schemas/ErrorEnvelope'
2831
+ '404':
2832
+ description: Scan not found.
2833
+ content:
2834
+ application/json:
2835
+ schema:
2836
+ $ref: '#/components/schemas/ErrorEnvelope'
2837
+ /api/floors/{floorId}/rooms:
2838
+ get:
2839
+ summary: Bundled scans + rooms for a floor. Each scan carries `floorPlanFrame`.
2840
+ tags:
2841
+ - lidar
2842
+ parameters:
2843
+ - *ref_1
2844
+ - schema:
2845
+ type: integer
2846
+ exclusiveMinimum: 0
2847
+ required: true
2848
+ name: floorId
2849
+ in: path
2850
+ responses:
2851
+ '200':
2852
+ description: Scans, rooms, and implicit session edges.
2853
+ content:
2854
+ application/json:
2855
+ schema:
2856
+ type: object
2857
+ properties:
2858
+ scans:
2859
+ type: array
2860
+ items:
2861
+ allOf:
2862
+ - type: object
2863
+ additionalProperties: {}
2864
+ - type: object
2865
+ properties:
2866
+ floorPlanFrame:
2867
+ type:
2868
+ - object
2869
+ - 'null'
2870
+ properties:
2871
+ pixelsPerMeter:
2872
+ type: number
2873
+ offsetX:
2874
+ type: number
2875
+ offsetY:
2876
+ type: number
2877
+ widthPx:
2878
+ type: integer
2879
+ heightPx:
2880
+ type: integer
2881
+ squareUpAngleRad:
2882
+ type: number
2883
+ mirrorX:
2884
+ type: boolean
2885
+ required:
2886
+ - pixelsPerMeter
2887
+ - offsetX
2888
+ - offsetY
2889
+ - widthPx
2890
+ - heightPx
2891
+ - squareUpAngleRad
2892
+ - mirrorX
2893
+ required:
2894
+ - floorPlanFrame
2895
+ rooms:
2896
+ type: array
2897
+ items:
2898
+ type: object
2899
+ additionalProperties: {}
2900
+ sessionConnections:
2901
+ type: array
2902
+ items:
2903
+ type: object
2904
+ properties:
2905
+ sessionId:
2906
+ type: string
2907
+ scanId:
2908
+ type: integer
2909
+ roomIds:
2910
+ type: array
2911
+ items:
2912
+ type: integer
2913
+ required:
2914
+ - sessionId
2915
+ - scanId
2916
+ - roomIds
2917
+ required:
2918
+ - scans
2919
+ - rooms
2920
+ - sessionConnections
2921
+ '403':
2922
+ description: Caller cannot reach this floor.
2923
+ content:
2924
+ application/json:
2925
+ schema:
2926
+ $ref: '#/components/schemas/ErrorEnvelope'
2927
+ '404':
2928
+ description: Floor not found.
2929
+ content:
2930
+ application/json:
2931
+ schema:
2932
+ $ref: '#/components/schemas/ErrorEnvelope'
2736
2933
  webhooks: {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serviceai/api-spec",
3
- "version": "1.0.15",
3
+ "version": "1.0.18",
4
4
  "description": "ServiceAi OpenAPI 3.1 contract + generated TS types. Auto-published from the desktop repo.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/types.d.ts CHANGED
@@ -1027,6 +1027,159 @@ export interface paths {
1027
1027
  patch?: never;
1028
1028
  trace?: never;
1029
1029
  };
1030
+ "/api/jobs/{jobId}/scans/{scanId}/rooms": {
1031
+ parameters: {
1032
+ query?: never;
1033
+ header?: never;
1034
+ path?: never;
1035
+ cookie?: never;
1036
+ };
1037
+ /** List scan_rooms for a scan. Each room carries the shared `floorPlanFrame`. */
1038
+ get: {
1039
+ parameters: {
1040
+ query?: never;
1041
+ header?: {
1042
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
1043
+ "X-Client-Version"?: string;
1044
+ };
1045
+ path: {
1046
+ jobId: number;
1047
+ scanId: number;
1048
+ };
1049
+ cookie?: never;
1050
+ };
1051
+ requestBody?: never;
1052
+ responses: {
1053
+ /** @description Rooms array; each room carries `floorPlanFrame` (identical across rooms — frame is per-scan). */
1054
+ 200: {
1055
+ headers: {
1056
+ [name: string]: unknown;
1057
+ };
1058
+ content: {
1059
+ "application/json": ({
1060
+ [key: string]: unknown;
1061
+ } & {
1062
+ floorPlanFrame: {
1063
+ pixelsPerMeter: number;
1064
+ offsetX: number;
1065
+ offsetY: number;
1066
+ widthPx: number;
1067
+ heightPx: number;
1068
+ squareUpAngleRad: number;
1069
+ mirrorX: boolean;
1070
+ } | null;
1071
+ })[];
1072
+ };
1073
+ };
1074
+ /** @description Caller not assigned to this job. */
1075
+ 403: {
1076
+ headers: {
1077
+ [name: string]: unknown;
1078
+ };
1079
+ content: {
1080
+ "application/json": components["schemas"]["ErrorEnvelope"];
1081
+ };
1082
+ };
1083
+ /** @description Scan not found. */
1084
+ 404: {
1085
+ headers: {
1086
+ [name: string]: unknown;
1087
+ };
1088
+ content: {
1089
+ "application/json": components["schemas"]["ErrorEnvelope"];
1090
+ };
1091
+ };
1092
+ };
1093
+ };
1094
+ put?: never;
1095
+ post?: never;
1096
+ delete?: never;
1097
+ options?: never;
1098
+ head?: never;
1099
+ patch?: never;
1100
+ trace?: never;
1101
+ };
1102
+ "/api/floors/{floorId}/rooms": {
1103
+ parameters: {
1104
+ query?: never;
1105
+ header?: never;
1106
+ path?: never;
1107
+ cookie?: never;
1108
+ };
1109
+ /** Bundled scans + rooms for a floor. Each scan carries `floorPlanFrame`. */
1110
+ get: {
1111
+ parameters: {
1112
+ query?: never;
1113
+ header?: {
1114
+ /** @description Semver of the calling app build. Drives the 426 / soft-upgrade handshake. */
1115
+ "X-Client-Version"?: string;
1116
+ };
1117
+ path: {
1118
+ floorId: number;
1119
+ };
1120
+ cookie?: never;
1121
+ };
1122
+ requestBody?: never;
1123
+ responses: {
1124
+ /** @description Scans, rooms, and implicit session edges. */
1125
+ 200: {
1126
+ headers: {
1127
+ [name: string]: unknown;
1128
+ };
1129
+ content: {
1130
+ "application/json": {
1131
+ scans: ({
1132
+ [key: string]: unknown;
1133
+ } & {
1134
+ floorPlanFrame: {
1135
+ pixelsPerMeter: number;
1136
+ offsetX: number;
1137
+ offsetY: number;
1138
+ widthPx: number;
1139
+ heightPx: number;
1140
+ squareUpAngleRad: number;
1141
+ mirrorX: boolean;
1142
+ } | null;
1143
+ })[];
1144
+ rooms: {
1145
+ [key: string]: unknown;
1146
+ }[];
1147
+ sessionConnections: {
1148
+ sessionId: string;
1149
+ scanId: number;
1150
+ roomIds: number[];
1151
+ }[];
1152
+ };
1153
+ };
1154
+ };
1155
+ /** @description Caller cannot reach this floor. */
1156
+ 403: {
1157
+ headers: {
1158
+ [name: string]: unknown;
1159
+ };
1160
+ content: {
1161
+ "application/json": components["schemas"]["ErrorEnvelope"];
1162
+ };
1163
+ };
1164
+ /** @description Floor not found. */
1165
+ 404: {
1166
+ headers: {
1167
+ [name: string]: unknown;
1168
+ };
1169
+ content: {
1170
+ "application/json": components["schemas"]["ErrorEnvelope"];
1171
+ };
1172
+ };
1173
+ };
1174
+ };
1175
+ put?: never;
1176
+ post?: never;
1177
+ delete?: never;
1178
+ options?: never;
1179
+ head?: never;
1180
+ patch?: never;
1181
+ trace?: never;
1182
+ };
1030
1183
  }
1031
1184
  export type webhooks = Record<string, never>;
1032
1185
  export interface components {
@@ -1051,7 +1204,7 @@ export interface components {
1051
1204
  };
1052
1205
  /** @description Cross-platform version handshake manifest. See docs/api-versioning.md. */
1053
1206
  VersionManifest: {
1054
- /** @example 1.0.15 */
1207
+ /** @example 1.0.18 */
1055
1208
  apiVersion: string;
1056
1209
  /** @example 1.0.0 */
1057
1210
  minClientVersion: string;
@@ -1494,6 +1647,16 @@ export interface components {
1494
1647
  arguments?: unknown;
1495
1648
  }[];
1496
1649
  };
1650
+ /** @description Persisted floor-plan PNG render-frame. `mirrorX` is derived from `scans.client_mirror_applied` (negate when false). Null sentinel: ANY required column NULL → `null` is emitted instead of a partial frame. */
1651
+ FloorPlanFrame: {
1652
+ pixelsPerMeter: number;
1653
+ offsetX: number;
1654
+ offsetY: number;
1655
+ widthPx: number;
1656
+ heightPx: number;
1657
+ squareUpAngleRad: number;
1658
+ mirrorX: boolean;
1659
+ };
1497
1660
  };
1498
1661
  responses: never;
1499
1662
  parameters: never;