@viewrium/sdk 0.1.0 → 2.0.0

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/dist/index.d.ts CHANGED
@@ -1,350 +1,3403 @@
1
1
  /**
2
- * Typed error hierarchy for the Viewrium SDK.
3
- *
4
- * All API-originated failures inherit from `ViewriumError`. Network-level
5
- * failures (connection reset, DNS) bubble up as the underlying fetch error
6
- * unchanged.
2
+ * This file was auto-generated by openapi-typescript.
3
+ * Do not make direct changes to the file.
7
4
  */
8
- declare class ViewriumError extends Error {
9
- readonly statusCode: number | undefined;
10
- readonly responseBody: unknown;
11
- constructor(message: string, options?: {
12
- statusCode?: number;
13
- responseBody?: unknown;
14
- });
15
- }
16
- declare class AuthError extends ViewriumError {
17
- constructor(message: string, options?: {
18
- statusCode?: number;
19
- responseBody?: unknown;
20
- });
21
- }
22
- declare class NotFoundError extends ViewriumError {
23
- constructor(message: string, options?: {
24
- statusCode?: number;
25
- responseBody?: unknown;
26
- });
5
+ interface paths {
6
+ "/v1/products": {
7
+ parameters: {
8
+ query?: never;
9
+ header?: never;
10
+ path?: never;
11
+ cookie?: never;
12
+ };
13
+ /**
14
+ * List Products
15
+ * @description The catalog (D54): one org, the principal's only org when `org_id` is omitted, or
16
+ * `scope=all` for every org an agency member or staff reaches. Filters AND together.
17
+ */
18
+ get: operations["list_products_v1_products_get"];
19
+ put?: never;
20
+ /**
21
+ * Create Product
22
+ * @description Exactly one source (image_paths | image_urls | video_url | source_url | model_url |
23
+ * resume_token).
24
+ *
25
+ * - 200 {ProductParkedResponse} - the gate ALWAYS parks a fresh request (D24): the
26
+ * analysis ran synchronously and NOTHING was written; the plan, the flags and the cost
27
+ * come back with the token the Summary posts to commit.
28
+ * - 202 {ProductResponse} - resume_token or model_url: the product and its asset rows
29
+ * exist and the run has started. 409 on a replayed token or a duplicate custom_id.
30
+ *
31
+ * D76: the plan is IMMUTABLE once the token exists. A resume commits the plan, the model
32
+ * verdict and the price it was quoted for, and may still edit the `draft` (product
33
+ * columns, none of which reaches the plan). A resume that states a different `order`, or
34
+ * any `overrides`, is a 400 - changing what is generated means a new request.
35
+ */
36
+ post: operations["create_product_v1_products_post"];
37
+ /**
38
+ * Batch Delete Products
39
+ * @description 200 with per-id affected/not_found/forbidden buckets.
40
+ */
41
+ delete: operations["batch_delete_products_v1_products_delete"];
42
+ options?: never;
43
+ head?: never;
44
+ patch?: never;
45
+ trace?: never;
46
+ };
47
+ "/v1/products/publish": {
48
+ parameters: {
49
+ query?: never;
50
+ header?: never;
51
+ path?: never;
52
+ cookie?: never;
53
+ };
54
+ get?: never;
55
+ put?: never;
56
+ /**
57
+ * Publish Products
58
+ * @description The bulk publish toggle (D52). Single products toggle through PATCH. 402 when the org's
59
+ * plan does not allow publishing, or the batch would pass its active-product cap.
60
+ */
61
+ post: operations["publish_products_v1_products_publish_post"];
62
+ delete?: never;
63
+ options?: never;
64
+ head?: never;
65
+ patch?: never;
66
+ trace?: never;
67
+ };
68
+ "/v1/products/export": {
69
+ parameters: {
70
+ query?: never;
71
+ header?: never;
72
+ path?: never;
73
+ cookie?: never;
74
+ };
75
+ /**
76
+ * Export Products
77
+ * @description A ZIP of finished images (D52): select by `ids` or by the list's filters, choose which
78
+ * image review statuses (default accepted and pending). Each image is finished on demand -
79
+ * composited onto its background with its shadow - and reused while its settings are
80
+ * unchanged. The root `manifest.json` names every entry and every image skipped. The 3D
81
+ * model is never included.
82
+ *
83
+ * - 200 - the archive streams.
84
+ * - 202 {ProductExportQueuedResponse} - too many images need finishing for one request:
85
+ * they are finished in the background, a download link is emailed, and the same request
86
+ * streams the ZIP once they are done.
87
+ *
88
+ * 400 past 200 products.
89
+ */
90
+ get: operations["export_products_v1_products_export_get"];
91
+ put?: never;
92
+ post?: never;
93
+ delete?: never;
94
+ options?: never;
95
+ head?: never;
96
+ patch?: never;
97
+ trace?: never;
98
+ };
99
+ "/v1/products/tags": {
100
+ parameters: {
101
+ query?: never;
102
+ header?: never;
103
+ path?: never;
104
+ cookie?: never;
105
+ };
106
+ /**
107
+ * List Product Tags
108
+ * @description Distinct tags across the same scope the list takes (`org_id` / `scope=all`).
109
+ */
110
+ get: operations["list_product_tags_v1_products_tags_get"];
111
+ put?: never;
112
+ post?: never;
113
+ delete?: never;
114
+ options?: never;
115
+ head?: never;
116
+ patch?: never;
117
+ trace?: never;
118
+ };
119
+ "/v1/products/{id}": {
120
+ parameters: {
121
+ query?: never;
122
+ header?: never;
123
+ path?: never;
124
+ cookie?: never;
125
+ };
126
+ /**
127
+ * Get Product
128
+ * @description Two response shapes by principal kind: session/sk get the product with its assets; a
129
+ * pk key gets PublicProductResponse, the allow-listed subset safe to expose in
130
+ * browser-visible storefront JS, and only for a published product with a ready model.
131
+ */
132
+ get: operations["get_product_v1_products__id__get"];
133
+ put?: never;
134
+ post?: never;
135
+ /** Delete Product */
136
+ delete: operations["delete_product_v1_products__id__delete"];
137
+ options?: never;
138
+ head?: never;
139
+ /**
140
+ * Update Product
141
+ * @description The customer's fields, the user-set status and the publish toggle. 409 on a duplicate
142
+ * custom_id; 400 for `awaiting_review`; 402 when the publish toggle is refused by the plan.
143
+ */
144
+ patch: operations["update_product_v1_products__id__patch"];
145
+ trace?: never;
146
+ };
147
+ "/v1/products/{id}/order": {
148
+ parameters: {
149
+ query?: never;
150
+ header?: never;
151
+ path?: never;
152
+ cookie?: never;
153
+ };
154
+ get?: never;
155
+ put?: never;
156
+ /**
157
+ * Order Product
158
+ * @description Order the missing half - images onto a model-only product, or a model onto an images
159
+ * product. PARKS (D24): the additions and their cost come back inside a `resume_token`
160
+ * that `POST /v1/products` commits; nothing is written here. 409 with `details.reason`
161
+ * (`no_analysis`, `generating`, `images_ordered`, `model_exists`, `model_failed`) when the
162
+ * order cannot be placed; a failed model is never re-ordered through this route.
163
+ */
164
+ post: operations["order_product_v1_products__id__order_post"];
165
+ delete?: never;
166
+ options?: never;
167
+ head?: never;
168
+ patch?: never;
169
+ trace?: never;
170
+ };
171
+ "/v1/products/{id}/review": {
172
+ parameters: {
173
+ query?: never;
174
+ header?: never;
175
+ path?: never;
176
+ cookie?: never;
177
+ };
178
+ get?: never;
179
+ put?: never;
180
+ /**
181
+ * Review Product
182
+ * @description The review flush (E4 P5): the coalesced patches the UI batched - image review
183
+ * decisions and settings, the model's decision, the product's user-set status - validated
184
+ * together and written in order. Review records decisions only; the finished images are
185
+ * made by the export.
186
+ */
187
+ post: operations["review_product_v1_products__id__review_post"];
188
+ delete?: never;
189
+ options?: never;
190
+ head?: never;
191
+ patch?: never;
192
+ trace?: never;
193
+ };
194
+ "/v1/products/{id}/images/{image_id}": {
195
+ parameters: {
196
+ query?: never;
197
+ header?: never;
198
+ path?: never;
199
+ cookie?: never;
200
+ };
201
+ get?: never;
202
+ put?: never;
203
+ post?: never;
204
+ delete?: never;
205
+ options?: never;
206
+ head?: never;
207
+ /**
208
+ * Update Product Image
209
+ * @description One image: its review decision (one way out of pending), its shadow and background
210
+ * overrides, its filename and alt text. Answers the image as it now reads. 404 for a
211
+ * position the customer does not own.
212
+ */
213
+ patch: operations["update_product_image_v1_products__id__images__image_id__patch"];
214
+ trace?: never;
215
+ };
216
+ "/v1/products/{id}/model/rescale": {
217
+ parameters: {
218
+ query?: never;
219
+ header?: never;
220
+ path?: never;
221
+ cookie?: never;
222
+ };
223
+ get?: never;
224
+ put?: never;
225
+ /**
226
+ * Rescale Product Model
227
+ * @description Re-apply real-world scale to the GLB in place; the product keeps the axes the
228
+ * customer stated. 409 without a ready model; 502 when the resize itself fails.
229
+ */
230
+ post: operations["rescale_product_model_v1_products__id__model_rescale_post"];
231
+ delete?: never;
232
+ options?: never;
233
+ head?: never;
234
+ patch?: never;
235
+ trace?: never;
236
+ };
237
+ "/v1/products/{id}/model": {
238
+ parameters: {
239
+ query?: never;
240
+ header?: never;
241
+ path?: never;
242
+ cookie?: never;
243
+ };
244
+ get?: never;
245
+ put?: never;
246
+ post?: never;
247
+ delete?: never;
248
+ options?: never;
249
+ head?: never;
250
+ /**
251
+ * Update Product Model
252
+ * @description The model's review decision (D14: independent of the images and of the product).
253
+ */
254
+ patch: operations["update_product_model_v1_products__id__model_patch"];
255
+ trace?: never;
256
+ };
257
+ "/v1/api_keys": {
258
+ parameters: {
259
+ query?: never;
260
+ header?: never;
261
+ path?: never;
262
+ cookie?: never;
263
+ };
264
+ /**
265
+ * List Api Keys
266
+ * @description sk auth sees only its own project's keys.
267
+ */
268
+ get: operations["list_api_keys_v1_api_keys_get"];
269
+ put?: never;
270
+ /**
271
+ * Create Api Key
272
+ * @description 201 - the full key is returned exactly once.
273
+ */
274
+ post: operations["create_api_key_v1_api_keys_post"];
275
+ delete?: never;
276
+ options?: never;
277
+ head?: never;
278
+ patch?: never;
279
+ trace?: never;
280
+ };
281
+ "/v1/api_keys/{id}": {
282
+ parameters: {
283
+ query?: never;
284
+ header?: never;
285
+ path?: never;
286
+ cookie?: never;
287
+ };
288
+ get?: never;
289
+ put?: never;
290
+ post?: never;
291
+ /**
292
+ * Revoke Api Key
293
+ * @description Soft revoke (sets revoked_at); the row is kept for usage attribution.
294
+ */
295
+ delete: operations["revoke_api_key_v1_api_keys__id__delete"];
296
+ options?: never;
297
+ head?: never;
298
+ patch?: never;
299
+ trace?: never;
300
+ };
301
+ "/v1/usage": {
302
+ parameters: {
303
+ query?: never;
304
+ header?: never;
305
+ path?: never;
306
+ cookie?: never;
307
+ };
308
+ /**
309
+ * Get Usage
310
+ * @description Buckets and totals over a window. One org (`org_id`, or the principal's only org), every
311
+ * org the session reaches (`scope=all`), or one product (`product_id`, which needs no
312
+ * `org_id`). Org-internal traffic is excluded unless `include_internal`.
313
+ *
314
+ * `group_by=product` adds `by_product`: the window's best-performing products, ranked by 3D
315
+ * views then AR entries and capped, each with its title. The buckets are unchanged - to break
316
+ * one period down, ask again with the window narrowed to it.
317
+ */
318
+ get: operations["get_usage_v1_usage_get"];
319
+ put?: never;
320
+ post?: never;
321
+ delete?: never;
322
+ options?: never;
323
+ head?: never;
324
+ patch?: never;
325
+ trace?: never;
326
+ };
327
+ "/v1/beacon": {
328
+ parameters: {
329
+ query?: never;
330
+ header?: never;
331
+ path?: never;
332
+ cookie?: never;
333
+ };
334
+ get?: never;
335
+ put?: never;
336
+ /**
337
+ * Ingest Beacon
338
+ * @description 204 No Content - never errors to the embed beyond 4xx auth/validation.
339
+ */
340
+ post: operations["ingest_beacon_v1_beacon_post"];
341
+ delete?: never;
342
+ options?: never;
343
+ head?: never;
344
+ patch?: never;
345
+ trace?: never;
346
+ };
347
+ "/v1/webhooks": {
348
+ parameters: {
349
+ query?: never;
350
+ header?: never;
351
+ path?: never;
352
+ cookie?: never;
353
+ };
354
+ /** List Webhook Endpoints */
355
+ get: operations["list_webhook_endpoints_v1_webhooks_get"];
356
+ put?: never;
357
+ /**
358
+ * Create Webhook Endpoint
359
+ * @description 201 - includes signing_secret (readable later too; parity with today).
360
+ */
361
+ post: operations["create_webhook_endpoint_v1_webhooks_post"];
362
+ delete?: never;
363
+ options?: never;
364
+ head?: never;
365
+ patch?: never;
366
+ trace?: never;
367
+ };
368
+ "/v1/webhooks/{id}": {
369
+ parameters: {
370
+ query?: never;
371
+ header?: never;
372
+ path?: never;
373
+ cookie?: never;
374
+ };
375
+ /** Get Webhook Endpoint */
376
+ get: operations["get_webhook_endpoint_v1_webhooks__id__get"];
377
+ put?: never;
378
+ post?: never;
379
+ /** Delete Webhook Endpoint */
380
+ delete: operations["delete_webhook_endpoint_v1_webhooks__id__delete"];
381
+ options?: never;
382
+ head?: never;
383
+ /** Update Webhook Endpoint */
384
+ patch: operations["update_webhook_endpoint_v1_webhooks__id__patch"];
385
+ trace?: never;
386
+ };
387
+ "/v1/webhooks/{id}/deliveries": {
388
+ parameters: {
389
+ query?: never;
390
+ header?: never;
391
+ path?: never;
392
+ cookie?: never;
393
+ };
394
+ /** List Webhook Deliveries */
395
+ get: operations["list_webhook_deliveries_v1_webhooks__id__deliveries_get"];
396
+ put?: never;
397
+ post?: never;
398
+ delete?: never;
399
+ options?: never;
400
+ head?: never;
401
+ patch?: never;
402
+ trace?: never;
403
+ };
404
+ "/v1/projects": {
405
+ parameters: {
406
+ query?: never;
407
+ header?: never;
408
+ path?: never;
409
+ cookie?: never;
410
+ };
411
+ /**
412
+ * List Projects
413
+ * @description sk auth: returns its one project.
414
+ */
415
+ get: operations["list_projects_v1_projects_get"];
416
+ put?: never;
417
+ post?: never;
418
+ delete?: never;
419
+ options?: never;
420
+ head?: never;
421
+ patch?: never;
422
+ trace?: never;
423
+ };
424
+ "/v1/projects/{id}": {
425
+ parameters: {
426
+ query?: never;
427
+ header?: never;
428
+ path?: never;
429
+ cookie?: never;
430
+ };
431
+ /** Get Project */
432
+ get: operations["get_project_v1_projects__id__get"];
433
+ put?: never;
434
+ post?: never;
435
+ delete?: never;
436
+ options?: never;
437
+ head?: never;
438
+ patch?: never;
439
+ trace?: never;
440
+ };
441
+ "/v1/health": {
442
+ parameters: {
443
+ query?: never;
444
+ header?: never;
445
+ path?: never;
446
+ cookie?: never;
447
+ };
448
+ /** Health */
449
+ get: operations["health_v1_health_get"];
450
+ put?: never;
451
+ post?: never;
452
+ delete?: never;
453
+ options?: never;
454
+ head?: never;
455
+ patch?: never;
456
+ trace?: never;
457
+ };
27
458
  }
28
- declare class ValidationError extends ViewriumError {
29
- constructor(message: string, options?: {
30
- statusCode?: number;
31
- responseBody?: unknown;
32
- });
459
+ interface components {
460
+ schemas: {
461
+ /** ApiKeyCreateRequest */
462
+ ApiKeyCreateRequest: {
463
+ key_type: components["schemas"]["KeyType"];
464
+ /** Org Id */
465
+ org_id?: string | null;
466
+ /** Project Id */
467
+ project_id?: string | null;
468
+ /** Name */
469
+ name?: string | null;
470
+ /**
471
+ * Scopes
472
+ * @default []
473
+ */
474
+ scopes: string[];
475
+ };
476
+ /**
477
+ * ApiKeyCreateResponse
478
+ * @description 201. `key` is the full key material. For secret keys this is the only time
479
+ * it is ever returned; publishable keys stay retrievable on later reads.
480
+ */
481
+ ApiKeyCreateResponse: {
482
+ /**
483
+ * Id
484
+ * Format: uuid
485
+ */
486
+ id: string;
487
+ /** Org Id */
488
+ org_id?: string | null;
489
+ /** Project Id */
490
+ project_id?: string | null;
491
+ key_type: components["schemas"]["KeyType"];
492
+ /** Key Prefix */
493
+ key_prefix: string;
494
+ /** Name */
495
+ name?: string | null;
496
+ /**
497
+ * Scopes
498
+ * @default []
499
+ */
500
+ scopes: string[];
501
+ /** Last Used At */
502
+ last_used_at?: string | null;
503
+ /** Revoked At */
504
+ revoked_at?: string | null;
505
+ /**
506
+ * Created At
507
+ * Format: date-time
508
+ */
509
+ created_at: string;
510
+ /** Key */
511
+ key: string;
512
+ };
513
+ /** ApiKeyListResponse */
514
+ ApiKeyListResponse: {
515
+ /** Data */
516
+ data: components["schemas"]["ApiKeyResponse"][];
517
+ };
518
+ /** ApiKeyResponse */
519
+ ApiKeyResponse: {
520
+ /**
521
+ * Id
522
+ * Format: uuid
523
+ */
524
+ id: string;
525
+ /** Org Id */
526
+ org_id?: string | null;
527
+ /** Project Id */
528
+ project_id?: string | null;
529
+ key_type: components["schemas"]["KeyType"];
530
+ /** Key Prefix */
531
+ key_prefix: string;
532
+ /** Name */
533
+ name?: string | null;
534
+ /**
535
+ * Scopes
536
+ * @default []
537
+ */
538
+ scopes: string[];
539
+ /** Last Used At */
540
+ last_used_at?: string | null;
541
+ /** Revoked At */
542
+ revoked_at?: string | null;
543
+ /**
544
+ * Created At
545
+ * Format: date-time
546
+ */
547
+ created_at: string;
548
+ /** Key */
549
+ key?: string | null;
550
+ };
551
+ /**
552
+ * AssetStatus
553
+ * @description product_images.status and product_models.status (CHECK-enforced). `skipped` is an
554
+ * image position nothing produced; a model row never carries it. Under E4 §6.1 a customer's
555
+ * WITHHELD position also reads `skipped` - the projection, not the row, says so.
556
+ * @enum {string}
557
+ */
558
+ AssetStatus: "planned" | "generating" | "ready" | "failed" | "skipped";
559
+ /**
560
+ * BeaconEventType
561
+ * @description The subset of UsageEventType that POST /v1/beacon may ingest.
562
+ * generation.* events are recorded server-side only, never via beacon.
563
+ * @enum {string}
564
+ */
565
+ BeaconEventType: "view" | "ar_enter" | "ar_session_ended";
566
+ /**
567
+ * BeaconRequest
568
+ * @description ONE ingest path, two auth modes (pk + Origin check for embeds; the first-party
569
+ * viewer posts without a key). DECIDED (Dave, 2026-07-20): anonymous first-party
570
+ * ingest is an accepted, signed-off risk. Writes usage_records; org/project/key
571
+ * attribution resolved server-side. Response: 204 No Content.
572
+ */
573
+ BeaconRequest: {
574
+ /**
575
+ * Model Id
576
+ * Format: uuid
577
+ */
578
+ model_id: string;
579
+ event_type: components["schemas"]["BeaconEventType"];
580
+ /** Session Id */
581
+ session_id?: string | null;
582
+ /** Duration Ms */
583
+ duration_ms?: number | null;
584
+ /** Metadata */
585
+ metadata?: {
586
+ [key: string]: unknown;
587
+ } | null;
588
+ };
589
+ /**
590
+ * DimensionUnit
591
+ * @description The unit `ModelCreateRequest.dimensions` is expressed in. API-boundary only:
592
+ * the endpoint converts to canonical cm (lib/units) before anything downstream sees
593
+ * the numbers - the DB, the workflow, and the SDKs' stored dims are always cm.
594
+ * @enum {string}
595
+ */
596
+ DimensionUnit: "mm" | "cm" | "m" | "in" | "ft";
597
+ /**
598
+ * DisplayStatus
599
+ * @description What a product LOOKS like, D13's vocabulary: the stored status, overridden by
600
+ * Generating while any asset is in flight and by Published once it is live. Values are
601
+ * the machine vocabulary; the customer copy is written from them on the surface.
602
+ * @enum {string}
603
+ */
604
+ DisplayStatus: "published" | "generating" | "failed" | "awaiting_review" | "needs_attention" | "ready_to_publish" | "complete";
605
+ /**
606
+ * GateFlags
607
+ * @description describe's structured read of the product (D25). `source_quality: poor` warns on both
608
+ * layers and gates neither.
609
+ */
610
+ GateFlags: {
611
+ /**
612
+ * Transparent
613
+ * @default false
614
+ */
615
+ transparent: boolean;
616
+ /**
617
+ * Geometry Risk
618
+ * @default false
619
+ */
620
+ geometry_risk: boolean;
621
+ /**
622
+ * Texture Risk
623
+ * @default false
624
+ */
625
+ texture_risk: boolean;
626
+ /**
627
+ * Source Quality
628
+ * @default ok
629
+ * @enum {string}
630
+ */
631
+ source_quality: "ok" | "poor";
632
+ /** Reasons */
633
+ reasons?: components["schemas"]["GateReason"][];
634
+ };
635
+ /**
636
+ * GateReason
637
+ * @description One raised flag, in describe's own words. Shown to the customer VERBATIM (D25).
638
+ */
639
+ GateReason: {
640
+ /** Code */
641
+ code: string;
642
+ /** Sentence */
643
+ sentence: string;
644
+ };
645
+ /** HTTPValidationError */
646
+ HTTPValidationError: {
647
+ /** Detail */
648
+ detail?: components["schemas"]["ValidationError"][];
649
+ };
650
+ /** HealthResponse */
651
+ HealthResponse: {
652
+ /**
653
+ * Status
654
+ * @default ok
655
+ * @constant
656
+ */
657
+ status: "ok";
658
+ /** Version */
659
+ version?: string | null;
660
+ };
661
+ /**
662
+ * KeyType
663
+ * @description api_keys.key_type (CHECK-enforced). Prefixes: pk_ (publishable), sk_ (secret).
664
+ * @enum {string}
665
+ */
666
+ KeyType: "publishable" | "secret";
667
+ /**
668
+ * ListScope
669
+ * @description GET /v1/products scope (II.4, D44): one org, or every org the principal reaches -
670
+ * an agency's clients, or everything for staff. Writes always name one org.
671
+ * @enum {string}
672
+ */
673
+ ListScope: "org" | "all";
674
+ /**
675
+ * ModelDimensions
676
+ * @description Real-world dimensions in cm (DB: products.height/width/depth numeric).
677
+ */
678
+ ModelDimensions: {
679
+ /** Height */
680
+ height?: number | null;
681
+ /** Width */
682
+ width?: number | null;
683
+ /** Depth */
684
+ depth?: number | null;
685
+ };
686
+ /**
687
+ * ModelPlanResponse
688
+ * @description The models layer's verdict. `views` is the D28 set for this form, in submission
689
+ * order; internal gap-fills are machinery and are deliberately NOT listed (D33).
690
+ *
691
+ * `unseen_views` are the views the model will be built from INVENTED rows (generated with
692
+ * `evidence: unseen`, the D28 fill excepted - `gate.invented_model_views`), listed whether or
693
+ * not they refuse it. Two or more put `unseen_views` in `blocked_by` (`gate.UNSEEN_BLOCK_AT`).
694
+ */
695
+ ModelPlanResponse: {
696
+ /** Ordered */
697
+ ordered: boolean;
698
+ /** Allowed */
699
+ allowed: boolean;
700
+ /** Blocked By */
701
+ blocked_by?: string[];
702
+ /** Views */
703
+ views?: string[];
704
+ /** Unseen Views */
705
+ unseen_views?: string[];
706
+ };
707
+ /**
708
+ * PlannedView
709
+ * @description One page position as it will be committed - a `product_images` row, pre-shaped.
710
+ *
711
+ * `reason_code` is the closed vocabulary of `lib/gate.py`; `reason` is routing's own
712
+ * factual sentence. The customer copy is written from the CODE on the surface that shows
713
+ * it (E6), never from this string.
714
+ */
715
+ PlannedView: {
716
+ /** View */
717
+ view: string;
718
+ /**
719
+ * Kind
720
+ * @enum {string}
721
+ */
722
+ kind: "wanted" | "extra";
723
+ /** Stands For */
724
+ stands_for: string;
725
+ /**
726
+ * Status
727
+ * @enum {string}
728
+ */
729
+ status: "planned" | "skipped";
730
+ /** Evidence */
731
+ evidence: string;
732
+ /** Reason Code */
733
+ reason_code: string;
734
+ /** Reason */
735
+ reason: string;
736
+ /** Deliverable */
737
+ deliverable: boolean;
738
+ /** Route */
739
+ route?: ("reuse" | "edit" | "generate") | null;
740
+ /** Photo */
741
+ photo?: number | null;
742
+ /** Pins */
743
+ pins?: number[];
744
+ /** Azimuth */
745
+ azimuth: string;
746
+ /** Tilt */
747
+ tilt: string;
748
+ /**
749
+ * Flagged
750
+ * @default false
751
+ */
752
+ flagged: boolean;
753
+ };
754
+ /**
755
+ * ProductBatchResponse
756
+ * @description Per-id outcome; 200 even when some ids were missing.
757
+ */
758
+ ProductBatchResponse: {
759
+ /** Affected */
760
+ affected: string[];
761
+ /** Not Found */
762
+ not_found: string[];
763
+ /** Forbidden */
764
+ forbidden: string[];
765
+ };
766
+ /**
767
+ * ProductCost
768
+ * @description What an order costs, in CREDITS (1 credit = $0.50): 1 per deliverable image, a flat 10
769
+ * for a model. Internal 3D gap-fills never enter the maths.
770
+ */
771
+ ProductCost: {
772
+ /** Images */
773
+ images: number;
774
+ /** Image Credits */
775
+ image_credits: number;
776
+ /** Model Credits */
777
+ model_credits: number;
778
+ /** Total Credits */
779
+ total_credits: number;
780
+ };
781
+ /**
782
+ * ProductCreateRequest
783
+ * @description POST /v1/products - park (200) or, with `resume_token`, commit (202).
784
+ *
785
+ * Exactly ONE source: image_paths | image_urls | video_url | source_url | model_url |
786
+ * resume_token.
787
+ * `image_paths` are UPLOADS-BUCKET object paths, not URLs: the wizard uploads
788
+ * direct-to-storage before any row exists (D24), so the gate is handed paths inside the
789
+ * caller's own org folder and re-hosts nothing (E1 P5).
790
+ */
791
+ ProductCreateRequest: {
792
+ /** Image Paths */
793
+ image_paths?: string[] | null;
794
+ /** Image Urls */
795
+ image_urls?: string[] | null;
796
+ /** Video Url */
797
+ video_url?: string | null;
798
+ /** Source Url */
799
+ source_url?: string | null;
800
+ /** Model Url */
801
+ model_url?: string | null;
802
+ /** Resume Token */
803
+ resume_token?: string | null;
804
+ /** Org Id */
805
+ org_id?: string | null;
806
+ draft?: components["schemas"]["ProductDraft"];
807
+ order?: components["schemas"]["ProductOrder"];
808
+ overrides?: components["schemas"]["ProductOverrides"];
809
+ };
810
+ /**
811
+ * ProductDraft
812
+ * @description The customer-editable half of a product, sent at park and again at resume.
813
+ *
814
+ * A resume may CHANGE these (D24: the wizard Summary is the park state, so its fields are
815
+ * still live when Generate is pressed), which is why they ride in the token rather than
816
+ * being frozen at the first call.
817
+ */
818
+ ProductDraft: {
819
+ /** Title */
820
+ title?: string | null;
821
+ /** Description */
822
+ description?: string | null;
823
+ /** Price */
824
+ price?: number | null;
825
+ /** Currency */
826
+ currency?: string | null;
827
+ /** Custom Id */
828
+ custom_id?: string | null;
829
+ /** Tags */
830
+ tags?: string[] | null;
831
+ /** Metadata */
832
+ metadata?: {
833
+ [key: string]: unknown;
834
+ } | null;
835
+ dimensions?: components["schemas"]["ModelDimensions"] | null;
836
+ unit?: components["schemas"]["DimensionUnit"] | null;
837
+ /** Wall Mounted */
838
+ wall_mounted?: boolean | null;
839
+ };
840
+ /**
841
+ * ProductExportQueuedResponse
842
+ * @description 202 from the export: more images need finishing than one request can do, so they are
843
+ * being finished in the background and nothing streamed. A download link is emailed to
844
+ * `notify_email` when they are done; repeating the same request then streams the ZIP
845
+ * (and repeating it earlier simply queues what is still missing).
846
+ */
847
+ ProductExportQueuedResponse: {
848
+ /**
849
+ * Status
850
+ * @default queued
851
+ * @constant
852
+ */
853
+ status: "queued";
854
+ /** Products */
855
+ products: number;
856
+ /** Images */
857
+ images: number;
858
+ /** To Finish */
859
+ to_finish: number;
860
+ /** Notify Email */
861
+ notify_email?: string | null;
862
+ };
863
+ /**
864
+ * ProductImagePatch
865
+ * @description PATCH /v1/products/{id}/images/{image_id}, and one item of the review flush.
866
+ *
867
+ * An explicit null CLEARS an override (the field is in `model_fields_set`); an omitted
868
+ * field leaves it alone. `review_status` is one way out of `pending` (Q7).
869
+ */
870
+ ProductImagePatch: {
871
+ review_status?: components["schemas"]["ReviewStatus"] | null;
872
+ shadow_override?: components["schemas"]["ShadowOverride"] | null;
873
+ /** Background Override */
874
+ background_override?: number[] | null;
875
+ /** Filename Override */
876
+ filename_override?: string | null;
877
+ /** Alt Override */
878
+ alt_override?: string | null;
879
+ };
880
+ /**
881
+ * ProductImageResponse
882
+ * @description One customer-visible image position (E4 plan §6.1). A withheld position reads
883
+ * `status: skipped` with a `skip_reason` and no files; a 3D-only row never appears.
884
+ * `filename` / `alt_text` are the override when set, else `lib/naming.py`'s.
885
+ */
886
+ ProductImageResponse: {
887
+ /**
888
+ * Id
889
+ * Format: uuid
890
+ */
891
+ id: string;
892
+ /** View */
893
+ view: string;
894
+ /**
895
+ * Kind
896
+ * @enum {string}
897
+ */
898
+ kind: "wanted" | "extra";
899
+ /** Stands For */
900
+ stands_for: string;
901
+ status: components["schemas"]["AssetStatus"];
902
+ /** Skip Reason */
903
+ skip_reason?: string | null;
904
+ /** Route */
905
+ route?: ("reuse" | "edit" | "generate") | null;
906
+ /** Source */
907
+ source?: ("photograph" | "edited" | "generated") | null;
908
+ /** Evidence */
909
+ evidence?: string | null;
910
+ /** Azimuth */
911
+ azimuth?: string | null;
912
+ /** Tilt */
913
+ tilt?: string | null;
914
+ /** Source Photo Id */
915
+ source_photo_id?: string | null;
916
+ /**
917
+ * Attempts
918
+ * @default 0
919
+ */
920
+ attempts: number;
921
+ /**
922
+ * Flagged
923
+ * @default false
924
+ */
925
+ flagged: boolean;
926
+ /**
927
+ * Cut Flagged
928
+ * @default false
929
+ */
930
+ cut_flagged: boolean;
931
+ /** Cut Problems */
932
+ cut_problems?: string | null;
933
+ /** @default pending */
934
+ review_status: components["schemas"]["ReviewStatus"];
935
+ /** Filename */
936
+ filename: string;
937
+ /** Alt Text */
938
+ alt_text: string;
939
+ /** Filename Override */
940
+ filename_override?: string | null;
941
+ /** Alt Override */
942
+ alt_override?: string | null;
943
+ shadow_override?: components["schemas"]["ShadowOverride"] | null;
944
+ /** Background Override */
945
+ background_override?: number[] | null;
946
+ /** Frame Scale */
947
+ frame_scale?: number | null;
948
+ /** Contact Regions */
949
+ contact_regions?: number[][];
950
+ /** Raw Url */
951
+ raw_url?: string | null;
952
+ /** Cutout Url */
953
+ cutout_url?: string | null;
954
+ /** Warnings */
955
+ warnings?: components["schemas"]["ProductWarning"][];
956
+ /**
957
+ * Created At
958
+ * Format: date-time
959
+ */
960
+ created_at: string;
961
+ /** Updated At */
962
+ updated_at?: string | null;
963
+ };
964
+ /** ProductListResponse */
965
+ ProductListResponse: {
966
+ /** Data */
967
+ data: components["schemas"]["ProductSummary"][];
968
+ /** Total */
969
+ total: number;
970
+ /** Limit */
971
+ limit: number;
972
+ /** Offset */
973
+ offset: number;
974
+ };
975
+ /** ProductModelPatch */
976
+ ProductModelPatch: {
977
+ review_status?: components["schemas"]["ReviewStatus"] | null;
978
+ };
979
+ /** ProductModelResponse */
980
+ ProductModelResponse: {
981
+ /**
982
+ * Id
983
+ * Format: uuid
984
+ */
985
+ id: string;
986
+ status: components["schemas"]["AssetStatus"];
987
+ /** @default pending */
988
+ review_status: components["schemas"]["ReviewStatus"];
989
+ /** Glb Url */
990
+ glb_url?: string | null;
991
+ /** Thumbnail Url */
992
+ thumbnail_url?: string | null;
993
+ /** Scale Factor */
994
+ scale_factor?: number | null;
995
+ /** Width Axis */
996
+ width_axis?: ("x" | "z") | null;
997
+ flags?: components["schemas"]["GateFlags"];
998
+ /** Warnings */
999
+ warnings?: components["schemas"]["ProductWarning"][];
1000
+ /**
1001
+ * Created At
1002
+ * Format: date-time
1003
+ */
1004
+ created_at: string;
1005
+ /** Completed At */
1006
+ completed_at?: string | null;
1007
+ };
1008
+ /**
1009
+ * ProductOrder
1010
+ * @description What the customer asked to be produced. Either half may be ordered alone (plan §2.3).
1011
+ */
1012
+ ProductOrder: {
1013
+ /**
1014
+ * Images
1015
+ * @default true
1016
+ */
1017
+ images: boolean;
1018
+ /**
1019
+ * Model
1020
+ * @default false
1021
+ */
1022
+ model: boolean;
1023
+ };
1024
+ /**
1025
+ * ProductOrderParkedResponse
1026
+ * @description 200 from the order park: the ADDITIONS the commit will make, priced, inside a token.
1027
+ * Nothing was written.
1028
+ */
1029
+ ProductOrderParkedResponse: {
1030
+ /**
1031
+ * Status
1032
+ * @default parked
1033
+ * @constant
1034
+ */
1035
+ status: "parked";
1036
+ /** Resume Token */
1037
+ resume_token: string;
1038
+ /**
1039
+ * Expires At
1040
+ * Format: date-time
1041
+ */
1042
+ expires_at: string;
1043
+ order: components["schemas"]["ProductOrder"];
1044
+ flags: components["schemas"]["GateFlags"];
1045
+ /** Plan */
1046
+ plan?: components["schemas"]["PlannedView"][];
1047
+ model: components["schemas"]["ModelPlanResponse"];
1048
+ cost: components["schemas"]["ProductCost"];
1049
+ /** Warnings */
1050
+ warnings?: string[];
1051
+ /**
1052
+ * Generate Unseen
1053
+ * @default false
1054
+ */
1055
+ generate_unseen: boolean;
1056
+ /**
1057
+ * Force Model
1058
+ * @default false
1059
+ */
1060
+ force_model: boolean;
1061
+ };
1062
+ /**
1063
+ * ProductOrderRequest
1064
+ * @description POST /v1/products/{id}/order - the missing half (E4 plan §6.4). Parks; the commit is
1065
+ * POST /v1/products with the returned resume_token.
1066
+ */
1067
+ ProductOrderRequest: {
1068
+ order: components["schemas"]["ProductOrder"];
1069
+ overrides?: components["schemas"]["ProductOverrides"];
1070
+ };
1071
+ /**
1072
+ * ProductOverrides
1073
+ * @description The wizard's per-run answers. None = no answer, and the org preference stands - that
1074
+ * is the whole of D25's `wizard override ?? org preference`.
1075
+ */
1076
+ ProductOverrides: {
1077
+ /** Generate Unseen */
1078
+ generate_unseen?: boolean | null;
1079
+ /** Force Model */
1080
+ force_model?: boolean | null;
1081
+ };
1082
+ /**
1083
+ * ProductOwner
1084
+ * @description The profile that created a product, for a created-by initials badge (D54). Carries an
1085
+ * email, so it never reaches a pk principal - `PublicProductResponse` has no owner.
1086
+ */
1087
+ ProductOwner: {
1088
+ /**
1089
+ * Id
1090
+ * Format: uuid
1091
+ */
1092
+ id: string;
1093
+ /** Display Name */
1094
+ display_name?: string | null;
1095
+ /** Email */
1096
+ email: string;
1097
+ };
1098
+ /**
1099
+ * ProductParkedResponse
1100
+ * @description 200 from POST /v1/products - the gate ALWAYS parks (D24).
1101
+ *
1102
+ * No product row exists, nothing was written, and nothing can orphan: the analysis lives
1103
+ * only inside `resume_token`, which the Summary posts back to commit. `expires_at` mirrors
1104
+ * the Fernet TTL (7 days, fal's URL retention floor).
1105
+ */
1106
+ ProductParkedResponse: {
1107
+ /**
1108
+ * Status
1109
+ * @default parked
1110
+ * @constant
1111
+ */
1112
+ status: "parked";
1113
+ /** Resume Token */
1114
+ resume_token: string;
1115
+ /**
1116
+ * Expires At
1117
+ * Format: date-time
1118
+ */
1119
+ expires_at: string;
1120
+ /** Form */
1121
+ form: string;
1122
+ /** Title */
1123
+ title?: string | null;
1124
+ /** Description */
1125
+ description?: string | null;
1126
+ /**
1127
+ * Subject
1128
+ * @default
1129
+ */
1130
+ subject: string;
1131
+ /**
1132
+ * Wall Mounted
1133
+ * @default false
1134
+ */
1135
+ wall_mounted: boolean;
1136
+ dimensions?: components["schemas"]["ModelDimensions"] | null;
1137
+ display_unit?: components["schemas"]["DimensionUnit"] | null;
1138
+ flags: components["schemas"]["GateFlags"];
1139
+ /** Warnings */
1140
+ warnings?: string[];
1141
+ /** Plan */
1142
+ plan?: components["schemas"]["PlannedView"][];
1143
+ model: components["schemas"]["ModelPlanResponse"];
1144
+ cost: components["schemas"]["ProductCost"];
1145
+ /**
1146
+ * Generate Unseen
1147
+ * @default false
1148
+ */
1149
+ generate_unseen: boolean;
1150
+ /**
1151
+ * Force Model
1152
+ * @default false
1153
+ */
1154
+ force_model: boolean;
1155
+ };
1156
+ /**
1157
+ * ProductPublishRequest
1158
+ * @description POST /v1/products/publish - the bulk publish toggle (D52, E8).
1159
+ */
1160
+ ProductPublishRequest: {
1161
+ /** Ids */
1162
+ ids: string[];
1163
+ /** Published */
1164
+ published: boolean;
1165
+ };
1166
+ /**
1167
+ * ProductRescaleRequest
1168
+ * @description POST /v1/products/{id}/model/rescale. At least one axis; every supplied axis is applied
1169
+ * to the GEOMETRY; `unit` covers the values (absent = cm) and becomes the product's
1170
+ * display_unit; `width_axis` skips the ruler render and the VLM call.
1171
+ */
1172
+ ProductRescaleRequest: {
1173
+ /** Height */
1174
+ height?: number | null;
1175
+ /** Width */
1176
+ width?: number | null;
1177
+ /** Depth */
1178
+ depth?: number | null;
1179
+ unit?: components["schemas"]["DimensionUnit"] | null;
1180
+ /** Width Axis */
1181
+ width_axis?: ("x" | "z") | null;
1182
+ };
1183
+ /**
1184
+ * ProductResponse
1185
+ * @description The product WITH its assets: 202 from a committed POST, and GET /v1/products/{id}.
1186
+ *
1187
+ * `status` is the STORED review status; `display_status` is D13's fold of it with
1188
+ * `published` and whether any asset is still in flight - the one a surface shows.
1189
+ * `images` is the customer's list (§6.1), in page order; `warnings` is every warning for
1190
+ * the product, image ones first.
1191
+ */
1192
+ ProductResponse: {
1193
+ /**
1194
+ * Id
1195
+ * Format: uuid
1196
+ */
1197
+ id: string;
1198
+ /**
1199
+ * Org Id
1200
+ * Format: uuid
1201
+ */
1202
+ org_id: string;
1203
+ /** Title */
1204
+ title: string;
1205
+ /** Custom Id */
1206
+ custom_id?: string | null;
1207
+ /** Tags */
1208
+ tags?: string[];
1209
+ status: components["schemas"]["ProductStatus"];
1210
+ display_status: components["schemas"]["DisplayStatus"];
1211
+ /**
1212
+ * Published
1213
+ * @default false
1214
+ */
1215
+ published: boolean;
1216
+ /** Form */
1217
+ form?: string | null;
1218
+ source_type?: components["schemas"]["SourceType"] | null;
1219
+ /** Price */
1220
+ price?: number | null;
1221
+ /** Currency */
1222
+ currency?: string | null;
1223
+ /** Height */
1224
+ height?: number | null;
1225
+ /** Width */
1226
+ width?: number | null;
1227
+ /** Depth */
1228
+ depth?: number | null;
1229
+ display_unit?: components["schemas"]["DimensionUnit"] | null;
1230
+ /**
1231
+ * Images Ordered
1232
+ * @default false
1233
+ */
1234
+ images_ordered: boolean;
1235
+ /** Thumbnail Url */
1236
+ thumbnail_url?: string | null;
1237
+ /**
1238
+ * Has Warnings
1239
+ * @default false
1240
+ */
1241
+ has_warnings: boolean;
1242
+ /**
1243
+ * Warning Count
1244
+ * @default 0
1245
+ */
1246
+ warning_count: number;
1247
+ /**
1248
+ * Comment Count
1249
+ * @default 0
1250
+ */
1251
+ comment_count: number;
1252
+ /**
1253
+ * Image Count
1254
+ * @default 0
1255
+ */
1256
+ image_count: number;
1257
+ model_status?: components["schemas"]["AssetStatus"] | null;
1258
+ owner?: components["schemas"]["ProductOwner"] | null;
1259
+ /**
1260
+ * Created At
1261
+ * Format: date-time
1262
+ */
1263
+ created_at: string;
1264
+ /** Updated At */
1265
+ updated_at?: string | null;
1266
+ /** Description */
1267
+ description?: string | null;
1268
+ /**
1269
+ * Wall Mounted
1270
+ * @default false
1271
+ */
1272
+ wall_mounted: boolean;
1273
+ /** Product Type */
1274
+ product_type?: string | null;
1275
+ /** Material Words */
1276
+ material_words?: string | null;
1277
+ /**
1278
+ * Transparent
1279
+ * @default false
1280
+ */
1281
+ transparent: boolean;
1282
+ /** Metadata */
1283
+ metadata?: {
1284
+ [key: string]: unknown;
1285
+ };
1286
+ /** Sources */
1287
+ sources?: components["schemas"]["ProductSourceResponse"][];
1288
+ /** Images */
1289
+ images?: components["schemas"]["ProductImageResponse"][];
1290
+ model?: components["schemas"]["ProductModelResponse"] | null;
1291
+ /** Warnings */
1292
+ warnings?: components["schemas"]["ProductWarning"][];
1293
+ /**
1294
+ * Open Request Count
1295
+ * @default 0
1296
+ */
1297
+ open_request_count: number;
1298
+ };
1299
+ /** ProductReviewImagePatch */
1300
+ ProductReviewImagePatch: {
1301
+ review_status?: components["schemas"]["ReviewStatus"] | null;
1302
+ shadow_override?: components["schemas"]["ShadowOverride"] | null;
1303
+ /** Background Override */
1304
+ background_override?: number[] | null;
1305
+ /** Filename Override */
1306
+ filename_override?: string | null;
1307
+ /** Alt Override */
1308
+ alt_override?: string | null;
1309
+ /**
1310
+ * Image Id
1311
+ * Format: uuid
1312
+ */
1313
+ image_id: string;
1314
+ };
1315
+ /**
1316
+ * ProductReviewRequest
1317
+ * @description POST /v1/products/{id}/review - the write-behind flush (E4 P5): the coalesced patches
1318
+ * the UI batched, applied in order after every one has been validated.
1319
+ */
1320
+ ProductReviewRequest: {
1321
+ status?: components["schemas"]["ProductStatus"] | null;
1322
+ /** Images */
1323
+ images?: components["schemas"]["ProductReviewImagePatch"][];
1324
+ model?: components["schemas"]["ProductModelPatch"] | null;
1325
+ };
1326
+ /** ProductReviewResponse */
1327
+ ProductReviewResponse: {
1328
+ /**
1329
+ * Product Id
1330
+ * Format: uuid
1331
+ */
1332
+ product_id: string;
1333
+ /** Applied */
1334
+ applied: number;
1335
+ display_status: components["schemas"]["DisplayStatus"];
1336
+ };
1337
+ /**
1338
+ * ProductSortField
1339
+ * @description Sortable columns for GET /v1/products. D54's four sorts are these two, each way;
1340
+ * restricted to a closed set because the value reaches PostgREST's `order` as a column.
1341
+ * @enum {string}
1342
+ */
1343
+ ProductSortField: "created_at" | "title";
1344
+ /** ProductSourceResponse */
1345
+ ProductSourceResponse: {
1346
+ /**
1347
+ * Id
1348
+ * Format: uuid
1349
+ */
1350
+ id: string;
1351
+ /**
1352
+ * Kind
1353
+ * @enum {string}
1354
+ */
1355
+ kind: "photo" | "video";
1356
+ /** Url */
1357
+ url: string;
1358
+ /** Original Name */
1359
+ original_name?: string | null;
1360
+ /**
1361
+ * Position
1362
+ * @default 0
1363
+ */
1364
+ position: number;
1365
+ /** Azimuth */
1366
+ azimuth?: string | null;
1367
+ /** Tilt */
1368
+ tilt?: string | null;
1369
+ /** Clean */
1370
+ clean?: boolean | null;
1371
+ /**
1372
+ * Created At
1373
+ * Format: date-time
1374
+ */
1375
+ created_at: string;
1376
+ };
1377
+ /**
1378
+ * ProductStatus
1379
+ * @description products.status (CHECK-enforced, E1 WP3). The STORED review lifecycle only - what a
1380
+ * surface DISPLAYS also folds in `published` and whether any asset is still generating
1381
+ * (D13); `lib.product_status.displayed_status` is the one place that fold happens.
1382
+ * @enum {string}
1383
+ */
1384
+ ProductStatus: "awaiting_review" | "needs_attention" | "ready_to_publish" | "complete";
1385
+ /**
1386
+ * ProductSummary
1387
+ * @description A catalog row (D54): the product plus what its card needs. No nested assets.
1388
+ */
1389
+ ProductSummary: {
1390
+ /**
1391
+ * Id
1392
+ * Format: uuid
1393
+ */
1394
+ id: string;
1395
+ /**
1396
+ * Org Id
1397
+ * Format: uuid
1398
+ */
1399
+ org_id: string;
1400
+ /** Title */
1401
+ title: string;
1402
+ /** Custom Id */
1403
+ custom_id?: string | null;
1404
+ /** Tags */
1405
+ tags?: string[];
1406
+ status: components["schemas"]["ProductStatus"];
1407
+ display_status: components["schemas"]["DisplayStatus"];
1408
+ /**
1409
+ * Published
1410
+ * @default false
1411
+ */
1412
+ published: boolean;
1413
+ /** Form */
1414
+ form?: string | null;
1415
+ source_type?: components["schemas"]["SourceType"] | null;
1416
+ /** Price */
1417
+ price?: number | null;
1418
+ /** Currency */
1419
+ currency?: string | null;
1420
+ /** Height */
1421
+ height?: number | null;
1422
+ /** Width */
1423
+ width?: number | null;
1424
+ /** Depth */
1425
+ depth?: number | null;
1426
+ display_unit?: components["schemas"]["DimensionUnit"] | null;
1427
+ /**
1428
+ * Images Ordered
1429
+ * @default false
1430
+ */
1431
+ images_ordered: boolean;
1432
+ /** Thumbnail Url */
1433
+ thumbnail_url?: string | null;
1434
+ /**
1435
+ * Has Warnings
1436
+ * @default false
1437
+ */
1438
+ has_warnings: boolean;
1439
+ /**
1440
+ * Warning Count
1441
+ * @default 0
1442
+ */
1443
+ warning_count: number;
1444
+ /**
1445
+ * Comment Count
1446
+ * @default 0
1447
+ */
1448
+ comment_count: number;
1449
+ /**
1450
+ * Image Count
1451
+ * @default 0
1452
+ */
1453
+ image_count: number;
1454
+ model_status?: components["schemas"]["AssetStatus"] | null;
1455
+ owner?: components["schemas"]["ProductOwner"] | null;
1456
+ /**
1457
+ * Created At
1458
+ * Format: date-time
1459
+ */
1460
+ created_at: string;
1461
+ /** Updated At */
1462
+ updated_at?: string | null;
1463
+ };
1464
+ /**
1465
+ * ProductUpdateRequest
1466
+ * @description PATCH /v1/products/{id} - the customer's fields, the user-set status (D13; never
1467
+ * back to `awaiting_review`) and the publish toggle. Dimensions are stored WITHOUT a
1468
+ * rescale; `POST .../model/rescale` rewrites the GLB.
1469
+ */
1470
+ ProductUpdateRequest: {
1471
+ /** Title */
1472
+ title?: string | null;
1473
+ /** Description */
1474
+ description?: string | null;
1475
+ /** Price */
1476
+ price?: number | null;
1477
+ /** Currency */
1478
+ currency?: string | null;
1479
+ /** Custom Id */
1480
+ custom_id?: string | null;
1481
+ /** Tags */
1482
+ tags?: string[] | null;
1483
+ /** Metadata */
1484
+ metadata?: {
1485
+ [key: string]: unknown;
1486
+ } | null;
1487
+ dimensions?: components["schemas"]["ModelDimensions"] | null;
1488
+ unit?: components["schemas"]["DimensionUnit"] | null;
1489
+ display_unit?: components["schemas"]["DimensionUnit"] | null;
1490
+ /** Wall Mounted */
1491
+ wall_mounted?: boolean | null;
1492
+ status?: components["schemas"]["ProductStatus"] | null;
1493
+ /** Published */
1494
+ published?: boolean | null;
1495
+ };
1496
+ /**
1497
+ * ProductWarning
1498
+ * @description One customer-facing warning, derived at read time (D21) by `lib/warnings.py`.
1499
+ */
1500
+ ProductWarning: {
1501
+ kind: components["schemas"]["WarningKind"];
1502
+ /**
1503
+ * Scope
1504
+ * @enum {string}
1505
+ */
1506
+ scope: "image" | "product";
1507
+ /** Title */
1508
+ title: string;
1509
+ /** Detail */
1510
+ detail: string;
1511
+ /** View */
1512
+ view?: string | null;
1513
+ };
1514
+ /** ProjectListResponse */
1515
+ ProjectListResponse: {
1516
+ /** Data */
1517
+ data: components["schemas"]["ProjectResponse"][];
1518
+ };
1519
+ /** ProjectResponse */
1520
+ ProjectResponse: {
1521
+ /**
1522
+ * Id
1523
+ * Format: uuid
1524
+ */
1525
+ id: string;
1526
+ /**
1527
+ * Org Id
1528
+ * Format: uuid
1529
+ */
1530
+ org_id: string;
1531
+ /** Name */
1532
+ name: string;
1533
+ /**
1534
+ * Allowed Domains
1535
+ * @default []
1536
+ */
1537
+ allowed_domains: string[];
1538
+ /**
1539
+ * Always Generate
1540
+ * @default false
1541
+ */
1542
+ always_generate: boolean;
1543
+ /**
1544
+ * Created At
1545
+ * Format: date-time
1546
+ */
1547
+ created_at: string;
1548
+ };
1549
+ /**
1550
+ * PublicProductResponse
1551
+ * @description What a PUBLISHABLE key receives from GET /v1/products/{id} - an explicit ALLOW-LIST,
1552
+ * deliberately a separate model rather than a filtered ProductResponse.
1553
+ *
1554
+ * pk keys ship inside the embed script in browser-visible JS on merchants' public
1555
+ * storefronts, so anything here is world-readable. An allow-list fails closed: a field
1556
+ * added to ProductResponse tomorrow is NOT exposed here until someone adds it on purpose.
1557
+ * Only a PUBLISHED product with a READY model is served at all (the `public_products()`
1558
+ * gate, D72); the images half is post-launch backlog #4.
1559
+ */
1560
+ PublicProductResponse: {
1561
+ /**
1562
+ * Id
1563
+ * Format: uuid
1564
+ */
1565
+ id: string;
1566
+ /** Title */
1567
+ title: string;
1568
+ /** Model Url */
1569
+ model_url?: string | null;
1570
+ /** Thumbnail Url */
1571
+ thumbnail_url?: string | null;
1572
+ model_status?: components["schemas"]["AssetStatus"] | null;
1573
+ };
1574
+ /**
1575
+ * ReviewStatus
1576
+ * @description product_images.review_status and product_models.review_status (CHECK-enforced, E1
1577
+ * WP3). One way out of `pending` (Dave, 2026-09-09): accepted and rejected swap freely,
1578
+ * nothing goes back to pending.
1579
+ * @enum {string}
1580
+ */
1581
+ ReviewStatus: "pending" | "accepted" | "rejected";
1582
+ /** ShadowContact */
1583
+ ShadowContact: {
1584
+ /** Trailpx */
1585
+ trailPx?: number | null;
1586
+ /** Dark */
1587
+ dark?: number | null;
1588
+ /** End */
1589
+ end?: number | null;
1590
+ /** Midpoint */
1591
+ midpoint?: number | null;
1592
+ /** Regionheight */
1593
+ regionHeight?: number | null;
1594
+ };
1595
+ /** ShadowGround */
1596
+ ShadowGround: {
1597
+ /** Nudge */
1598
+ nudge?: number | null;
1599
+ /** Tilt */
1600
+ tilt?: number | null;
1601
+ };
1602
+ /**
1603
+ * ShadowOverride
1604
+ * @description A per-image shadow override in the RENDERER'S own shape (`@repo/heinimex-render`'s
1605
+ * `ShadowSettings` minus `background`), every field optional: the compositor merges it over
1606
+ * CALIBRATED field by field, so a caller sends only what it overrides. Background is its
1607
+ * own column (`background_override`), as the review app keeps it.
1608
+ */
1609
+ ShadowOverride: {
1610
+ /** Mode */
1611
+ mode?: ("silhouette" | "contact" | "both") | null;
1612
+ /** Tone */
1613
+ tone?: number[] | null;
1614
+ silhouette?: components["schemas"]["ShadowSilhouette"] | null;
1615
+ contact?: components["schemas"]["ShadowContact"] | null;
1616
+ ground?: components["schemas"]["ShadowGround"] | null;
1617
+ };
1618
+ /** ShadowSilhouette */
1619
+ ShadowSilhouette: {
1620
+ /** Length */
1621
+ length?: number | null;
1622
+ /** Lean */
1623
+ lean?: number | null;
1624
+ /** Blurnear */
1625
+ blurNear?: number | null;
1626
+ /** Blurfar */
1627
+ blurFar?: number | null;
1628
+ /** Opacity */
1629
+ opacity?: number | null;
1630
+ /** Distancefade */
1631
+ distanceFade?: number | null;
1632
+ };
1633
+ /**
1634
+ * SortOrder
1635
+ * @enum {string}
1636
+ */
1637
+ SortOrder: "asc" | "desc";
1638
+ /**
1639
+ * SourceType
1640
+ * @description scans.source_type (unconstrained text in prod, default 'scan').
1641
+ *
1642
+ * DECIDED (Dave, 2026-07-20): v2 vocabulary is image | video | url | model with a
1643
+ * CHECK; legacy mapping upload -> model and scan -> video. Backfill drafted in the
1644
+ * pending migration (section 3); the CHECK itself waits for cutover because the live
1645
+ * v1 app still writes 'upload' and defaults to 'scan'.
1646
+ * @enum {string}
1647
+ */
1648
+ SourceType: "image" | "video" | "url" | "model" | "scan" | "upload";
1649
+ /** TagCount */
1650
+ TagCount: {
1651
+ /** Tag */
1652
+ tag: string;
1653
+ /** Count */
1654
+ count: number;
1655
+ };
1656
+ /**
1657
+ * TagMatch
1658
+ * @description How repeated ?tag= values combine (Dave ruling, 2026-07-22: caller-selectable,
1659
+ * the dashboard exposes an All/Any control inside the Tags dropdown).
1660
+ * @enum {string}
1661
+ */
1662
+ TagMatch: "all" | "any";
1663
+ /**
1664
+ * TagsResponse
1665
+ * @description Distinct tags across the list's scope (GET /v1/products/tags).
1666
+ */
1667
+ TagsResponse: {
1668
+ /** Data */
1669
+ data: components["schemas"]["TagCount"][];
1670
+ };
1671
+ /** UsageBucket */
1672
+ UsageBucket: {
1673
+ /**
1674
+ * Period Start
1675
+ * Format: date-time
1676
+ */
1677
+ period_start: string;
1678
+ event_type: components["schemas"]["UsageEventType"];
1679
+ /** Count */
1680
+ count: number;
1681
+ /** Cost Cents */
1682
+ cost_cents?: number | null;
1683
+ duration?: components["schemas"]["UsageDuration"] | null;
1684
+ };
1685
+ /**
1686
+ * UsageDuration
1687
+ * @description AR session length, as a SUM plus its own divisor - never a pre-computed average.
1688
+ *
1689
+ * The average is `total_ms / count`, so the same object serves a per-bucket mean (the
1690
+ * temporal distribution) and a window mean (the summary tile) from one response: a
1691
+ * pre-computed average could not be re-aggregated across buckets, a sum can.
1692
+ *
1693
+ * `count` is NOT UsageBucket.count. Only `ar_session_ended` carries a duration, and even
1694
+ * there `duration_ms` is optional on the beacon and arrives from an untrusted body, so
1695
+ * the events that contributed a usable value are counted separately and are the only
1696
+ * correct divisor.
1697
+ */
1698
+ UsageDuration: {
1699
+ /** Total Ms */
1700
+ total_ms: number;
1701
+ /** Count */
1702
+ count: number;
1703
+ };
1704
+ /**
1705
+ * UsageEventType
1706
+ * @description usage_records.event_type (CHECK-enforced, exact prod list - all six live in data).
1707
+ * @enum {string}
1708
+ */
1709
+ UsageEventType: "generation.created" | "generation.completed" | "generation.failed" | "view" | "ar_enter" | "ar_session_ended";
1710
+ /**
1711
+ * UsageProductTotals
1712
+ * @description One product's counts over the window - the `group_by=product` row.
1713
+ *
1714
+ * `title` is read from the product itself, so a caller ranking products needs no second request
1715
+ * to name them. A subject with no product row is a v1 SCAN and is left out entirely: v2 serves
1716
+ * no scan, and a ranking whose rows cannot be opened is a list of dead ends.
1717
+ */
1718
+ UsageProductTotals: {
1719
+ /**
1720
+ * Product Id
1721
+ * Format: uuid
1722
+ */
1723
+ product_id: string;
1724
+ /** Title */
1725
+ title?: string | null;
1726
+ /** Totals */
1727
+ totals: {
1728
+ [key: string]: number;
1729
+ };
1730
+ };
1731
+ /** UsageResponse */
1732
+ UsageResponse: {
1733
+ /**
1734
+ * Start
1735
+ * Format: date-time
1736
+ */
1737
+ start: string;
1738
+ /**
1739
+ * End
1740
+ * Format: date-time
1741
+ */
1742
+ end: string;
1743
+ /**
1744
+ * Granularity
1745
+ * @enum {string}
1746
+ */
1747
+ granularity: "hour" | "day";
1748
+ /** Buckets */
1749
+ buckets: components["schemas"]["UsageBucket"][];
1750
+ /** Totals */
1751
+ totals: {
1752
+ [key: string]: number;
1753
+ };
1754
+ /** Durations */
1755
+ durations?: {
1756
+ [key: string]: components["schemas"]["UsageDuration"];
1757
+ };
1758
+ /** By Product */
1759
+ by_product?: components["schemas"]["UsageProductTotals"][];
1760
+ };
1761
+ /** ValidationError */
1762
+ ValidationError: {
1763
+ /** Location */
1764
+ loc: (string | number)[];
1765
+ /** Message */
1766
+ msg: string;
1767
+ /** Error Type */
1768
+ type: string;
1769
+ /** Input */
1770
+ input?: unknown;
1771
+ /** Context */
1772
+ ctx?: Record<string, never>;
1773
+ };
1774
+ /**
1775
+ * WarningKind
1776
+ * @description The customer-facing warnings derived at read time (D21) - `lib/warnings.py` is the
1777
+ * rule, this is its vocabulary on the wire.
1778
+ * @enum {string}
1779
+ */
1780
+ WarningKind: "cut" | "generation" | "missing" | "lighting" | "model_failed" | "model_risk";
1781
+ /** WebhookDeliveryListResponse */
1782
+ WebhookDeliveryListResponse: {
1783
+ /** Data */
1784
+ data: components["schemas"]["WebhookDeliveryResponse"][];
1785
+ /** Limit */
1786
+ limit: number;
1787
+ /** Offset */
1788
+ offset: number;
1789
+ };
1790
+ /** WebhookDeliveryResponse */
1791
+ WebhookDeliveryResponse: {
1792
+ /**
1793
+ * Id
1794
+ * Format: uuid
1795
+ */
1796
+ id: string;
1797
+ /**
1798
+ * Endpoint Id
1799
+ * Format: uuid
1800
+ */
1801
+ endpoint_id: string;
1802
+ event_type: components["schemas"]["WebhookEventType"];
1803
+ /**
1804
+ * Event Id
1805
+ * Format: uuid
1806
+ */
1807
+ event_id: string;
1808
+ /** Http Status */
1809
+ http_status?: number | null;
1810
+ /** Attempts */
1811
+ attempts: number;
1812
+ /** Succeeded At */
1813
+ succeeded_at?: string | null;
1814
+ /** Last Attempt At */
1815
+ last_attempt_at?: string | null;
1816
+ /** Last Error */
1817
+ last_error?: string | null;
1818
+ /** Payload Json */
1819
+ payload_json?: {
1820
+ [key: string]: unknown;
1821
+ } | null;
1822
+ };
1823
+ /** WebhookEndpointCreateRequest */
1824
+ WebhookEndpointCreateRequest: {
1825
+ /** Url */
1826
+ url: string;
1827
+ /** Events */
1828
+ events: components["schemas"]["WebhookEventType"][];
1829
+ /** Description */
1830
+ description?: string | null;
1831
+ /** Org Id */
1832
+ org_id?: string | null;
1833
+ /** Project Id */
1834
+ project_id?: string | null;
1835
+ };
1836
+ /**
1837
+ * WebhookEndpointCreateResponse
1838
+ * @description 201. signing_secret returned on create; also readable later by org members
1839
+ * (DB stores it plaintext; dashboard shows it). PROPOSED: keep parity with current
1840
+ * behavior rather than show-once.
1841
+ */
1842
+ WebhookEndpointCreateResponse: {
1843
+ /**
1844
+ * Id
1845
+ * Format: uuid
1846
+ */
1847
+ id: string;
1848
+ /** Org Id */
1849
+ org_id?: string | null;
1850
+ /** Project Id */
1851
+ project_id?: string | null;
1852
+ /** Url */
1853
+ url: string;
1854
+ /** Description */
1855
+ description?: string | null;
1856
+ /** Events */
1857
+ events: components["schemas"]["WebhookEventType"][];
1858
+ /** Enabled */
1859
+ enabled: boolean;
1860
+ /**
1861
+ * Created At
1862
+ * Format: date-time
1863
+ */
1864
+ created_at: string;
1865
+ /**
1866
+ * Updated At
1867
+ * Format: date-time
1868
+ */
1869
+ updated_at: string;
1870
+ /** Signing Secret */
1871
+ signing_secret: string;
1872
+ };
1873
+ /** WebhookEndpointListResponse */
1874
+ WebhookEndpointListResponse: {
1875
+ /** Data */
1876
+ data: components["schemas"]["WebhookEndpointResponse"][];
1877
+ };
1878
+ /** WebhookEndpointResponse */
1879
+ WebhookEndpointResponse: {
1880
+ /**
1881
+ * Id
1882
+ * Format: uuid
1883
+ */
1884
+ id: string;
1885
+ /** Org Id */
1886
+ org_id?: string | null;
1887
+ /** Project Id */
1888
+ project_id?: string | null;
1889
+ /** Url */
1890
+ url: string;
1891
+ /** Description */
1892
+ description?: string | null;
1893
+ /** Events */
1894
+ events: components["schemas"]["WebhookEventType"][];
1895
+ /** Enabled */
1896
+ enabled: boolean;
1897
+ /**
1898
+ * Created At
1899
+ * Format: date-time
1900
+ */
1901
+ created_at: string;
1902
+ /**
1903
+ * Updated At
1904
+ * Format: date-time
1905
+ */
1906
+ updated_at: string;
1907
+ };
1908
+ /** WebhookEndpointUpdateRequest */
1909
+ WebhookEndpointUpdateRequest: {
1910
+ /** Url */
1911
+ url?: string | null;
1912
+ /** Events */
1913
+ events?: components["schemas"]["WebhookEventType"][] | null;
1914
+ /** Description */
1915
+ description?: string | null;
1916
+ /** Enabled */
1917
+ enabled?: boolean | null;
1918
+ };
1919
+ /**
1920
+ * WebhookEventType
1921
+ * @description webhook_endpoints.events / webhook_deliveries.event_type (unconstrained text).
1922
+ *
1923
+ * App-enforced vocabulary (docs + dashboard). Both prod tables are empty, so v2 can
1924
+ * rename/constrain freely with zero migration risk.
1925
+ * @enum {string}
1926
+ */
1927
+ WebhookEventType: "model.ready" | "model.failed" | "product.completed" | "product.failed";
1928
+ };
1929
+ responses: never;
1930
+ parameters: never;
1931
+ requestBodies: never;
1932
+ headers: never;
1933
+ pathItems: never;
33
1934
  }
34
- declare class RateLimitError extends ViewriumError {
35
- readonly retryAfter: number | undefined;
36
- constructor(message: string, options?: {
37
- statusCode?: number;
38
- responseBody?: unknown;
39
- retryAfter?: number;
40
- });
41
- }
42
- declare class APIError extends ViewriumError {
43
- constructor(message: string, options?: {
44
- statusCode?: number;
45
- responseBody?: unknown;
46
- });
1935
+ interface operations {
1936
+ list_products_v1_products_get: {
1937
+ parameters: {
1938
+ query?: {
1939
+ limit?: number;
1940
+ offset?: number;
1941
+ org_id?: string | null;
1942
+ scope?: components["schemas"]["ListScope"];
1943
+ q?: string | null;
1944
+ custom_id?: string | null;
1945
+ status?: components["schemas"]["DisplayStatus"][] | null;
1946
+ tag?: string[] | null;
1947
+ tag_match?: components["schemas"]["TagMatch"];
1948
+ created_after?: string | null;
1949
+ created_before?: string | null;
1950
+ created_by?: string[] | null;
1951
+ has_comments?: boolean | null;
1952
+ has_warnings?: boolean | null;
1953
+ form?: string[] | null;
1954
+ source_type?: components["schemas"]["SourceType"][] | null;
1955
+ sort?: components["schemas"]["ProductSortField"];
1956
+ order?: components["schemas"]["SortOrder"];
1957
+ };
1958
+ header?: never;
1959
+ path?: never;
1960
+ cookie?: never;
1961
+ };
1962
+ requestBody?: never;
1963
+ responses: {
1964
+ /** @description Successful Response */
1965
+ 200: {
1966
+ headers: {
1967
+ [name: string]: unknown;
1968
+ };
1969
+ content: {
1970
+ "application/json": components["schemas"]["ProductListResponse"];
1971
+ };
1972
+ };
1973
+ /** @description Validation Error */
1974
+ 422: {
1975
+ headers: {
1976
+ [name: string]: unknown;
1977
+ };
1978
+ content: {
1979
+ "application/json": components["schemas"]["HTTPValidationError"];
1980
+ };
1981
+ };
1982
+ };
1983
+ };
1984
+ create_product_v1_products_post: {
1985
+ parameters: {
1986
+ query?: never;
1987
+ header?: never;
1988
+ path?: never;
1989
+ cookie?: never;
1990
+ };
1991
+ requestBody: {
1992
+ content: {
1993
+ "application/json": components["schemas"]["ProductCreateRequest"];
1994
+ };
1995
+ };
1996
+ responses: {
1997
+ /** @description Successful Response */
1998
+ 200: {
1999
+ headers: {
2000
+ [name: string]: unknown;
2001
+ };
2002
+ content: {
2003
+ "application/json": components["schemas"]["ProductParkedResponse"];
2004
+ };
2005
+ };
2006
+ /** @description Accepted */
2007
+ 202: {
2008
+ headers: {
2009
+ [name: string]: unknown;
2010
+ };
2011
+ content: {
2012
+ "application/json": components["schemas"]["ProductResponse"];
2013
+ };
2014
+ };
2015
+ /** @description Validation Error */
2016
+ 422: {
2017
+ headers: {
2018
+ [name: string]: unknown;
2019
+ };
2020
+ content: {
2021
+ "application/json": components["schemas"]["HTTPValidationError"];
2022
+ };
2023
+ };
2024
+ };
2025
+ };
2026
+ batch_delete_products_v1_products_delete: {
2027
+ parameters: {
2028
+ query: {
2029
+ ids: string[];
2030
+ };
2031
+ header?: never;
2032
+ path?: never;
2033
+ cookie?: never;
2034
+ };
2035
+ requestBody?: never;
2036
+ responses: {
2037
+ /** @description Successful Response */
2038
+ 200: {
2039
+ headers: {
2040
+ [name: string]: unknown;
2041
+ };
2042
+ content: {
2043
+ "application/json": components["schemas"]["ProductBatchResponse"];
2044
+ };
2045
+ };
2046
+ /** @description Validation Error */
2047
+ 422: {
2048
+ headers: {
2049
+ [name: string]: unknown;
2050
+ };
2051
+ content: {
2052
+ "application/json": components["schemas"]["HTTPValidationError"];
2053
+ };
2054
+ };
2055
+ };
2056
+ };
2057
+ publish_products_v1_products_publish_post: {
2058
+ parameters: {
2059
+ query?: never;
2060
+ header?: never;
2061
+ path?: never;
2062
+ cookie?: never;
2063
+ };
2064
+ requestBody: {
2065
+ content: {
2066
+ "application/json": components["schemas"]["ProductPublishRequest"];
2067
+ };
2068
+ };
2069
+ responses: {
2070
+ /** @description Successful Response */
2071
+ 200: {
2072
+ headers: {
2073
+ [name: string]: unknown;
2074
+ };
2075
+ content: {
2076
+ "application/json": components["schemas"]["ProductBatchResponse"];
2077
+ };
2078
+ };
2079
+ /** @description Validation Error */
2080
+ 422: {
2081
+ headers: {
2082
+ [name: string]: unknown;
2083
+ };
2084
+ content: {
2085
+ "application/json": components["schemas"]["HTTPValidationError"];
2086
+ };
2087
+ };
2088
+ };
2089
+ };
2090
+ export_products_v1_products_export_get: {
2091
+ parameters: {
2092
+ query?: {
2093
+ org_id?: string | null;
2094
+ scope?: components["schemas"]["ListScope"];
2095
+ q?: string | null;
2096
+ custom_id?: string | null;
2097
+ status?: components["schemas"]["DisplayStatus"][] | null;
2098
+ tag?: string[] | null;
2099
+ tag_match?: components["schemas"]["TagMatch"];
2100
+ created_after?: string | null;
2101
+ created_before?: string | null;
2102
+ created_by?: string[] | null;
2103
+ has_comments?: boolean | null;
2104
+ has_warnings?: boolean | null;
2105
+ form?: string[] | null;
2106
+ source_type?: components["schemas"]["SourceType"][] | null;
2107
+ ids?: string[] | null;
2108
+ image_review_status?: components["schemas"]["ReviewStatus"][];
2109
+ };
2110
+ header?: never;
2111
+ path?: never;
2112
+ cookie?: never;
2113
+ };
2114
+ requestBody?: never;
2115
+ responses: {
2116
+ /** @description The archive */
2117
+ 200: {
2118
+ headers: {
2119
+ [name: string]: unknown;
2120
+ };
2121
+ content: {
2122
+ "application/json": unknown;
2123
+ "application/zip": unknown;
2124
+ };
2125
+ };
2126
+ /** @description Accepted */
2127
+ 202: {
2128
+ headers: {
2129
+ [name: string]: unknown;
2130
+ };
2131
+ content: {
2132
+ "application/json": components["schemas"]["ProductExportQueuedResponse"];
2133
+ };
2134
+ };
2135
+ /** @description Validation Error */
2136
+ 422: {
2137
+ headers: {
2138
+ [name: string]: unknown;
2139
+ };
2140
+ content: {
2141
+ "application/json": components["schemas"]["HTTPValidationError"];
2142
+ };
2143
+ };
2144
+ };
2145
+ };
2146
+ list_product_tags_v1_products_tags_get: {
2147
+ parameters: {
2148
+ query?: {
2149
+ org_id?: string | null;
2150
+ scope?: components["schemas"]["ListScope"];
2151
+ q?: string | null;
2152
+ custom_id?: string | null;
2153
+ status?: components["schemas"]["DisplayStatus"][] | null;
2154
+ tag?: string[] | null;
2155
+ tag_match?: components["schemas"]["TagMatch"];
2156
+ created_after?: string | null;
2157
+ created_before?: string | null;
2158
+ created_by?: string[] | null;
2159
+ has_comments?: boolean | null;
2160
+ has_warnings?: boolean | null;
2161
+ form?: string[] | null;
2162
+ source_type?: components["schemas"]["SourceType"][] | null;
2163
+ };
2164
+ header?: never;
2165
+ path?: never;
2166
+ cookie?: never;
2167
+ };
2168
+ requestBody?: never;
2169
+ responses: {
2170
+ /** @description Successful Response */
2171
+ 200: {
2172
+ headers: {
2173
+ [name: string]: unknown;
2174
+ };
2175
+ content: {
2176
+ "application/json": components["schemas"]["TagsResponse"];
2177
+ };
2178
+ };
2179
+ /** @description Validation Error */
2180
+ 422: {
2181
+ headers: {
2182
+ [name: string]: unknown;
2183
+ };
2184
+ content: {
2185
+ "application/json": components["schemas"]["HTTPValidationError"];
2186
+ };
2187
+ };
2188
+ };
2189
+ };
2190
+ get_product_v1_products__id__get: {
2191
+ parameters: {
2192
+ query?: never;
2193
+ header?: never;
2194
+ path: {
2195
+ id: string;
2196
+ };
2197
+ cookie?: never;
2198
+ };
2199
+ requestBody?: never;
2200
+ responses: {
2201
+ /** @description Successful Response */
2202
+ 200: {
2203
+ headers: {
2204
+ [name: string]: unknown;
2205
+ };
2206
+ content: {
2207
+ "application/json": components["schemas"]["ProductResponse"] | components["schemas"]["PublicProductResponse"];
2208
+ };
2209
+ };
2210
+ /** @description Validation Error */
2211
+ 422: {
2212
+ headers: {
2213
+ [name: string]: unknown;
2214
+ };
2215
+ content: {
2216
+ "application/json": components["schemas"]["HTTPValidationError"];
2217
+ };
2218
+ };
2219
+ };
2220
+ };
2221
+ delete_product_v1_products__id__delete: {
2222
+ parameters: {
2223
+ query?: never;
2224
+ header?: never;
2225
+ path: {
2226
+ id: string;
2227
+ };
2228
+ cookie?: never;
2229
+ };
2230
+ requestBody?: never;
2231
+ responses: {
2232
+ /** @description Successful Response */
2233
+ 204: {
2234
+ headers: {
2235
+ [name: string]: unknown;
2236
+ };
2237
+ content?: never;
2238
+ };
2239
+ /** @description Validation Error */
2240
+ 422: {
2241
+ headers: {
2242
+ [name: string]: unknown;
2243
+ };
2244
+ content: {
2245
+ "application/json": components["schemas"]["HTTPValidationError"];
2246
+ };
2247
+ };
2248
+ };
2249
+ };
2250
+ update_product_v1_products__id__patch: {
2251
+ parameters: {
2252
+ query?: never;
2253
+ header?: never;
2254
+ path: {
2255
+ id: string;
2256
+ };
2257
+ cookie?: never;
2258
+ };
2259
+ requestBody: {
2260
+ content: {
2261
+ "application/json": components["schemas"]["ProductUpdateRequest"];
2262
+ };
2263
+ };
2264
+ responses: {
2265
+ /** @description Successful Response */
2266
+ 200: {
2267
+ headers: {
2268
+ [name: string]: unknown;
2269
+ };
2270
+ content: {
2271
+ "application/json": components["schemas"]["ProductResponse"];
2272
+ };
2273
+ };
2274
+ /** @description Validation Error */
2275
+ 422: {
2276
+ headers: {
2277
+ [name: string]: unknown;
2278
+ };
2279
+ content: {
2280
+ "application/json": components["schemas"]["HTTPValidationError"];
2281
+ };
2282
+ };
2283
+ };
2284
+ };
2285
+ order_product_v1_products__id__order_post: {
2286
+ parameters: {
2287
+ query?: never;
2288
+ header?: never;
2289
+ path: {
2290
+ id: string;
2291
+ };
2292
+ cookie?: never;
2293
+ };
2294
+ requestBody: {
2295
+ content: {
2296
+ "application/json": components["schemas"]["ProductOrderRequest"];
2297
+ };
2298
+ };
2299
+ responses: {
2300
+ /** @description Successful Response */
2301
+ 200: {
2302
+ headers: {
2303
+ [name: string]: unknown;
2304
+ };
2305
+ content: {
2306
+ "application/json": components["schemas"]["ProductOrderParkedResponse"];
2307
+ };
2308
+ };
2309
+ /** @description Validation Error */
2310
+ 422: {
2311
+ headers: {
2312
+ [name: string]: unknown;
2313
+ };
2314
+ content: {
2315
+ "application/json": components["schemas"]["HTTPValidationError"];
2316
+ };
2317
+ };
2318
+ };
2319
+ };
2320
+ review_product_v1_products__id__review_post: {
2321
+ parameters: {
2322
+ query?: never;
2323
+ header?: never;
2324
+ path: {
2325
+ id: string;
2326
+ };
2327
+ cookie?: never;
2328
+ };
2329
+ requestBody: {
2330
+ content: {
2331
+ "application/json": components["schemas"]["ProductReviewRequest"];
2332
+ };
2333
+ };
2334
+ responses: {
2335
+ /** @description Successful Response */
2336
+ 200: {
2337
+ headers: {
2338
+ [name: string]: unknown;
2339
+ };
2340
+ content: {
2341
+ "application/json": components["schemas"]["ProductReviewResponse"];
2342
+ };
2343
+ };
2344
+ /** @description Validation Error */
2345
+ 422: {
2346
+ headers: {
2347
+ [name: string]: unknown;
2348
+ };
2349
+ content: {
2350
+ "application/json": components["schemas"]["HTTPValidationError"];
2351
+ };
2352
+ };
2353
+ };
2354
+ };
2355
+ update_product_image_v1_products__id__images__image_id__patch: {
2356
+ parameters: {
2357
+ query?: never;
2358
+ header?: never;
2359
+ path: {
2360
+ id: string;
2361
+ image_id: string;
2362
+ };
2363
+ cookie?: never;
2364
+ };
2365
+ requestBody: {
2366
+ content: {
2367
+ "application/json": components["schemas"]["ProductImagePatch"];
2368
+ };
2369
+ };
2370
+ responses: {
2371
+ /** @description Successful Response */
2372
+ 200: {
2373
+ headers: {
2374
+ [name: string]: unknown;
2375
+ };
2376
+ content: {
2377
+ "application/json": components["schemas"]["ProductImageResponse"];
2378
+ };
2379
+ };
2380
+ /** @description Validation Error */
2381
+ 422: {
2382
+ headers: {
2383
+ [name: string]: unknown;
2384
+ };
2385
+ content: {
2386
+ "application/json": components["schemas"]["HTTPValidationError"];
2387
+ };
2388
+ };
2389
+ };
2390
+ };
2391
+ rescale_product_model_v1_products__id__model_rescale_post: {
2392
+ parameters: {
2393
+ query?: never;
2394
+ header?: never;
2395
+ path: {
2396
+ id: string;
2397
+ };
2398
+ cookie?: never;
2399
+ };
2400
+ requestBody: {
2401
+ content: {
2402
+ "application/json": components["schemas"]["ProductRescaleRequest"];
2403
+ };
2404
+ };
2405
+ responses: {
2406
+ /** @description Successful Response */
2407
+ 200: {
2408
+ headers: {
2409
+ [name: string]: unknown;
2410
+ };
2411
+ content: {
2412
+ "application/json": components["schemas"]["ProductResponse"];
2413
+ };
2414
+ };
2415
+ /** @description Validation Error */
2416
+ 422: {
2417
+ headers: {
2418
+ [name: string]: unknown;
2419
+ };
2420
+ content: {
2421
+ "application/json": components["schemas"]["HTTPValidationError"];
2422
+ };
2423
+ };
2424
+ };
2425
+ };
2426
+ update_product_model_v1_products__id__model_patch: {
2427
+ parameters: {
2428
+ query?: never;
2429
+ header?: never;
2430
+ path: {
2431
+ id: string;
2432
+ };
2433
+ cookie?: never;
2434
+ };
2435
+ requestBody: {
2436
+ content: {
2437
+ "application/json": components["schemas"]["ProductModelPatch"];
2438
+ };
2439
+ };
2440
+ responses: {
2441
+ /** @description Successful Response */
2442
+ 200: {
2443
+ headers: {
2444
+ [name: string]: unknown;
2445
+ };
2446
+ content: {
2447
+ "application/json": components["schemas"]["ProductModelResponse"];
2448
+ };
2449
+ };
2450
+ /** @description Validation Error */
2451
+ 422: {
2452
+ headers: {
2453
+ [name: string]: unknown;
2454
+ };
2455
+ content: {
2456
+ "application/json": components["schemas"]["HTTPValidationError"];
2457
+ };
2458
+ };
2459
+ };
2460
+ };
2461
+ list_api_keys_v1_api_keys_get: {
2462
+ parameters: {
2463
+ query?: never;
2464
+ header?: never;
2465
+ path?: never;
2466
+ cookie?: never;
2467
+ };
2468
+ requestBody?: never;
2469
+ responses: {
2470
+ /** @description Successful Response */
2471
+ 200: {
2472
+ headers: {
2473
+ [name: string]: unknown;
2474
+ };
2475
+ content: {
2476
+ "application/json": components["schemas"]["ApiKeyListResponse"];
2477
+ };
2478
+ };
2479
+ };
2480
+ };
2481
+ create_api_key_v1_api_keys_post: {
2482
+ parameters: {
2483
+ query?: never;
2484
+ header?: never;
2485
+ path?: never;
2486
+ cookie?: never;
2487
+ };
2488
+ requestBody: {
2489
+ content: {
2490
+ "application/json": components["schemas"]["ApiKeyCreateRequest"];
2491
+ };
2492
+ };
2493
+ responses: {
2494
+ /** @description Successful Response */
2495
+ 201: {
2496
+ headers: {
2497
+ [name: string]: unknown;
2498
+ };
2499
+ content: {
2500
+ "application/json": components["schemas"]["ApiKeyCreateResponse"];
2501
+ };
2502
+ };
2503
+ /** @description Validation Error */
2504
+ 422: {
2505
+ headers: {
2506
+ [name: string]: unknown;
2507
+ };
2508
+ content: {
2509
+ "application/json": components["schemas"]["HTTPValidationError"];
2510
+ };
2511
+ };
2512
+ };
2513
+ };
2514
+ revoke_api_key_v1_api_keys__id__delete: {
2515
+ parameters: {
2516
+ query?: never;
2517
+ header?: never;
2518
+ path: {
2519
+ id: string;
2520
+ };
2521
+ cookie?: never;
2522
+ };
2523
+ requestBody?: never;
2524
+ responses: {
2525
+ /** @description Successful Response */
2526
+ 204: {
2527
+ headers: {
2528
+ [name: string]: unknown;
2529
+ };
2530
+ content?: never;
2531
+ };
2532
+ /** @description Validation Error */
2533
+ 422: {
2534
+ headers: {
2535
+ [name: string]: unknown;
2536
+ };
2537
+ content: {
2538
+ "application/json": components["schemas"]["HTTPValidationError"];
2539
+ };
2540
+ };
2541
+ };
2542
+ };
2543
+ get_usage_v1_usage_get: {
2544
+ parameters: {
2545
+ query?: {
2546
+ start?: string | null;
2547
+ end?: string | null;
2548
+ org_id?: string | null;
2549
+ scope?: components["schemas"]["ListScope"];
2550
+ product_id?: string | null;
2551
+ project_id?: string | null;
2552
+ model_id?: string | null;
2553
+ event_type?: components["schemas"]["UsageEventType"] | null;
2554
+ granularity?: "hour" | "day";
2555
+ include_internal?: boolean;
2556
+ group_by?: "product" | null;
2557
+ };
2558
+ header?: never;
2559
+ path?: never;
2560
+ cookie?: never;
2561
+ };
2562
+ requestBody?: never;
2563
+ responses: {
2564
+ /** @description Successful Response */
2565
+ 200: {
2566
+ headers: {
2567
+ [name: string]: unknown;
2568
+ };
2569
+ content: {
2570
+ "application/json": components["schemas"]["UsageResponse"];
2571
+ };
2572
+ };
2573
+ /** @description Validation Error */
2574
+ 422: {
2575
+ headers: {
2576
+ [name: string]: unknown;
2577
+ };
2578
+ content: {
2579
+ "application/json": components["schemas"]["HTTPValidationError"];
2580
+ };
2581
+ };
2582
+ };
2583
+ };
2584
+ ingest_beacon_v1_beacon_post: {
2585
+ parameters: {
2586
+ query?: never;
2587
+ header?: never;
2588
+ path?: never;
2589
+ cookie?: never;
2590
+ };
2591
+ requestBody: {
2592
+ content: {
2593
+ "application/json": components["schemas"]["BeaconRequest"];
2594
+ };
2595
+ };
2596
+ responses: {
2597
+ /** @description Successful Response */
2598
+ 204: {
2599
+ headers: {
2600
+ [name: string]: unknown;
2601
+ };
2602
+ content?: never;
2603
+ };
2604
+ /** @description Validation Error */
2605
+ 422: {
2606
+ headers: {
2607
+ [name: string]: unknown;
2608
+ };
2609
+ content: {
2610
+ "application/json": components["schemas"]["HTTPValidationError"];
2611
+ };
2612
+ };
2613
+ };
2614
+ };
2615
+ list_webhook_endpoints_v1_webhooks_get: {
2616
+ parameters: {
2617
+ query?: never;
2618
+ header?: never;
2619
+ path?: never;
2620
+ cookie?: never;
2621
+ };
2622
+ requestBody?: never;
2623
+ responses: {
2624
+ /** @description Successful Response */
2625
+ 200: {
2626
+ headers: {
2627
+ [name: string]: unknown;
2628
+ };
2629
+ content: {
2630
+ "application/json": components["schemas"]["WebhookEndpointListResponse"];
2631
+ };
2632
+ };
2633
+ };
2634
+ };
2635
+ create_webhook_endpoint_v1_webhooks_post: {
2636
+ parameters: {
2637
+ query?: never;
2638
+ header?: never;
2639
+ path?: never;
2640
+ cookie?: never;
2641
+ };
2642
+ requestBody: {
2643
+ content: {
2644
+ "application/json": components["schemas"]["WebhookEndpointCreateRequest"];
2645
+ };
2646
+ };
2647
+ responses: {
2648
+ /** @description Successful Response */
2649
+ 201: {
2650
+ headers: {
2651
+ [name: string]: unknown;
2652
+ };
2653
+ content: {
2654
+ "application/json": components["schemas"]["WebhookEndpointCreateResponse"];
2655
+ };
2656
+ };
2657
+ /** @description Validation Error */
2658
+ 422: {
2659
+ headers: {
2660
+ [name: string]: unknown;
2661
+ };
2662
+ content: {
2663
+ "application/json": components["schemas"]["HTTPValidationError"];
2664
+ };
2665
+ };
2666
+ };
2667
+ };
2668
+ get_webhook_endpoint_v1_webhooks__id__get: {
2669
+ parameters: {
2670
+ query?: never;
2671
+ header?: never;
2672
+ path: {
2673
+ id: string;
2674
+ };
2675
+ cookie?: never;
2676
+ };
2677
+ requestBody?: never;
2678
+ responses: {
2679
+ /** @description Successful Response */
2680
+ 200: {
2681
+ headers: {
2682
+ [name: string]: unknown;
2683
+ };
2684
+ content: {
2685
+ "application/json": components["schemas"]["WebhookEndpointResponse"];
2686
+ };
2687
+ };
2688
+ /** @description Validation Error */
2689
+ 422: {
2690
+ headers: {
2691
+ [name: string]: unknown;
2692
+ };
2693
+ content: {
2694
+ "application/json": components["schemas"]["HTTPValidationError"];
2695
+ };
2696
+ };
2697
+ };
2698
+ };
2699
+ delete_webhook_endpoint_v1_webhooks__id__delete: {
2700
+ parameters: {
2701
+ query?: never;
2702
+ header?: never;
2703
+ path: {
2704
+ id: string;
2705
+ };
2706
+ cookie?: never;
2707
+ };
2708
+ requestBody?: never;
2709
+ responses: {
2710
+ /** @description Successful Response */
2711
+ 204: {
2712
+ headers: {
2713
+ [name: string]: unknown;
2714
+ };
2715
+ content?: never;
2716
+ };
2717
+ /** @description Validation Error */
2718
+ 422: {
2719
+ headers: {
2720
+ [name: string]: unknown;
2721
+ };
2722
+ content: {
2723
+ "application/json": components["schemas"]["HTTPValidationError"];
2724
+ };
2725
+ };
2726
+ };
2727
+ };
2728
+ update_webhook_endpoint_v1_webhooks__id__patch: {
2729
+ parameters: {
2730
+ query?: never;
2731
+ header?: never;
2732
+ path: {
2733
+ id: string;
2734
+ };
2735
+ cookie?: never;
2736
+ };
2737
+ requestBody: {
2738
+ content: {
2739
+ "application/json": components["schemas"]["WebhookEndpointUpdateRequest"];
2740
+ };
2741
+ };
2742
+ responses: {
2743
+ /** @description Successful Response */
2744
+ 200: {
2745
+ headers: {
2746
+ [name: string]: unknown;
2747
+ };
2748
+ content: {
2749
+ "application/json": components["schemas"]["WebhookEndpointResponse"];
2750
+ };
2751
+ };
2752
+ /** @description Validation Error */
2753
+ 422: {
2754
+ headers: {
2755
+ [name: string]: unknown;
2756
+ };
2757
+ content: {
2758
+ "application/json": components["schemas"]["HTTPValidationError"];
2759
+ };
2760
+ };
2761
+ };
2762
+ };
2763
+ list_webhook_deliveries_v1_webhooks__id__deliveries_get: {
2764
+ parameters: {
2765
+ query?: {
2766
+ limit?: number;
2767
+ offset?: number;
2768
+ };
2769
+ header?: never;
2770
+ path: {
2771
+ id: string;
2772
+ };
2773
+ cookie?: never;
2774
+ };
2775
+ requestBody?: never;
2776
+ responses: {
2777
+ /** @description Successful Response */
2778
+ 200: {
2779
+ headers: {
2780
+ [name: string]: unknown;
2781
+ };
2782
+ content: {
2783
+ "application/json": components["schemas"]["WebhookDeliveryListResponse"];
2784
+ };
2785
+ };
2786
+ /** @description Validation Error */
2787
+ 422: {
2788
+ headers: {
2789
+ [name: string]: unknown;
2790
+ };
2791
+ content: {
2792
+ "application/json": components["schemas"]["HTTPValidationError"];
2793
+ };
2794
+ };
2795
+ };
2796
+ };
2797
+ list_projects_v1_projects_get: {
2798
+ parameters: {
2799
+ query?: never;
2800
+ header?: never;
2801
+ path?: never;
2802
+ cookie?: never;
2803
+ };
2804
+ requestBody?: never;
2805
+ responses: {
2806
+ /** @description Successful Response */
2807
+ 200: {
2808
+ headers: {
2809
+ [name: string]: unknown;
2810
+ };
2811
+ content: {
2812
+ "application/json": components["schemas"]["ProjectListResponse"];
2813
+ };
2814
+ };
2815
+ };
2816
+ };
2817
+ get_project_v1_projects__id__get: {
2818
+ parameters: {
2819
+ query?: never;
2820
+ header?: never;
2821
+ path: {
2822
+ id: string;
2823
+ };
2824
+ cookie?: never;
2825
+ };
2826
+ requestBody?: never;
2827
+ responses: {
2828
+ /** @description Successful Response */
2829
+ 200: {
2830
+ headers: {
2831
+ [name: string]: unknown;
2832
+ };
2833
+ content: {
2834
+ "application/json": components["schemas"]["ProjectResponse"];
2835
+ };
2836
+ };
2837
+ /** @description Validation Error */
2838
+ 422: {
2839
+ headers: {
2840
+ [name: string]: unknown;
2841
+ };
2842
+ content: {
2843
+ "application/json": components["schemas"]["HTTPValidationError"];
2844
+ };
2845
+ };
2846
+ };
2847
+ };
2848
+ health_v1_health_get: {
2849
+ parameters: {
2850
+ query?: never;
2851
+ header?: never;
2852
+ path?: never;
2853
+ cookie?: never;
2854
+ };
2855
+ requestBody?: never;
2856
+ responses: {
2857
+ /** @description Successful Response */
2858
+ 200: {
2859
+ headers: {
2860
+ [name: string]: unknown;
2861
+ };
2862
+ content: {
2863
+ "application/json": components["schemas"]["HealthResponse"];
2864
+ };
2865
+ };
2866
+ };
2867
+ };
47
2868
  }
48
2869
 
49
2870
  /**
50
- * Public types for the Viewrium SDK.
2871
+ * Typed errors for the Viewrium SDK.
51
2872
  *
52
- * Response shapes mirror the backend JSON contract (snake_case). Request
53
- * param interfaces use camelCase; the SDK maps camelCase → snake_case on
54
- * the wire.
2873
+ * The API returns one error envelope: `{ "error": { code, message, details } }`.
2874
+ * Non-2xx responses are parsed into a `ViewriumError` subclass keyed by HTTP status;
2875
+ * consumers branch with `instanceof`.
55
2876
  */
56
- interface ViewriumOptions {
57
- /** Override the API key. Defaults to the value passed to `new Viewrium(...)`. */
58
- apiKey?: string;
59
- /** Base URL. Defaults to `process.env.VIEWRIUM_API_BASE` or `https://api.viewrium.com`. */
60
- apiBase?: string;
61
- /** Per-request timeout in milliseconds. Default 60_000. */
62
- timeoutMs?: number;
63
- /** Retries on 429 / 5xx. Default 3. */
64
- maxRetries?: number;
65
- /** Override `globalThis.fetch` (test seams / custom transports). */
66
- fetch?: typeof fetch;
67
- }
68
- type SizeUnit = "cm" | "in";
69
- type ModelStatus = "uploading" | "processing" | "queueing" | "successful" | "failed" | "expired";
70
- type SourceType = "scan" | "upload" | "image";
71
- type UsageEventType = "generation.created" | "generation.completed" | "generation.failed" | "view" | "ar_enter" | "ar_session_ended";
72
- interface Model {
73
- id: string;
74
- project_id: string | null;
75
- custom_id: string | null;
76
- title: string | null;
77
- description: string | null;
78
- price: number | null;
79
- currency: string | null;
80
- status: ModelStatus;
81
- source_type: SourceType | null;
82
- model_url: string | null;
83
- thumbnail_url: string | null;
84
- height: number | null;
85
- width: number | null;
86
- depth: number | null;
87
- scale_factor: number | null;
88
- tags: string[];
89
- metadata: Record<string, unknown>;
90
- display_enabled: boolean;
91
- created_at: string;
92
- completed_at: string | null;
93
- [key: string]: unknown;
94
- }
95
- interface ModelListResponse {
96
- data: Model[];
97
- limit: number;
98
- offset: number;
2877
+ interface ViewriumErrorOptions {
2878
+ status?: number | null;
2879
+ code?: string;
2880
+ details?: unknown;
2881
+ cause?: unknown;
99
2882
  }
100
- interface ModelEvent {
101
- id: string;
102
- event_type: UsageEventType;
103
- timestamp: string;
104
- metadata: Record<string, unknown>;
105
- }
106
- interface ModelEventListResponse {
107
- data: ModelEvent[];
108
- limit: number;
109
- offset: number;
110
- }
111
- interface DeletedResponse {
112
- id: string;
113
- deleted: true;
2883
+ declare class ViewriumError extends Error {
2884
+ /** HTTP status, or `null` for transport/connection failures. */
2885
+ readonly status: number | null;
2886
+ /** Machine-readable code from the error envelope. */
2887
+ readonly code: string;
2888
+ /** Optional structured details from the error envelope. */
2889
+ readonly details: unknown;
2890
+ constructor(message: string, options?: ViewriumErrorOptions);
114
2891
  }
115
- interface ApiKey {
116
- id: string;
117
- name: string | null;
118
- key_type: "secret" | "publishable";
119
- key_prefix: string;
120
- last_used_at: string | null;
121
- revoked_at: string | null;
122
- created_at: string;
2892
+ /** 400 - malformed request. */
2893
+ declare class BadRequestError extends ViewriumError {
123
2894
  }
124
- /** Returned by `apiKeys.create` - `key` is plaintext, shown once. */
125
- interface ApiKeyWithSecret extends Omit<ApiKey, "last_used_at" | "revoked_at"> {
126
- key: string;
2895
+ /** 401 - missing or invalid API key. */
2896
+ declare class AuthenticationError extends ViewriumError {
127
2897
  }
128
- interface ApiKeyListResponse {
129
- data: ApiKey[];
2898
+ /**
2899
+ * 402 - the organization's plan does not allow this: no plan, a used free trial, a failed payment,
2900
+ * or past the plan's active-product cap. `details.reason` says which.
2901
+ */
2902
+ declare class PaymentRequiredError extends ViewriumError {
130
2903
  }
131
- interface RevokedResponse {
132
- id: string;
133
- revoked: true;
2904
+ /** 403 - the key is valid but not allowed (scope, origin, or role). */
2905
+ declare class PermissionError extends ViewriumError {
134
2906
  }
135
- interface UsageSummary {
136
- org_id: string;
137
- plan_tier: "free" | "pro" | "enterprise";
138
- since: string | null;
139
- until: string | null;
140
- counts: Partial<Record<UsageEventType, number>>;
2907
+ /** 404 - no such resource. */
2908
+ declare class NotFoundError extends ViewriumError {
141
2909
  }
142
- interface TagSummary {
143
- tag: string;
144
- count: number;
2910
+ /** 409 - conflict (e.g. duplicate `custom_id`, model not in a rescalable state). */
2911
+ declare class ConflictError extends ViewriumError {
145
2912
  }
146
- interface TagListResponse {
147
- data: TagSummary[];
2913
+ /** 422 - request failed validation. */
2914
+ declare class UnprocessableEntityError extends ViewriumError {
148
2915
  }
149
- interface ModelCreateParams {
150
- customId?: string;
151
- title?: string;
152
- description?: string;
153
- /** Provide one of `images` (URL list) or `sourceUrl` - not both. */
154
- images?: string[];
155
- /** Product page URL. Server extracts candidate images. */
156
- sourceUrl?: string;
157
- height?: number;
158
- sizeUnit?: SizeUnit;
159
- tags?: string[];
160
- price?: number;
161
- currency?: string;
162
- metadata?: Record<string, unknown>;
163
- displayEnabled?: boolean;
2916
+ /** 429 - rate limited, or not enough credits (`code: "quota_exceeded"`, `details.shortfall_credits`). */
2917
+ declare class RateLimitError extends ViewriumError {
164
2918
  }
165
- interface ModelCreateFromImagesParams {
166
- title?: string;
167
- customId?: string;
168
- description?: string;
169
- height?: number;
170
- sizeUnit?: SizeUnit;
171
- tags?: string[];
172
- displayEnabled?: boolean;
2919
+ /** 5xx - the API failed to handle the request. */
2920
+ declare class ServerError extends ViewriumError {
173
2921
  }
174
- interface ModelCreateFromVideoParams extends ModelCreateFromImagesParams {
2922
+ /** The request never reached the API (DNS/TLS/offline/abort). */
2923
+ declare class ConnectionError extends ViewriumError {
175
2924
  }
176
- interface ModelUpdateParams {
177
- customId?: string;
178
- title?: string;
179
- description?: string;
180
- price?: number;
181
- currency?: string;
182
- height?: number;
183
- sizeUnit?: SizeUnit;
184
- tags?: string[];
185
- metadata?: Record<string, unknown>;
186
- displayEnabled?: boolean;
2925
+ interface ApiErrorPayload {
2926
+ code: string;
2927
+ message: string;
2928
+ details?: unknown;
187
2929
  }
188
- interface ModelListParams {
189
- tag?: string;
190
- tags?: string[] | string;
191
- customId?: string;
192
- status?: ModelStatus;
193
- limit?: number;
194
- offset?: number;
2930
+ /** Build the right `ViewriumError` subclass for an HTTP status + parsed envelope. */
2931
+ declare function createApiError(status: number, payload: ApiErrorPayload): ViewriumError;
2932
+
2933
+ type JsonContent<T> = T extends {
2934
+ content: {
2935
+ "application/json": infer B;
2936
+ };
2937
+ } ? B : never;
2938
+ /** GET /v1/products/{id} resolves to one of two shapes by principal: the full product for a
2939
+ * session or `sk_` key, the allow-listed public projection for a `pk_` key. */
2940
+ type ProductGetResult = JsonContent<paths["/v1/products/{id}"]["get"]["responses"][200]>;
2941
+ /** A product with its assets (`sk_` view). */
2942
+ type Product = Extract<ProductGetResult, {
2943
+ org_id: string;
2944
+ }>;
2945
+ /** The `pk_` view of a published product: id, title, model_url, thumbnail_url, model_status. */
2946
+ type PublicProduct = Exclude<ProductGetResult, {
2947
+ org_id: string;
2948
+ }>;
2949
+ type ProductList = JsonContent<paths["/v1/products"]["get"]["responses"][200]>;
2950
+ /** A catalog row - the product plus what its card needs, with no nested assets. */
2951
+ type ProductSummary = ProductList["data"][number];
2952
+ type ProductListParams = NonNullable<paths["/v1/products"]["get"]["parameters"]["query"]>;
2953
+ type ProductCreateParams = JsonContent<paths["/v1/products"]["post"]["requestBody"]>;
2954
+ /** POST /v1/products 200 - the gate parked; resume by POSTing `resume_token`. */
2955
+ type ProductParked = JsonContent<paths["/v1/products"]["post"]["responses"][200]>;
2956
+ /** One page position of a parked plan. Both park shapes carry the same row type. */
2957
+ type PlannedView = NonNullable<ProductParked["plan"]>[number];
2958
+ /** describe's structured read of the product (D25), with its verbatim reasons. */
2959
+ type GateFlags = ProductParked["flags"];
2960
+ /** The models layer's verdict: whether a model is allowed, what blocks it, and the views it
2961
+ * would be built from - including `unseen_views`, the invented ones. */
2962
+ type ModelPlan = ProductParked["model"];
2963
+ /** The priced bundle, in credits (1 credit = $0.50): 1 per deliverable image, a flat 10 for a model. */
2964
+ type ProductCost = ProductParked["cost"];
2965
+ /** POST /v1/products 202 - a committed product whose run has started. */
2966
+ type ProductCreated = JsonContent<paths["/v1/products"]["post"]["responses"][202]>;
2967
+ /** POST /v1/products resolves to one of the two (discriminate on `status === "parked"`). */
2968
+ type ProductCreateResult = ProductParked | ProductCreated;
2969
+ type ProductUpdateParams = JsonContent<NonNullable<paths["/v1/products/{id}"]["patch"]["requestBody"]>>;
2970
+ type ProductBatchResult = JsonContent<paths["/v1/products"]["delete"]["responses"][200]>;
2971
+ type ProductPublishParams = JsonContent<paths["/v1/products/publish"]["post"]["requestBody"]>;
2972
+ type Tags = JsonContent<paths["/v1/products/tags"]["get"]["responses"][200]>;
2973
+ type TagsParams = NonNullable<paths["/v1/products/tags"]["get"]["parameters"]["query"]>;
2974
+ /** POST /v1/products/{id}/review - the write-behind flush (image + model decisions, the
2975
+ * product's user-set status). It records decisions only. */
2976
+ type ProductReviewParams = JsonContent<paths["/v1/products/{id}/review"]["post"]["requestBody"]>;
2977
+ type ProductReviewResult = JsonContent<paths["/v1/products/{id}/review"]["post"]["responses"][200]>;
2978
+ type ProductImagePatchParams = JsonContent<NonNullable<paths["/v1/products/{id}/images/{image_id}"]["patch"]["requestBody"]>>;
2979
+ type ProductImage = JsonContent<paths["/v1/products/{id}/images/{image_id}"]["patch"]["responses"][200]>;
2980
+ type ProductModelPatchParams = JsonContent<NonNullable<paths["/v1/products/{id}/model"]["patch"]["requestBody"]>>;
2981
+ type ProductModel = JsonContent<paths["/v1/products/{id}/model"]["patch"]["responses"][200]>;
2982
+ type ProductRescaleParams = JsonContent<paths["/v1/products/{id}/model/rescale"]["post"]["requestBody"]>;
2983
+ /** POST /v1/products/{id}/order - the missing half; parks like a create, commits through
2984
+ * `products.create({ resume_token })`. */
2985
+ type ProductOrderParams = JsonContent<paths["/v1/products/{id}/order"]["post"]["requestBody"]>;
2986
+ type ProductOrderParked = JsonContent<paths["/v1/products/{id}/order"]["post"]["responses"][200]>;
2987
+ type ProductExportParams = NonNullable<paths["/v1/products/export"]["get"]["parameters"]["query"]>;
2988
+ /** 202 from the export: the images are being finished in the background and a download link
2989
+ * is emailed; the same request streams the ZIP once they are done. */
2990
+ type ProductExportQueued = JsonContent<paths["/v1/products/export"]["get"]["responses"][202]>;
2991
+ type ApiKeyList = JsonContent<paths["/v1/api_keys"]["get"]["responses"][200]>;
2992
+ type ApiKeyCreateParams = JsonContent<paths["/v1/api_keys"]["post"]["requestBody"]>;
2993
+ type ApiKeyCreated = JsonContent<paths["/v1/api_keys"]["post"]["responses"][201]>;
2994
+ type Usage = JsonContent<paths["/v1/usage"]["get"]["responses"][200]>;
2995
+ type UsageParams = NonNullable<paths["/v1/usage"]["get"]["parameters"]["query"]>;
2996
+ /** One `group_by: "product"` row: a product's counts over the window, with its title. */
2997
+ type UsageProductTotals = NonNullable<Usage["by_product"]>[number];
2998
+ type WebhookEndpoint = JsonContent<paths["/v1/webhooks/{id}"]["get"]["responses"][200]>;
2999
+ type WebhookEndpointList = JsonContent<paths["/v1/webhooks"]["get"]["responses"][200]>;
3000
+ type WebhookEndpointCreateParams = JsonContent<paths["/v1/webhooks"]["post"]["requestBody"]>;
3001
+ type WebhookEndpointCreated = JsonContent<paths["/v1/webhooks"]["post"]["responses"][201]>;
3002
+ type WebhookEndpointUpdateParams = JsonContent<NonNullable<paths["/v1/webhooks/{id}"]["patch"]["requestBody"]>>;
3003
+ type WebhookDeliveryList = JsonContent<paths["/v1/webhooks/{id}/deliveries"]["get"]["responses"][200]>;
3004
+ type WebhookDeliveriesParams = NonNullable<paths["/v1/webhooks/{id}/deliveries"]["get"]["parameters"]["query"]>;
3005
+ type Project = JsonContent<paths["/v1/projects/{id}"]["get"]["responses"][200]>;
3006
+ type ProjectList = JsonContent<paths["/v1/projects"]["get"]["responses"][200]>;
3007
+ type BeaconParams = JsonContent<paths["/v1/beacon"]["post"]["requestBody"]>;
3008
+ type Health = JsonContent<paths["/v1/health"]["get"]["responses"][200]>;
3009
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
3010
+ interface ViewriumOptions {
3011
+ /** `sk_` secret key (server) or `pk_` publishable key (browser). */
3012
+ apiKey: string;
3013
+ /** API origin; defaults to `https://api.viewrium.com`. Trailing slash is stripped. */
3014
+ baseUrl?: string;
3015
+ /** Injectable fetch (tests, custom agents). Defaults to `globalThis.fetch`. */
3016
+ fetch?: FetchLike;
195
3017
  }
196
- interface ModelListEventsParams {
197
- limit?: number;
198
- offset?: number;
3018
+ type QueryValue = string | number | boolean | readonly string[] | null | undefined;
3019
+ type QueryParams = Record<string, QueryValue>;
3020
+ interface RequestOptions {
3021
+ query?: QueryParams;
3022
+ body?: unknown;
199
3023
  }
200
- interface ApiKeyCreateParams {
201
- name?: string;
3024
+ type RequestFn = <T>(method: string, path: string, options?: RequestOptions) => Promise<T>;
3025
+ /** The escape hatch for the one route that answers bytes rather than JSON (the export ZIP) -
3026
+ * or, on a 202, JSON after all. */
3027
+ type BlobRequestFn = (method: string, path: string, options?: RequestOptions) => Promise<Blob | ProductExportQueued>;
3028
+ declare function createProducts(request: RequestFn, requestBlob: BlobRequestFn): {
202
3029
  /**
203
- * Type of key to create. Defaults to `"secret"`.
204
- * - `"secret"` (sk_*): server-only, authenticates /v1/* calls.
205
- * - `"publishable"` (pk_*): client-safe, used by the embed tag. Origin
206
- * restrictions inherit from the project's allowed_domains.
3030
+ * POST /v1/products - the single generation entrypoint. Supply exactly one source
3031
+ * (`image_paths` | `image_urls` | `video_url` | `source_url` | `model_url` |
3032
+ * `resume_token`). A fresh request ALWAYS parks (200, `status === "parked"`, nothing
3033
+ * written): the plan, the model verdict and the price come back inside `resume_token`,
3034
+ * which you POST back to commit (202, `ProductCreated`). A `model_url` commits at once.
207
3035
  */
208
- keyType?: "secret" | "publishable";
209
- }
210
- interface UsageRetrieveParams {
211
- /** ISO 8601 lower bound (inclusive). */
212
- since?: string;
213
- /** ISO 8601 upper bound (inclusive). */
214
- until?: string;
215
- }
216
- /**
217
- * A file passed to multipart endpoints.
218
- *
219
- * - In Node: a path string (SDK reads it via `fs.readFile`) or a `Blob`/`File`.
220
- * - In browsers / edge runtimes: `Blob` / `File` (native).
221
- * - Anywhere: a `{ data, filename, contentType? }` record with raw bytes.
222
- */
223
- type FileInput = string | Blob | {
224
- data: Uint8Array | ArrayBuffer | Blob;
225
- filename?: string;
226
- contentType?: string;
3036
+ create: (params: ProductCreateParams) => Promise<ProductCreateResult>;
3037
+ /**
3038
+ * GET /v1/products - the catalog: one org (`org_id`, or the caller's only org), or
3039
+ * `scope: "all"` for every org an agency member or staff reaches. Filters AND together.
3040
+ */
3041
+ list: (params?: ProductListParams) => Promise<{
3042
+ data: components["schemas"]["ProductSummary"][];
3043
+ total: number;
3044
+ limit: number;
3045
+ offset: number;
3046
+ }>;
3047
+ /** GET /v1/products/{id} - the product with its assets (a `pk_` key gets `PublicProduct`). */
3048
+ retrieve: (id: string) => Promise<{
3049
+ id: string;
3050
+ org_id: string;
3051
+ title: string;
3052
+ custom_id?: string | null;
3053
+ tags?: string[];
3054
+ status: components["schemas"]["ProductStatus"];
3055
+ display_status: components["schemas"]["DisplayStatus"];
3056
+ published: boolean;
3057
+ form?: string | null;
3058
+ source_type?: components["schemas"]["SourceType"] | null;
3059
+ price?: number | null;
3060
+ currency?: string | null;
3061
+ height?: number | null;
3062
+ width?: number | null;
3063
+ depth?: number | null;
3064
+ display_unit?: components["schemas"]["DimensionUnit"] | null;
3065
+ images_ordered: boolean;
3066
+ thumbnail_url?: string | null;
3067
+ has_warnings: boolean;
3068
+ warning_count: number;
3069
+ comment_count: number;
3070
+ image_count: number;
3071
+ model_status?: components["schemas"]["AssetStatus"] | null;
3072
+ owner?: components["schemas"]["ProductOwner"] | null;
3073
+ created_at: string;
3074
+ updated_at?: string | null;
3075
+ description?: string | null;
3076
+ wall_mounted: boolean;
3077
+ product_type?: string | null;
3078
+ material_words?: string | null;
3079
+ transparent: boolean;
3080
+ metadata?: {
3081
+ [key: string]: unknown;
3082
+ };
3083
+ sources?: components["schemas"]["ProductSourceResponse"][];
3084
+ images?: components["schemas"]["ProductImageResponse"][];
3085
+ model?: components["schemas"]["ProductModelResponse"] | null;
3086
+ warnings?: components["schemas"]["ProductWarning"][];
3087
+ open_request_count: number;
3088
+ }>;
3089
+ /**
3090
+ * PATCH /v1/products/{id} - the customer's fields, the user-set status and the publish
3091
+ * toggle. 409 on a `custom_id` another product in the org already holds.
3092
+ */
3093
+ update: (id: string, params: ProductUpdateParams) => Promise<{
3094
+ id: string;
3095
+ org_id: string;
3096
+ title: string;
3097
+ custom_id?: string | null;
3098
+ tags?: string[];
3099
+ status: components["schemas"]["ProductStatus"];
3100
+ display_status: components["schemas"]["DisplayStatus"];
3101
+ published: boolean;
3102
+ form?: string | null;
3103
+ source_type?: components["schemas"]["SourceType"] | null;
3104
+ price?: number | null;
3105
+ currency?: string | null;
3106
+ height?: number | null;
3107
+ width?: number | null;
3108
+ depth?: number | null;
3109
+ display_unit?: components["schemas"]["DimensionUnit"] | null;
3110
+ images_ordered: boolean;
3111
+ thumbnail_url?: string | null;
3112
+ has_warnings: boolean;
3113
+ warning_count: number;
3114
+ comment_count: number;
3115
+ image_count: number;
3116
+ model_status?: components["schemas"]["AssetStatus"] | null;
3117
+ owner?: components["schemas"]["ProductOwner"] | null;
3118
+ created_at: string;
3119
+ updated_at?: string | null;
3120
+ description?: string | null;
3121
+ wall_mounted: boolean;
3122
+ product_type?: string | null;
3123
+ material_words?: string | null;
3124
+ transparent: boolean;
3125
+ metadata?: {
3126
+ [key: string]: unknown;
3127
+ };
3128
+ sources?: components["schemas"]["ProductSourceResponse"][];
3129
+ images?: components["schemas"]["ProductImageResponse"][];
3130
+ model?: components["schemas"]["ProductModelResponse"] | null;
3131
+ warnings?: components["schemas"]["ProductWarning"][];
3132
+ open_request_count: number;
3133
+ }>;
3134
+ /** DELETE /v1/products/{id} */
3135
+ delete: (id: string) => Promise<void>;
3136
+ /** DELETE /v1/products?ids=... - per-id affected/not_found/forbidden buckets. */
3137
+ batchDelete: (ids: readonly string[]) => Promise<{
3138
+ affected: string[];
3139
+ not_found: string[];
3140
+ forbidden: string[];
3141
+ }>;
3142
+ /** POST /v1/products/publish - the bulk publish toggle; one product toggles through `update`. */
3143
+ publish: (params: ProductPublishParams) => Promise<{
3144
+ affected: string[];
3145
+ not_found: string[];
3146
+ forbidden: string[];
3147
+ }>;
3148
+ /** GET /v1/products/tags - distinct tags across the same scope the list takes. */
3149
+ tags: (params?: TagsParams) => Promise<{
3150
+ data: components["schemas"]["TagCount"][];
3151
+ }>;
3152
+ /** POST /v1/products/{id}/review - the review flush: image and model decisions plus the
3153
+ * product's user-set status, validated together and applied in order. */
3154
+ review: (id: string, params: ProductReviewParams) => Promise<{
3155
+ product_id: string;
3156
+ applied: number;
3157
+ display_status: components["schemas"]["DisplayStatus"];
3158
+ }>;
3159
+ /** PATCH /v1/products/{id}/images/{image_id} - one image's decision and overrides. */
3160
+ updateImage: (id: string, imageId: string, params: ProductImagePatchParams) => Promise<{
3161
+ id: string;
3162
+ view: string;
3163
+ kind: "wanted" | "extra";
3164
+ stands_for: string;
3165
+ status: components["schemas"]["AssetStatus"];
3166
+ skip_reason?: string | null;
3167
+ route?: ("reuse" | "edit" | "generate") | null;
3168
+ source?: ("photograph" | "edited" | "generated") | null;
3169
+ evidence?: string | null;
3170
+ azimuth?: string | null;
3171
+ tilt?: string | null;
3172
+ source_photo_id?: string | null;
3173
+ attempts: number;
3174
+ flagged: boolean;
3175
+ cut_flagged: boolean;
3176
+ cut_problems?: string | null;
3177
+ review_status: components["schemas"]["ReviewStatus"];
3178
+ filename: string;
3179
+ alt_text: string;
3180
+ filename_override?: string | null;
3181
+ alt_override?: string | null;
3182
+ shadow_override?: components["schemas"]["ShadowOverride"] | null;
3183
+ background_override?: number[] | null;
3184
+ frame_scale?: number | null;
3185
+ contact_regions?: number[][];
3186
+ raw_url?: string | null;
3187
+ cutout_url?: string | null;
3188
+ warnings?: components["schemas"]["ProductWarning"][];
3189
+ created_at: string;
3190
+ updated_at?: string | null;
3191
+ }>;
3192
+ /** PATCH /v1/products/{id}/model - the model's review decision. */
3193
+ updateModel: (id: string, params: ProductModelPatchParams) => Promise<{
3194
+ id: string;
3195
+ status: components["schemas"]["AssetStatus"];
3196
+ review_status: components["schemas"]["ReviewStatus"];
3197
+ glb_url?: string | null;
3198
+ thumbnail_url?: string | null;
3199
+ scale_factor?: number | null;
3200
+ width_axis?: ("x" | "z") | null;
3201
+ flags?: components["schemas"]["GateFlags"];
3202
+ warnings?: components["schemas"]["ProductWarning"][];
3203
+ created_at: string;
3204
+ completed_at?: string | null;
3205
+ }>;
3206
+ /**
3207
+ * POST /v1/products/{id}/model/rescale - re-apply real-world scale to the GLB in place.
3208
+ * Every supplied axis is applied to the geometry; `unit` covers them (omitted = cm) and is
3209
+ * remembered as the product's `display_unit`. The result carries the ACHIEVED dimensions.
3210
+ */
3211
+ rescale: (id: string, params: ProductRescaleParams) => Promise<{
3212
+ id: string;
3213
+ org_id: string;
3214
+ title: string;
3215
+ custom_id?: string | null;
3216
+ tags?: string[];
3217
+ status: components["schemas"]["ProductStatus"];
3218
+ display_status: components["schemas"]["DisplayStatus"];
3219
+ published: boolean;
3220
+ form?: string | null;
3221
+ source_type?: components["schemas"]["SourceType"] | null;
3222
+ price?: number | null;
3223
+ currency?: string | null;
3224
+ height?: number | null;
3225
+ width?: number | null;
3226
+ depth?: number | null;
3227
+ display_unit?: components["schemas"]["DimensionUnit"] | null;
3228
+ images_ordered: boolean;
3229
+ thumbnail_url?: string | null;
3230
+ has_warnings: boolean;
3231
+ warning_count: number;
3232
+ comment_count: number;
3233
+ image_count: number;
3234
+ model_status?: components["schemas"]["AssetStatus"] | null;
3235
+ owner?: components["schemas"]["ProductOwner"] | null;
3236
+ created_at: string;
3237
+ updated_at?: string | null;
3238
+ description?: string | null;
3239
+ wall_mounted: boolean;
3240
+ product_type?: string | null;
3241
+ material_words?: string | null;
3242
+ transparent: boolean;
3243
+ metadata?: {
3244
+ [key: string]: unknown;
3245
+ };
3246
+ sources?: components["schemas"]["ProductSourceResponse"][];
3247
+ images?: components["schemas"]["ProductImageResponse"][];
3248
+ model?: components["schemas"]["ProductModelResponse"] | null;
3249
+ warnings?: components["schemas"]["ProductWarning"][];
3250
+ open_request_count: number;
3251
+ }>;
3252
+ /** POST /v1/products/{id}/order - park the missing half (images or a model); commit the
3253
+ * returned `resume_token` with `create`. */
3254
+ order: (id: string, params: ProductOrderParams) => Promise<{
3255
+ status: "parked";
3256
+ resume_token: string;
3257
+ expires_at: string;
3258
+ order: components["schemas"]["ProductOrder"];
3259
+ flags: components["schemas"]["GateFlags"];
3260
+ plan?: components["schemas"]["PlannedView"][];
3261
+ model: components["schemas"]["ModelPlanResponse"];
3262
+ cost: components["schemas"]["ProductCost"];
3263
+ warnings?: string[];
3264
+ generate_unseen: boolean;
3265
+ force_model: boolean;
3266
+ }>;
3267
+ /** GET /v1/products/export - a ZIP of finished images (never the 3D model), resolved as a
3268
+ * `Blob`; or `ProductExportQueued` (202) when the images are being finished in the
3269
+ * background and a download link is emailed - repeat the request later to stream it. */
3270
+ export: (params?: ProductExportParams) => Promise<{
3271
+ status: "queued";
3272
+ products: number;
3273
+ images: number;
3274
+ to_finish: number;
3275
+ notify_email?: string | null;
3276
+ } | Blob>;
3277
+ };
3278
+ declare function createApiKeys(request: RequestFn): {
3279
+ /** GET /v1/api_keys */
3280
+ list: () => Promise<{
3281
+ data: components["schemas"]["ApiKeyResponse"][];
3282
+ }>;
3283
+ /** POST /v1/api_keys - the full key is returned exactly once. */
3284
+ create: (params: ApiKeyCreateParams) => Promise<{
3285
+ id: string;
3286
+ org_id?: string | null;
3287
+ project_id?: string | null;
3288
+ key_type: components["schemas"]["KeyType"];
3289
+ key_prefix: string;
3290
+ name?: string | null;
3291
+ scopes: string[];
3292
+ last_used_at?: string | null;
3293
+ revoked_at?: string | null;
3294
+ created_at: string;
3295
+ key: string;
3296
+ }>;
3297
+ /** DELETE /v1/api_keys/{id} - soft revoke. */
3298
+ revoke: (id: string) => Promise<void>;
3299
+ };
3300
+ declare function createUsage(request: RequestFn): {
3301
+ /** GET /v1/usage - buckets and totals over the window; `group_by: "product"` adds the
3302
+ * window's best-performing products in `by_product`. */
3303
+ get: (params?: UsageParams) => Promise<{
3304
+ start: string;
3305
+ end: string;
3306
+ granularity: "hour" | "day";
3307
+ buckets: components["schemas"]["UsageBucket"][];
3308
+ totals: {
3309
+ [key: string]: number;
3310
+ };
3311
+ durations?: {
3312
+ [key: string]: components["schemas"]["UsageDuration"];
3313
+ };
3314
+ by_product?: components["schemas"]["UsageProductTotals"][];
3315
+ }>;
3316
+ };
3317
+ declare function createWebhooks(request: RequestFn): {
3318
+ /** GET /v1/webhooks */
3319
+ list: () => Promise<{
3320
+ data: components["schemas"]["WebhookEndpointResponse"][];
3321
+ }>;
3322
+ /** POST /v1/webhooks - includes the signing secret. */
3323
+ create: (params: WebhookEndpointCreateParams) => Promise<{
3324
+ id: string;
3325
+ org_id?: string | null;
3326
+ project_id?: string | null;
3327
+ url: string;
3328
+ description?: string | null;
3329
+ events: components["schemas"]["WebhookEventType"][];
3330
+ enabled: boolean;
3331
+ created_at: string;
3332
+ updated_at: string;
3333
+ signing_secret: string;
3334
+ }>;
3335
+ /** GET /v1/webhooks/{id} */
3336
+ retrieve: (id: string) => Promise<{
3337
+ id: string;
3338
+ org_id?: string | null;
3339
+ project_id?: string | null;
3340
+ url: string;
3341
+ description?: string | null;
3342
+ events: components["schemas"]["WebhookEventType"][];
3343
+ enabled: boolean;
3344
+ created_at: string;
3345
+ updated_at: string;
3346
+ }>;
3347
+ /** PATCH /v1/webhooks/{id} */
3348
+ update: (id: string, params: WebhookEndpointUpdateParams) => Promise<{
3349
+ id: string;
3350
+ org_id?: string | null;
3351
+ project_id?: string | null;
3352
+ url: string;
3353
+ description?: string | null;
3354
+ events: components["schemas"]["WebhookEventType"][];
3355
+ enabled: boolean;
3356
+ created_at: string;
3357
+ updated_at: string;
3358
+ }>;
3359
+ /** DELETE /v1/webhooks/{id} */
3360
+ delete: (id: string) => Promise<void>;
3361
+ /** GET /v1/webhooks/{id}/deliveries */
3362
+ listDeliveries: (id: string, params?: WebhookDeliveriesParams) => Promise<{
3363
+ data: components["schemas"]["WebhookDeliveryResponse"][];
3364
+ limit: number;
3365
+ offset: number;
3366
+ }>;
3367
+ };
3368
+ declare function createProjects(request: RequestFn): {
3369
+ /** GET /v1/projects */
3370
+ list: () => Promise<{
3371
+ data: components["schemas"]["ProjectResponse"][];
3372
+ }>;
3373
+ /** GET /v1/projects/{id} */
3374
+ retrieve: (id: string) => Promise<{
3375
+ id: string;
3376
+ org_id: string;
3377
+ name: string;
3378
+ allowed_domains: string[];
3379
+ always_generate: boolean;
3380
+ created_at: string;
3381
+ }>;
3382
+ };
3383
+ declare function createBeacon(request: RequestFn): {
3384
+ /** POST /v1/beacon - ingest a view/AR analytics event (204). */
3385
+ send: (params: BeaconParams) => Promise<void>;
227
3386
  };
228
-
229
- /**
230
- * Models resource - 3D models (backed by the `scans` table).
231
- *
232
- * Mirrors the 8 `/v1/models*` endpoints. camelCase param names are mapped
233
- * to the snake_case wire contract on the way out; response shapes pass
234
- * through as-is.
235
- */
236
-
237
- declare class ModelsResource {
238
- private readonly client;
239
- constructor(client: Viewrium);
240
- create(params: ModelCreateParams): Promise<Model>;
241
- createFromImages(images: FileInput[], params?: ModelCreateFromImagesParams): Promise<Model>;
242
- createFromVideo(video: FileInput, params?: ModelCreateFromVideoParams): Promise<Model>;
243
- retrieve(modelId: string): Promise<Model>;
244
- list(params?: ModelListParams): Promise<ModelListResponse>;
245
- modify(modelId: string, params: ModelUpdateParams): Promise<Model>;
246
- delete(modelId: string): Promise<DeletedResponse>;
247
- listEvents(modelId: string, params?: ModelListEventsParams): Promise<ModelEventListResponse>;
248
- }
249
-
250
- /**
251
- * Api keys resource.
252
- *
253
- * Creates secret (`sk_*`) or publishable (`pk_*`) keys. pk keys inherit
254
- * origin restrictions from their project's `allowed_domains`. The plaintext
255
- * key is returned ONCE from `create()`; store it securely on your side.
256
- */
257
-
258
- declare class ApiKeysResource {
259
- private readonly client;
260
- constructor(client: Viewrium);
261
- create(params?: ApiKeyCreateParams): Promise<ApiKeyWithSecret>;
262
- list(): Promise<ApiKeyListResponse>;
263
- delete(keyId: string): Promise<RevokedResponse>;
264
- }
265
-
266
- /**
267
- * Usage resource.
268
- *
269
- * Returns aggregated event counts for the caller's organization. Internal
270
- * service events (rodin / gemini / scrape / convert / store) are never
271
- * exposed here.
272
- */
273
-
274
- declare class UsageResource {
275
- private readonly client;
276
- constructor(client: Viewrium);
277
- retrieve(params?: UsageRetrieveParams): Promise<UsageSummary>;
278
- }
279
-
280
- /**
281
- * Tags resource.
282
- *
283
- * Lists unique tags across the caller's organization with per-tag model
284
- * counts. Tags are created implicitly when assigned to a model; there is
285
- * no POST /v1/tags endpoint.
286
- */
287
-
288
- declare class TagsResource {
289
- private readonly client;
290
- constructor(client: Viewrium);
291
- list(): Promise<TagListResponse>;
292
- }
293
-
294
- /**
295
- * Viewrium client.
296
- *
297
- * Instantiate once per api key:
298
- *
299
- * const viewrium = new Viewrium(process.env.VIEWRIUM_API_KEY!);
300
- * const model = await viewrium.models.create({ images: [...] });
301
- *
302
- * The client uses `globalThis.fetch` by default (Node 18+, browsers, edge
303
- * runtimes all provide it). Pass `fetch` in options for custom transports
304
- * or testing.
305
- */
306
-
307
- interface RequestOptions {
308
- method: string;
309
- path: string;
310
- query?: Record<string, unknown>;
311
- json?: unknown;
312
- body?: BodyInit;
313
- headers?: Record<string, string>;
314
- /** Override per-request timeout in ms. */
315
- timeoutMs?: number;
316
- }
317
3387
  declare class Viewrium {
318
- readonly models: ModelsResource;
319
- readonly apiKeys: ApiKeysResource;
320
- readonly usage: UsageResource;
321
- readonly tags: TagsResource;
322
- private readonly apiKey;
323
- private readonly apiBase;
324
- private readonly timeoutMs;
325
- private readonly maxRetries;
326
- private readonly fetchImpl;
327
- constructor(apiKey?: string, options?: ViewriumOptions);
328
- /** Low-level request. Resources delegate here. */
329
- request<T = unknown>(options: RequestOptions): Promise<T>;
330
- private buildUrl;
331
- private buildHeaders;
332
- private buildBody;
3388
+ #private;
3389
+ readonly baseUrl: string;
3390
+ readonly products: ReturnType<typeof createProducts>;
3391
+ readonly apiKeys: ReturnType<typeof createApiKeys>;
3392
+ readonly usage: ReturnType<typeof createUsage>;
3393
+ readonly webhooks: ReturnType<typeof createWebhooks>;
3394
+ readonly projects: ReturnType<typeof createProjects>;
3395
+ readonly beacon: ReturnType<typeof createBeacon>;
3396
+ constructor(options: ViewriumOptions);
3397
+ /** GET /v1/health */
3398
+ health(): Promise<Health>;
333
3399
  }
3400
+ /** Convenience factory: `createViewrium({ apiKey })` is `new Viewrium({ apiKey })`. */
3401
+ declare function createViewrium(options: ViewriumOptions): Viewrium;
334
3402
 
335
- /**
336
- * Official JavaScript / TypeScript SDK for the Viewrium API.
337
- *
338
- * import Viewrium from "@viewrium/sdk";
339
- *
340
- * const viewrium = new Viewrium(process.env.VIEWRIUM_API_KEY!);
341
- *
342
- * const model = await viewrium.models.create({
343
- * customId: "sku-1",
344
- * images: ["https://example.com/img1.jpg"],
345
- * height: 90,
346
- * tags: ["chair", "summer-2026"],
347
- * });
348
- */
349
-
350
- export { APIError, type ApiKey, type ApiKeyCreateParams, type ApiKeyListResponse, type ApiKeyWithSecret, ApiKeysResource, AuthError, type DeletedResponse, type FileInput, type Model, type ModelCreateFromImagesParams, type ModelCreateFromVideoParams, type ModelCreateParams, type ModelEvent, type ModelEventListResponse, type ModelListEventsParams, type ModelListParams, type ModelListResponse, type ModelStatus, type ModelUpdateParams, ModelsResource, NotFoundError, RateLimitError, type RequestOptions, type RevokedResponse, type SizeUnit, type SourceType, type TagListResponse, type TagSummary, TagsResource, type UsageEventType, UsageResource, type UsageRetrieveParams, type UsageSummary, ValidationError, Viewrium, ViewriumError, type ViewriumOptions, Viewrium as default };
3403
+ export { type ApiErrorPayload, type ApiKeyCreateParams, type ApiKeyCreated, type ApiKeyList, AuthenticationError, BadRequestError, type BeaconParams, ConflictError, ConnectionError, type FetchLike, type GateFlags, type Health, type ModelPlan, NotFoundError, PaymentRequiredError, PermissionError, type PlannedView, type Product, type ProductBatchResult, type ProductCost, type ProductCreateParams, type ProductCreateResult, type ProductCreated, type ProductExportParams, type ProductExportQueued, type ProductImage, type ProductImagePatchParams, type ProductList, type ProductListParams, type ProductModel, type ProductModelPatchParams, type ProductOrderParams, type ProductOrderParked, type ProductParked, type ProductPublishParams, type ProductRescaleParams, type ProductReviewParams, type ProductReviewResult, type ProductSummary, type ProductUpdateParams, type Project, type ProjectList, type PublicProduct, RateLimitError, ServerError, type Tags, type TagsParams, UnprocessableEntityError, type Usage, type UsageParams, type UsageProductTotals, Viewrium, ViewriumError, type ViewriumErrorOptions, type ViewriumOptions, type WebhookDeliveriesParams, type WebhookDeliveryList, type WebhookEndpoint, type WebhookEndpointCreateParams, type WebhookEndpointCreated, type WebhookEndpointList, type WebhookEndpointUpdateParams, createApiError, createViewrium };