repzo 1.0.291 → 1.0.293

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/changelog.md +28 -0
  2. package/lib/index.d.ts +244 -2
  3. package/lib/index.js +541 -0
  4. package/lib/types/index.d.ts +4533 -1
  5. package/package.json +1 -1
  6. package/src/index.ts +1270 -0
  7. package/src/oas/activity-ai-object-detection-session-frame.yaml +600 -0
  8. package/src/oas/ai-object-detection-assigned-missions.yaml +283 -0
  9. package/src/oas/ai-object-detection-assignment-rule.yaml +357 -0
  10. package/src/oas/ai-object-detection-category.yaml +338 -0
  11. package/src/oas/ai-object-detection-dataset.yaml +342 -0
  12. package/src/oas/ai-object-detection-detection-settings.yaml +410 -0
  13. package/src/oas/ai-object-detection-inference.yaml +818 -0
  14. package/src/oas/ai-object-detection-label-group.yaml +265 -0
  15. package/src/oas/ai-object-detection-label-report.yaml +366 -0
  16. package/src/oas/ai-object-detection-label.yaml +395 -0
  17. package/src/oas/ai-object-detection-metric-result.yaml +734 -0
  18. package/src/oas/ai-object-detection-metric.yaml +559 -0
  19. package/src/oas/ai-object-detection-mission-results.yaml +370 -0
  20. package/src/oas/ai-object-detection-mission-set.yaml +250 -0
  21. package/src/oas/ai-object-detection-mission.yaml +349 -0
  22. package/src/oas/ai-object-detection-model-version-epoch.yaml +214 -0
  23. package/src/oas/ai-object-detection-model-version-train-agent.yaml +88 -0
  24. package/src/oas/ai-object-detection-model-version.yaml +567 -0
  25. package/src/oas/ai-object-detection-model.yaml +369 -0
  26. package/src/oas/ai-object-detection-segment.yaml +301 -0
  27. package/src/oas/ai-object-detection-session-analysis.yaml +1306 -0
  28. package/src/oas/ai-object-detection-session-election.yaml +193 -0
  29. package/src/oas/ai-object-detection-session-insight.yaml +418 -0
  30. package/src/oas/ai-object-detection-session.yaml +840 -0
  31. package/src/oas/ai-object-detection-settings.yaml +249 -0
  32. package/src/oas/ai-object-detection-task.yaml +1007 -0
  33. package/src/oas/object-detection-analytics-report.yaml +674 -0
  34. package/src/types/index.ts +5159 -1
@@ -0,0 +1,840 @@
1
+ openapi: 3.0.3
2
+ info:
3
+ title: Repzo API - AI Object Detection Session
4
+ version: 1.0.0
5
+ description: |
6
+ **Object Detection Sessions** are the parent record for a single
7
+ AR shelf scan captured by the mobile app. A session groups N
8
+ `ai-object-detection-task` rows (one per submitted frame) and adds
9
+ aggregate 3D scene state, so the existing task-based training and
10
+ review pipeline is reused unchanged — a session is a thin parent,
11
+ the frame is still the atomic annotatable unit.
12
+
13
+ **Who calls it.** Reps create sessions implicitly when the device
14
+ posts its first frame to `/activity-ai-object-detection-session-frame`
15
+ (which upserts the session). Admins and reps list and inspect
16
+ sessions from the dashboard. Detection/placement is triggered later,
17
+ per task, via `ai-object-detection-inference`, and cross-frame fusion
18
+ runs through `ai-object-detection-session-analysis` (its own
19
+ versioned collection — see below).
20
+
21
+ **Multi-tenancy & lifecycle.** Records are scoped by
22
+ `company_namespace[]` (server-injected from the caller's session).
23
+ Soft-delete via `disabled: true`. On `DELETE`, child tasks are left
24
+ intact by default (they remain usable training data); pass
25
+ `?cascade=true` to also soft-delete the session's child tasks.
26
+
27
+ **Status FSM.** `open` (device still streaming / new frames arrived)
28
+ → `infer_in_progress` (an analysis run is in flight) → `inferred`
29
+ (last run succeeded) or `failed` (last run failed). The transitions
30
+ are written by `ai-object-detection-session-analysis` runs; a new
31
+ frame arriving on a settled (`inferred`/`failed`) session reopens it
32
+ to `open` (there is newer data than the last analysis). Per-run
33
+ status/history still lives on the analysis documents. `uploaded` is
34
+ written by the device's upload-complete marker (see the frame intake
35
+ service) — it also fires the category auto-analysis;
36
+ `rearbitrating` remains a reserved/legacy value with no writer.
37
+
38
+ **Key relationships.** References `clients` (the `client` /
39
+ store being scanned), `ai-object-detection-category` (the optional
40
+ `category` picked on the device — drives auto-analysis),
41
+ `ai.objectDetectionModelVersions` (via
42
+ `inference_runs.model_version`), `sv.aiObjectDetectionLabels` (via
43
+ `objects.label_id`), and `media.mediaStorages` (point cloud).
44
+ Child frames link back via the task's `session` field.
45
+
46
+ **Denormalized aggregates.** `frames_total`, `frames_accepted`,
47
+ `tasks_count` are incremented by the frame intake; `detections_count`
48
+ and `objects_count` are refreshed on every successful analysis run.
49
+ `scene`, `objects[]`, and `inference_runs[]` are **legacy** fields
50
+ from the removed in-session build_scene flow — they are retained for
51
+ older data but no longer written; cross-frame fused output now lives
52
+ in `ai-object-detection-session-analysis`.
53
+
54
+ **Population.** `?populatedKeys[]=` supports `client`, `category`,
55
+ `inference_runs.model_version`, `objects.label_id`, and
56
+ `scene.point_cloud_media`. `client`/`category` keep their ids and the
57
+ populated documents are returned under `client_populated` /
58
+ `category_populated`; the nested keys are populated in place (the
59
+ referenced field is replaced by the populated document).
60
+ servers:
61
+ - url: https://sv.api.repzo.me
62
+ security:
63
+ - ApiKeyAuth: []
64
+ - JwtAuth: []
65
+ paths:
66
+ /ai-object-detection-session:
67
+ get:
68
+ summary: Find object detection sessions
69
+ operationId: findAiObjectDetectionSessions
70
+ parameters:
71
+ - in: query
72
+ name: _id
73
+ description: |
74
+ Filter by session `_id`. Pass once for a single match, or as
75
+ `?_id[]=...&_id[]=...` for multiple.
76
+ schema:
77
+ oneOf:
78
+ - type: string
79
+ - type: array
80
+ items: { type: string }
81
+ - in: query
82
+ name: session_id
83
+ description: Filter by the device-generated session id (4-char).
84
+ schema:
85
+ oneOf:
86
+ - type: string
87
+ - type: array
88
+ items: { type: string }
89
+ - in: query
90
+ name: status
91
+ description: Filter by lifecycle status.
92
+ schema:
93
+ oneOf:
94
+ - type: string
95
+ enum:
96
+ [
97
+ open,
98
+ uploaded,
99
+ infer_in_progress,
100
+ inferred,
101
+ rearbitrating,
102
+ failed,
103
+ ]
104
+ - type: array
105
+ items:
106
+ type: string
107
+ enum:
108
+ [
109
+ open,
110
+ uploaded,
111
+ infer_in_progress,
112
+ inferred,
113
+ rearbitrating,
114
+ failed,
115
+ ]
116
+ - in: query
117
+ name: client
118
+ description: Filter by the scanned store/client (`clients` `_id`).
119
+ schema:
120
+ oneOf:
121
+ - type: string
122
+ - type: array
123
+ items: { type: string }
124
+ - in: query
125
+ name: category
126
+ description: Filter by the detection category (`ai-object-detection-category` `_id`).
127
+ schema:
128
+ oneOf:
129
+ - type: string
130
+ - type: array
131
+ items: { type: string }
132
+ - in: query
133
+ name: rep
134
+ description: Filter by the rep stamped on the session's frames (`representatives` `_id`).
135
+ schema:
136
+ oneOf:
137
+ - type: string
138
+ - type: array
139
+ items: { type: string }
140
+ - in: query
141
+ name: visit_id
142
+ description: Filter by the device visit id the scan happened in.
143
+ schema:
144
+ oneOf:
145
+ - type: string
146
+ - type: array
147
+ items: { type: string }
148
+ - in: query
149
+ name: route
150
+ description: Filter by the visit's route (`sv.routes` `_id`).
151
+ schema:
152
+ oneOf:
153
+ - type: string
154
+ - type: array
155
+ items: { type: string }
156
+ - in: query
157
+ name: creator._id
158
+ description: Filter by the id of the rep/admin who created the session.
159
+ schema:
160
+ oneOf:
161
+ - type: string
162
+ - type: array
163
+ items: { type: string }
164
+ - in: query
165
+ name: inference_runs.model_version
166
+ description: Filter sessions that ran a given model version.
167
+ schema:
168
+ oneOf:
169
+ - type: string
170
+ - type: array
171
+ items: { type: string }
172
+ - in: query
173
+ name: from_createdAt
174
+ description: Return sessions created at/after this Unix timestamp (ms).
175
+ schema: { type: number }
176
+ - in: query
177
+ name: to_createdAt
178
+ description: Return sessions created at/before this Unix timestamp (ms).
179
+ schema: { type: number }
180
+ - in: query
181
+ name: from_updatedAt
182
+ description: Cursor — sessions with `updatedAt` greater than this Unix timestamp (ms).
183
+ schema: { type: number }
184
+ - in: query
185
+ name: to_updatedAt
186
+ description: Cursor — sessions with `updatedAt` less than this Unix timestamp (ms).
187
+ schema: { type: number }
188
+ - in: query
189
+ name: disabled
190
+ description: Include disabled (soft-deleted) sessions. Defaults to `false`.
191
+ schema: { type: boolean, default: false }
192
+ - in: query
193
+ name: sort
194
+ description: Field to sort by. Defaults to `_id`.
195
+ schema: { type: string, default: _id }
196
+ - in: query
197
+ name: sortPageOrder
198
+ description: Sort direction. Defaults to descending.
199
+ schema: { type: string, enum: [asc, dsc], default: dsc }
200
+ - in: query
201
+ name: populatedKeys
202
+ description: |
203
+ Embed referenced documents. Supported: `client`, `category`,
204
+ `inference_runs.model_version`, `objects.label_id`,
205
+ `scene.point_cloud_media`.
206
+ schema:
207
+ type: array
208
+ items:
209
+ type: string
210
+ enum:
211
+ - client
212
+ - category
213
+ - inference_runs.model_version
214
+ - objects.label_id
215
+ - scene.point_cloud_media
216
+ - in: query
217
+ name: per_page
218
+ description: Page size. Defaults to the server's configured pagination limit.
219
+ schema: { type: integer, minimum: 1, maximum: 500 }
220
+ example: 50
221
+ - in: query
222
+ name: page
223
+ description: 1-indexed page number.
224
+ schema: { type: integer, minimum: 1 }
225
+ example: 1
226
+ responses:
227
+ "200":
228
+ description: Paginated list of object detection sessions.
229
+ content:
230
+ application/json:
231
+ schema:
232
+ $ref: "#/components/schemas/SessionFindResult"
233
+ post:
234
+ summary: Create an object detection session
235
+ description: |
236
+ Opens a session. Usually created implicitly on the first frame
237
+ POST to `/activity-ai-object-detection-session-frame`; this endpoint is
238
+ for explicit/manual creation. `company_namespace` and `creator`
239
+ are server-injected.
240
+ operationId: createAiObjectDetectionSession
241
+ requestBody:
242
+ required: true
243
+ content:
244
+ application/json:
245
+ schema:
246
+ $ref: "#/components/schemas/SessionCreateBody"
247
+ responses:
248
+ "201":
249
+ description: The newly-created session document.
250
+ content:
251
+ application/json:
252
+ schema:
253
+ $ref: "#/components/schemas/SessionSchema"
254
+ patch:
255
+ summary: Bulk-update sessions
256
+ description: |
257
+ Bulk update via the `patch-action` writeQuery shape (e.g. bulk
258
+ status change or bulk disable). The rows to update are selected by
259
+ the SAME query filters as `GET /ai-object-detection-session`
260
+ (`_id`, `session_id`, `status`, `client`, `category`, `rep`,
261
+ `visit_id`, `route`, `creator._id`, `inference_runs.model_version`,
262
+ `from_/to_createdAt`, `from_/to_updatedAt`, `disabled`). Returns
263
+ `{ nFound, nModified }`.
264
+ operationId: patchAiObjectDetectionSession
265
+ parameters:
266
+ - in: query
267
+ name: _id
268
+ description: Restrict the bulk update to these session ids.
269
+ schema:
270
+ oneOf:
271
+ - type: string
272
+ - type: array
273
+ items: { type: string }
274
+ - in: query
275
+ name: session_id
276
+ schema:
277
+ oneOf:
278
+ - type: string
279
+ - type: array
280
+ items: { type: string }
281
+ - in: query
282
+ name: status
283
+ schema:
284
+ oneOf:
285
+ - type: string
286
+ - type: array
287
+ items: { type: string }
288
+ - in: query
289
+ name: client
290
+ schema:
291
+ oneOf:
292
+ - type: string
293
+ - type: array
294
+ items: { type: string }
295
+ - in: query
296
+ name: category
297
+ schema:
298
+ oneOf:
299
+ - type: string
300
+ - type: array
301
+ items: { type: string }
302
+ - in: query
303
+ name: rep
304
+ schema:
305
+ oneOf:
306
+ - type: string
307
+ - type: array
308
+ items: { type: string }
309
+ - in: query
310
+ name: visit_id
311
+ schema:
312
+ oneOf:
313
+ - type: string
314
+ - type: array
315
+ items: { type: string }
316
+ - in: query
317
+ name: route
318
+ schema:
319
+ oneOf:
320
+ - type: string
321
+ - type: array
322
+ items: { type: string }
323
+ - in: query
324
+ name: creator._id
325
+ schema:
326
+ oneOf:
327
+ - type: string
328
+ - type: array
329
+ items: { type: string }
330
+ - in: query
331
+ name: inference_runs.model_version
332
+ schema:
333
+ oneOf:
334
+ - type: string
335
+ - type: array
336
+ items: { type: string }
337
+ - in: query
338
+ name: from_createdAt
339
+ schema: { type: number }
340
+ - in: query
341
+ name: to_createdAt
342
+ schema: { type: number }
343
+ - in: query
344
+ name: from_updatedAt
345
+ schema: { type: number }
346
+ - in: query
347
+ name: to_updatedAt
348
+ schema: { type: number }
349
+ - in: query
350
+ name: disabled
351
+ schema: { type: boolean }
352
+ requestBody:
353
+ required: true
354
+ content:
355
+ application/json:
356
+ schema:
357
+ $ref: "#/components/schemas/PatchActionBody"
358
+ responses:
359
+ "200":
360
+ description: Bulk-update result.
361
+ content:
362
+ application/json:
363
+ schema:
364
+ $ref: "#/components/schemas/PatchActionResult"
365
+ /ai-object-detection-session/{id}:
366
+ get:
367
+ summary: Get a session by id
368
+ operationId: getAiObjectDetectionSession
369
+ parameters:
370
+ - in: path
371
+ name: id
372
+ required: true
373
+ schema: { type: string }
374
+ - in: query
375
+ name: populatedKeys
376
+ description: Same population keys as the list endpoint.
377
+ schema:
378
+ type: array
379
+ items:
380
+ type: string
381
+ enum:
382
+ - client
383
+ - category
384
+ - inference_runs.model_version
385
+ - objects.label_id
386
+ - scene.point_cloud_media
387
+ responses:
388
+ "200":
389
+ description: The session document for the given `_id`.
390
+ content:
391
+ application/json:
392
+ schema:
393
+ $ref: "#/components/schemas/SessionSchema"
394
+ put:
395
+ summary: Update a session
396
+ description: |
397
+ Standard put — typically for limited fields (status corrections,
398
+ capture metadata). `company_namespace` is derived from the
399
+ caller's session.
400
+ operationId: updateAiObjectDetectionSession
401
+ parameters:
402
+ - in: path
403
+ name: id
404
+ required: true
405
+ schema: { type: string }
406
+ requestBody:
407
+ required: true
408
+ content:
409
+ application/json:
410
+ schema:
411
+ $ref: "#/components/schemas/SessionUpdateBody"
412
+ responses:
413
+ "200":
414
+ description: The session document after the update is applied.
415
+ content:
416
+ application/json:
417
+ schema:
418
+ $ref: "#/components/schemas/SessionSchema"
419
+ delete:
420
+ summary: Soft-delete a session
421
+ description: |
422
+ Soft-deletes the session (`disabled: true`). By default child
423
+ tasks are kept (usable training data). Pass `?cascade=true` to
424
+ also soft-delete the session's child tasks.
425
+ operationId: removeAiObjectDetectionSession
426
+ parameters:
427
+ - in: path
428
+ name: id
429
+ required: true
430
+ schema: { type: string }
431
+ - in: query
432
+ name: cascade
433
+ description: When `true`, also soft-delete the session's child tasks.
434
+ schema: { type: boolean, default: false }
435
+ responses:
436
+ "200":
437
+ description: "The session document after soft-deletion (`disabled: true`)."
438
+ content:
439
+ application/json:
440
+ schema:
441
+ $ref: "#/components/schemas/SessionSchema"
442
+ components:
443
+ securitySchemes:
444
+ ApiKeyAuth:
445
+ type: apiKey
446
+ in: header
447
+ name: api-key
448
+ description: |
449
+ Server-issued API key. Also accepted via the `x-api-key` header or the
450
+ `?apiKey=` query parameter as fallbacks.
451
+ JwtAuth:
452
+ type: apiKey
453
+ in: header
454
+ name: Authorization
455
+ description: |
456
+ Raw JWT in the `Authorization` header — **no `Bearer ` prefix**.
457
+ Obtained from `POST /authenticate` (admin / rep / client login).
458
+ schemas:
459
+ UserRef:
460
+ type: object
461
+ description: Compact actor reference (rep or admin).
462
+ properties:
463
+ _id: { type: string }
464
+ type: { type: string, enum: [admin, rep] }
465
+ name: { type: string }
466
+ rep: { type: string }
467
+ admin: { type: string }
468
+ Device:
469
+ type: object
470
+ properties:
471
+ platform: { type: string }
472
+ os: { type: string }
473
+ model: { type: string }
474
+ app_version: { type: string }
475
+ ar_engine: { type: string, enum: [ARKit, ARCore] }
476
+ CaptureSettings:
477
+ type: object
478
+ properties:
479
+ rate_hz: { type: number }
480
+ recording_enabled: { type: boolean }
481
+ target_distance_m: { type: number }
482
+ resolution:
483
+ type: string
484
+ enum: [medium, high, max]
485
+ description: Camera format preference active during capture.
486
+ Plane:
487
+ type: object
488
+ properties:
489
+ kind: { type: string, enum: [shelf, floor, wall] }
490
+ normal:
491
+ type: array
492
+ items: { type: number }
493
+ description: 3-float plane normal.
494
+ d:
495
+ type: number
496
+ description: "Plane equation constant in Ax+By+Cz+d=0."
497
+ inliers: { type: number }
498
+ bounds:
499
+ type: object
500
+ properties:
501
+ min: { type: array, items: { type: number } }
502
+ max: { type: array, items: { type: number } }
503
+ SessionObject:
504
+ type: object
505
+ description: Cross-frame fused object after clustering.
506
+ properties:
507
+ _id: { type: string }
508
+ label_id: { type: string }
509
+ cluster_size:
510
+ type: number
511
+ description: How many frames contributed to this object.
512
+ world_position:
513
+ type: object
514
+ properties:
515
+ x: { type: number }
516
+ y: { type: number }
517
+ z: { type: number }
518
+ world_orientation:
519
+ type: object
520
+ properties:
521
+ yaw: { type: number }
522
+ pitch: { type: number }
523
+ roll: { type: number }
524
+ bbox_3d:
525
+ type: object
526
+ properties:
527
+ min: { type: array, items: { type: number } }
528
+ max: { type: array, items: { type: number } }
529
+ placement_confidence:
530
+ type: number
531
+ description: Fused 0..1 placement confidence.
532
+ winning_task: { type: string }
533
+ winning_annotation_id: { type: string }
534
+ contributing_tasks:
535
+ type: array
536
+ items: { type: string }
537
+ arbitration:
538
+ type: object
539
+ additionalProperties: true
540
+ description: Weights / tiebreak rationale.
541
+ InferenceRun:
542
+ type: object
543
+ properties:
544
+ _id: { type: string }
545
+ model_version: { type: string }
546
+ started_at: { type: number }
547
+ finished_at: { type: number }
548
+ config:
549
+ type: object
550
+ additionalProperties: true
551
+ description: Clustering params, RANSAC config.
552
+ status: { type: string, enum: [pending, success, failed] }
553
+ triggered_by:
554
+ $ref: "#/components/schemas/UserRef"
555
+ notes: { type: string }
556
+ Scene:
557
+ type: object
558
+ properties:
559
+ planes:
560
+ type: array
561
+ items:
562
+ $ref: "#/components/schemas/Plane"
563
+ point_cloud_media:
564
+ type: string
565
+ description: "`media.mediaStorages` id of the .ply / .npy point cloud."
566
+ point_cloud_stats:
567
+ type: object
568
+ properties:
569
+ n_points: { type: number }
570
+ voxel_size_m: { type: number }
571
+ bbox_min: { type: array, items: { type: number } }
572
+ bbox_max: { type: array, items: { type: number } }
573
+ world_frame:
574
+ type: string
575
+ enum: [arkit, arcore, normalized]
576
+ description: Coordinate system the world positions are reported in.
577
+ SessionSchema:
578
+ type: object
579
+ description: Object detection session document.
580
+ properties:
581
+ _id: { type: string }
582
+ session_id:
583
+ type: string
584
+ description: "Device-generated session id (4-char). Election clones carry a `-E<n>` suffix."
585
+ source_session:
586
+ type: string
587
+ description: Provenance — the session this one was materialized from by `ai-object-detection-session-election` (absent on device-captured sessions).
588
+ device:
589
+ $ref: "#/components/schemas/Device"
590
+ client:
591
+ type: string
592
+ description: "`clients` `_id` of the scanned store/client (optional)."
593
+ client_populated:
594
+ type: object
595
+ description: Populated client document (when `client` is in `populatedKeys`).
596
+ additionalProperties: true
597
+ category:
598
+ type: string
599
+ description: |
600
+ `ai-object-detection-category` `_id` picked on the device before
601
+ calibration (optional). Drives the auto-analysis fired when the
602
+ device posts the upload-complete marker.
603
+ category_populated:
604
+ type: object
605
+ description: Populated category document (when `category` is in `populatedKeys`).
606
+ additionalProperties: true
607
+ mission:
608
+ type: string
609
+ description: |
610
+ `ai-object-detection-mission` `_id` the session was STARTED FROM
611
+ on the device ("SCAN THIS MISSION") — optional; generic scans
612
+ carry none. The attribution anchor: mission completions and
613
+ mission-result rows credit THIS mission only.
614
+ rep:
615
+ type: string
616
+ description: "`representatives` `_id` sent on the frames (defaults to the rep in the token) — the scanning rep for api-key intakes."
617
+ visit_id:
618
+ type: string
619
+ description: |
620
+ Device visit id (`visits.visit_id`) of the visit the scan happened
621
+ in — stamped by the frame intake, stored as-is (visits sync after
622
+ the fact, never resolved to a server ref). Copied onto the
623
+ mission/metric results so the assigned-missions read can tell
624
+ "done in THIS visit".
625
+ route:
626
+ type: string
627
+ description: "`sv.routes` `_id` of the visit's route, as sent by the device."
628
+ business_day:
629
+ type: string
630
+ description: "Business day of the scan, `YYYY-MM-DD` — as sent by the device, else stamped once from the capture time under the rep's stamping context (shift schedule, `end_of_day` cut, time zone)."
631
+ time_zone:
632
+ type: string
633
+ description: IANA timezone of the device at capture, as sent by the device.
634
+ aisle_label: { type: string }
635
+ aisle_length_m: { type: number }
636
+ capture_settings:
637
+ $ref: "#/components/schemas/CaptureSettings"
638
+ status:
639
+ type: string
640
+ enum:
641
+ [open, uploaded, infer_in_progress, inferred, rearbitrating, failed]
642
+ _errors:
643
+ type: array
644
+ items: { type: object, additionalProperties: true }
645
+ frames_total: { type: number }
646
+ frames_accepted: { type: number }
647
+ tasks_count: { type: number }
648
+ detections_count: { type: number }
649
+ objects_count: { type: number }
650
+ ground:
651
+ type: object
652
+ description: |
653
+ Floor lock measured on-device at capture entry (absent when the
654
+ rep skipped it). World Y is gravity-aligned, so the height above
655
+ the floor of any world point is "y - ground.y_world".
656
+ properties:
657
+ y_world:
658
+ { type: number, description: Floor height (world Y), metres. }
659
+ samples: { type: integer }
660
+ spread_m:
661
+ {
662
+ type: number,
663
+ description: Lock quality (IQR of the samples),
664
+ metres.,
665
+ }
666
+ camera_height_m: { type: number }
667
+ eye_level_m:
668
+ type: number
669
+ description: |
670
+ Camera height above the locked floor while shooting the shelf,
671
+ metres — the rep's standing eye level.
672
+ world_alignment:
673
+ type: string
674
+ description: "'gravity' on both ARKit/ARCore — recorded, not assumed."
675
+ coverage_m2:
676
+ type: number
677
+ description: Device-computed swept shelf area, m² — includes quality-rejected (never uploaded) attempts, so it cannot be recomputed server-side. Monotonic per session.
678
+ ground_skipped:
679
+ type: boolean
680
+ description: The rep tapped Skip on the floor point (a deliberate choice, unlike a lock that never converged).
681
+ session_score:
682
+ type: number
683
+ description: "Election evaluation — average frame quality, 0..1 (written by `ai-object-detection-session-election`)."
684
+ session_verdict:
685
+ type: string
686
+ enum: [excellent, good, acceptable, rejected]
687
+ description: Articulated verdict from `session_score` against the namespace detection-settings score bands; `rejected` when any rejection reason fired.
688
+ rejection_reasons:
689
+ type: array
690
+ items: { type: string }
691
+ description: "Session gates that fired at election: `coverage_below_target`, `jump_detected`, `too_many_elected`."
692
+ election_excluded:
693
+ type: array
694
+ description: Error-tier frames excluded from the election (they stay on the source session).
695
+ items:
696
+ type: object
697
+ properties:
698
+ task: { type: string }
699
+ violations:
700
+ type: array
701
+ items: { type: string }
702
+ scene:
703
+ $ref: "#/components/schemas/Scene"
704
+ objects:
705
+ type: array
706
+ items:
707
+ $ref: "#/components/schemas/SessionObject"
708
+ inference_runs:
709
+ type: array
710
+ items:
711
+ $ref: "#/components/schemas/InferenceRun"
712
+ creator:
713
+ $ref: "#/components/schemas/UserRef"
714
+ editor:
715
+ $ref: "#/components/schemas/UserRef"
716
+ disabled: { type: boolean }
717
+ company_namespace:
718
+ type: array
719
+ items: { type: string }
720
+ description: Tenant key. Server-injected — never accept from clients.
721
+ createdAt: { type: string, format: date-time }
722
+ updatedAt: { type: string, format: date-time }
723
+ SessionCreateBody:
724
+ type: object
725
+ description: |
726
+ Body for opening a session. `creator` and the aggregate counters are
727
+ server-managed. The tenant key (`company_namespace`) is optional for
728
+ SDK callers and is otherwise injected from the caller's session.
729
+ properties:
730
+ session_id: { type: string }
731
+ source_session: { type: string }
732
+ device:
733
+ $ref: "#/components/schemas/Device"
734
+ client: { type: string }
735
+ category: { type: string }
736
+ mission: { type: string }
737
+ rep: { type: string }
738
+ visit_id: { type: string }
739
+ route: { type: string }
740
+ business_day:
741
+ type: string
742
+ description: "`YYYY-MM-DD`."
743
+ time_zone: { type: string }
744
+ capture_settings:
745
+ $ref: "#/components/schemas/CaptureSettings"
746
+ coverage_m2: { type: number }
747
+ ground:
748
+ type: object
749
+ properties:
750
+ y_world: { type: number }
751
+ samples: { type: integer }
752
+ spread_m: { type: number }
753
+ camera_height_m: { type: number }
754
+ eye_level_m: { type: number }
755
+ ground_skipped: { type: boolean }
756
+ world_alignment: { type: string }
757
+ status:
758
+ type: string
759
+ enum:
760
+ [open, uploaded, infer_in_progress, inferred, rearbitrating, failed]
761
+ default: open
762
+ company_namespace:
763
+ type: array
764
+ items: { type: string }
765
+ description: Optional tenant namespace override for SDK callers.
766
+ SessionUpdateBody:
767
+ type: object
768
+ description: |
769
+ Body for updating a session (any stored field; `editor` is
770
+ server-stamped). The tenant key is derived from the caller's
771
+ session. Set `disabled: true` to soft-delete.
772
+ properties:
773
+ session_id: { type: string }
774
+ device:
775
+ $ref: "#/components/schemas/Device"
776
+ client: { type: string }
777
+ category: { type: string }
778
+ mission: { type: string }
779
+ rep: { type: string }
780
+ visit_id: { type: string }
781
+ route: { type: string }
782
+ business_day: { type: string }
783
+ time_zone: { type: string }
784
+ capture_settings:
785
+ $ref: "#/components/schemas/CaptureSettings"
786
+ coverage_m2: { type: number }
787
+ ground:
788
+ type: object
789
+ properties:
790
+ y_world: { type: number }
791
+ samples: { type: integer }
792
+ spread_m: { type: number }
793
+ camera_height_m: { type: number }
794
+ eye_level_m: { type: number }
795
+ ground_skipped: { type: boolean }
796
+ world_alignment: { type: string }
797
+ status:
798
+ type: string
799
+ enum:
800
+ [open, uploaded, infer_in_progress, inferred, rearbitrating, failed]
801
+ disabled: { type: boolean }
802
+ PatchActionWrite:
803
+ type: object
804
+ properties:
805
+ key: { type: string }
806
+ command:
807
+ type: string
808
+ enum: [set, addToSet, pull]
809
+ value: {}
810
+ PatchActionBody:
811
+ type: object
812
+ description: Bulk-update body. `writeQuery[]` describes the writes applied to filtered rows.
813
+ properties:
814
+ writeQuery:
815
+ type: array
816
+ items:
817
+ $ref: "#/components/schemas/PatchActionWrite"
818
+ PatchActionResult:
819
+ type: object
820
+ properties:
821
+ nFound: { type: number }
822
+ nModified: { type: number }
823
+ SessionFindResult:
824
+ type: object
825
+ description: Standard paginated result envelope.
826
+ properties:
827
+ data:
828
+ type: array
829
+ items:
830
+ $ref: "#/components/schemas/SessionSchema"
831
+ total_result: { type: number }
832
+ current_count: { type: number }
833
+ total_pages: { type: number }
834
+ current_page: { type: number }
835
+ per_page: { type: number }
836
+ first_page_url: { type: string }
837
+ last_page_url: { type: string }
838
+ next_page_url: { type: string, nullable: true }
839
+ prev_page_url: { type: string, nullable: true }
840
+ path: { type: string }