@ingenx-io/valets-schema-mcp-server 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/data/docs/collections/firestore-paths.md +56 -3
  2. package/data/docs/enums/attention-status.md +24 -0
  3. package/data/docs/enums/booking-status.md +2 -2
  4. package/data/docs/enums/customer-payment-status.md +2 -2
  5. package/data/docs/enums/customer-payment-target-type.md +2 -2
  6. package/data/docs/enums/delivery-type.md +2 -2
  7. package/data/docs/enums/deployment-link-type.md +26 -0
  8. package/data/docs/enums/event-status.md +2 -2
  9. package/data/docs/enums/fulfillment-status.md +2 -2
  10. package/data/docs/enums/loyalty-transaction-type.md +2 -2
  11. package/data/docs/enums/order-status.md +2 -2
  12. package/data/docs/enums/payment-method.md +2 -2
  13. package/data/docs/enums/payment-proof-status.md +2 -2
  14. package/data/docs/enums/payment-status.md +2 -2
  15. package/data/docs/enums/pending-issue.md +31 -0
  16. package/data/docs/enums/return-status.md +2 -2
  17. package/data/docs/enums/session-status.md +2 -2
  18. package/data/docs/enums/site-status.md +24 -0
  19. package/data/docs/enums/ticket-status.md +2 -2
  20. package/data/docs/index.md +15 -3
  21. package/data/docs/models/allowed-user.md +188 -0
  22. package/data/docs/models/analytics-backfill.md +398 -0
  23. package/data/docs/models/analytics-daily.md +351 -0
  24. package/data/docs/models/analytics-event.md +533 -0
  25. package/data/docs/models/analytics-hourly.md +372 -0
  26. package/data/docs/models/booking-version.md +2 -2
  27. package/data/docs/models/booking.md +2 -2
  28. package/data/docs/models/customer-payment-allocation.md +2 -2
  29. package/data/docs/models/customer-payment.md +2 -2
  30. package/data/docs/models/customer.md +2 -2
  31. package/data/docs/models/event.md +2 -2
  32. package/data/docs/models/loyalty-config.md +2 -2
  33. package/data/docs/models/loyalty-reward.md +2 -2
  34. package/data/docs/models/loyalty-status.md +2 -2
  35. package/data/docs/models/loyalty-transaction.md +2 -2
  36. package/data/docs/models/magic-link-request.md +285 -0
  37. package/data/docs/models/metrics-current.md +2 -2
  38. package/data/docs/models/metrics-daily.md +2 -2
  39. package/data/docs/models/metrics-monthly.md +2 -2
  40. package/data/docs/models/order-item.md +2 -2
  41. package/data/docs/models/order.md +248 -220
  42. package/data/docs/models/sale.md +2 -2
  43. package/data/docs/models/site-payment.md +200 -0
  44. package/data/docs/models/site.md +561 -0
  45. package/data/docs/models/ticket.md +2 -2
  46. package/data/static/llms.txt +362 -2
  47. package/data/static/openapi.yaml +1068 -0
  48. package/data/static/schemas.json +1229 -44
  49. package/package.json +1 -1
@@ -20,6 +20,16 @@ info:
20
20
  paths: {}
21
21
  components:
22
22
  schemas:
23
+ AttentionStatus:
24
+ type: string
25
+ enum:
26
+ - ACTIVE
27
+ - STALE
28
+ - ON_HOLD
29
+ - ESCALATED
30
+ description: Operational attention status for Order and Booking (D39). Controls
31
+ home-page queue assignment. Server-owned — set via triggers or callable fn
32
+ only.
23
33
  BookingStatus:
24
34
  type: string
25
35
  enum:
@@ -59,6 +69,17 @@ components:
59
69
  comes to the business (ON_SITE), collects their order themselves (PICK_UP),
60
70
  or receives a physical delivery (DELIVERY). Drives whether fulfillmentStatus
61
71
  is relevant.
72
+ DeploymentLinkType:
73
+ type: string
74
+ enum:
75
+ - web
76
+ - mobile
77
+ - pwa
78
+ - app-store
79
+ - play-store
80
+ - other
81
+ description: Category of a Site deployment link (D41). Used for UI icon/labelling
82
+ and deep-link handling.
62
83
  EventStatus:
63
84
  type: string
64
85
  enum:
@@ -135,6 +156,23 @@ components:
135
156
  - PARTIALLY_REFUNDED
136
157
  description: Payment lifecycle status (D01 amended). Used by Order, Sale/Purchase,
137
158
  Booking.
159
+ PendingIssue:
160
+ type: string
161
+ enum:
162
+ - PAYMENT_PROOF_PENDING
163
+ - PAYMENT_PROOF_REJECTED
164
+ - AMOUNT_DISCREPANCY
165
+ - RETURN_UNRESOLVED
166
+ - OVERDUE_DELIVERY
167
+ - SESSION_OVERDUE
168
+ - PAYMENT_INCOMPLETE
169
+ - CANCELLATION_REQUESTED
170
+ - UNREFUNDED_CANCELLATION
171
+ - UPCOMING_UNPAID
172
+ - NO_SHOW_UNRESOLVED
173
+ description: Specific detected conditions on an Order or Booking requiring human
174
+ action (D39). Stored as an array — multiple issues can be active simultaneously.
175
+ Server-owned.
138
176
  ReturnStatus:
139
177
  type: string
140
178
  enum:
@@ -157,6 +195,15 @@ components:
157
195
  - CANCELLED
158
196
  description: Per-date/per-slot booking session status (D19). Dashboard is sole
159
197
  writer; Mobile is read-only.
198
+ SiteStatus:
199
+ type: string
200
+ enum:
201
+ - ACTIVE
202
+ - INACTIVE
203
+ - EXPIRED
204
+ - ARCHIVED
205
+ description: Lifecycle status for a Site (D41). Drives whether the site is reachable
206
+ and whether analytics/payments flow.
160
207
  TicketStatus:
161
208
  type: string
162
209
  enum:
@@ -180,6 +227,646 @@ components:
180
227
  - _nanoseconds
181
228
  additionalProperties: false
182
229
  description: Firestore Timestamp serialized representation
230
+ AllowedUser:
231
+ type: object
232
+ properties:
233
+ id:
234
+ readOnly: true
235
+ description: (Read-only) Firestore document ID = contact identifier (email
236
+ or E.164 phone), URL-escaped.
237
+ type:
238
+ - string
239
+ - 'null'
240
+ companyId:
241
+ type: string
242
+ x-immutable: true
243
+ description: (Immutable) FK → Company document ID.
244
+ siteId:
245
+ type: string
246
+ x-immutable: true
247
+ description: (Immutable) FK → Site document ID (D40 sub-tenant scope).
248
+ contact:
249
+ type: string
250
+ description: Email or E.164 phone number. Canonical identifier for this
251
+ user within the site.
252
+ tier:
253
+ type: string
254
+ description: Access tier. Free string per site (ING-304 open question —
255
+ tier values are site-specific today).
256
+ amount:
257
+ type: number
258
+ description: Amount paid. Generalized from amount_xof per D40 decision.
259
+ currency:
260
+ default: XOF
261
+ description: Currency code (ISO 4217). Defaults to XOF for legacy SR-Single
262
+ parity.
263
+ type: string
264
+ transactionId:
265
+ type: string
266
+ description: Payment provider transaction ID (e.g. Wave).
267
+ paidAt:
268
+ $ref: '#/components/schemas/FirestoreTimestamp'
269
+ description: RFC3339Nano UTC when payment was completed.
270
+ required:
271
+ - companyId
272
+ - siteId
273
+ - contact
274
+ - tier
275
+ - amount
276
+ - currency
277
+ - transactionId
278
+ - paidAt
279
+ additionalProperties: false
280
+ description: 'AllowedUser model (D40 / ING-304). Collection: companies/{companyId}/sites/{siteId}/allowed_users/{contactId}.
281
+ Authoritative paid-access allowlist. Upsert semantics — tier upgrades overwrite.
282
+ Source of truth for access checks and referral code resolution.'
283
+ AllowedUserCreate:
284
+ allOf:
285
+ - $ref: '#/components/schemas/AllowedUser'
286
+ description: Write payload for creating a new AllowedUser document. Fields marked
287
+ `readOnly` are server-set and must not be included. Fields marked `x-immutable`
288
+ may be set once at creation.
289
+ required:
290
+ - companyId
291
+ - siteId
292
+ - contact
293
+ - tier
294
+ - amount
295
+ - currency
296
+ - transactionId
297
+ - paidAt
298
+ AllowedUserUpdate:
299
+ allOf:
300
+ - $ref: '#/components/schemas/AllowedUser'
301
+ description: Write payload for partial update (PATCH) of a AllowedUser document.
302
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
303
+ sent.
304
+ AnalyticsBackfill:
305
+ type: object
306
+ properties:
307
+ id:
308
+ readOnly: true
309
+ description: (Read-only) Firestore document ID, auto-generated.
310
+ type:
311
+ - string
312
+ - 'null'
313
+ companyId:
314
+ type: string
315
+ x-immutable: true
316
+ description: (Immutable) FK → Company document ID.
317
+ siteId:
318
+ type: string
319
+ x-immutable: true
320
+ description: (Immutable) FK → Site document ID (D40).
321
+ status:
322
+ type: string
323
+ enum:
324
+ - pending
325
+ - running
326
+ - completed
327
+ - failed
328
+ - cancelled
329
+ description: Run status.
330
+ from:
331
+ type: string
332
+ x-immutable: true
333
+ description: (Immutable) Inclusive start date (`YYYY-MM-DD`, UTC).
334
+ to:
335
+ type: string
336
+ x-immutable: true
337
+ description: (Immutable) Inclusive end date (`YYYY-MM-DD`, UTC).
338
+ dryRun:
339
+ type: boolean
340
+ x-immutable: true
341
+ description: (Immutable) When true, rollup writes are skipped — only source
342
+ counts are returned for diffing.
343
+ processedDates:
344
+ type: array
345
+ items:
346
+ type: string
347
+ description: Ordered list of `YYYY-MM-DD` dates already processed by this
348
+ run.
349
+ errors:
350
+ type: array
351
+ items:
352
+ type: object
353
+ properties:
354
+ date:
355
+ type: string
356
+ description: '`YYYY-MM-DD` date that failed.'
357
+ message:
358
+ type: string
359
+ description: Error message captured at failure.
360
+ at:
361
+ $ref: '#/components/schemas/FirestoreTimestamp'
362
+ description: When the error occurred.
363
+ required:
364
+ - date
365
+ - message
366
+ - at
367
+ additionalProperties: false
368
+ description: Per-date errors collected during the run.
369
+ triggeredBy:
370
+ type: string
371
+ x-immutable: true
372
+ description: (Immutable) UID of the admin who triggered the backfill.
373
+ startedAt:
374
+ $ref: '#/components/schemas/FirestoreTimestamp'
375
+ description: (Read-only) When the run actually started.
376
+ readOnly: true
377
+ completedAt:
378
+ anyOf:
379
+ - $ref: '#/components/schemas/FirestoreTimestamp'
380
+ - type: 'null'
381
+ readOnly: true
382
+ description: (Read-only) When the run reached a terminal status.
383
+ required:
384
+ - companyId
385
+ - siteId
386
+ - status
387
+ - from
388
+ - to
389
+ - dryRun
390
+ - processedDates
391
+ - errors
392
+ - triggeredBy
393
+ - startedAt
394
+ additionalProperties: false
395
+ description: 'AnalyticsBackfill run (D42 / ING-304). Collection: companies/{companyId}/sites/{siteId}/analytics_backfills/{runId}.
396
+ Tracks admin-triggered rollup backfill jobs; supports dry-run.'
397
+ AnalyticsBackfillCreate:
398
+ allOf:
399
+ - $ref: '#/components/schemas/AnalyticsBackfill'
400
+ description: Write payload for creating a new AnalyticsBackfill document. Fields
401
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
402
+ may be set once at creation.
403
+ required:
404
+ - companyId
405
+ - siteId
406
+ - status
407
+ - from
408
+ - to
409
+ - dryRun
410
+ - processedDates
411
+ - errors
412
+ - triggeredBy
413
+ AnalyticsBackfillUpdate:
414
+ allOf:
415
+ - $ref: '#/components/schemas/AnalyticsBackfill'
416
+ description: Write payload for partial update (PATCH) of a AnalyticsBackfill
417
+ document. All fields optional. Fields marked `readOnly` or `x-immutable` must
418
+ not be sent.
419
+ AnalyticsDaily:
420
+ type: object
421
+ properties:
422
+ totalEvents:
423
+ type: integer
424
+ minimum: -9007199254740991
425
+ maximum: 9007199254740991
426
+ description: Total event count within the bucket.
427
+ pageViews:
428
+ type: integer
429
+ minimum: -9007199254740991
430
+ maximum: 9007199254740991
431
+ description: Count of `page_view` + `screen_view` events.
432
+ sessions:
433
+ type: integer
434
+ minimum: -9007199254740991
435
+ maximum: 9007199254740991
436
+ description: Distinct session count within the bucket (by `sessionId`).
437
+ uniqueUsers:
438
+ type: integer
439
+ minimum: -9007199254740991
440
+ maximum: 9007199254740991
441
+ description: Distinct identified `userId` count. Anonymous sessions (userId=null)
442
+ are excluded.
443
+ anonymousSessions:
444
+ type: integer
445
+ minimum: -9007199254740991
446
+ maximum: 9007199254740991
447
+ description: Distinct session count where `userId` was null for the entire
448
+ session.
449
+ orders:
450
+ type: integer
451
+ minimum: -9007199254740991
452
+ maximum: 9007199254740991
453
+ description: Count of `order_submitted` events (or Order documents scoped
454
+ to this site, D43) within the bucket.
455
+ paymentsCompleted:
456
+ type: integer
457
+ minimum: -9007199254740991
458
+ maximum: 9007199254740991
459
+ description: Count of `payment_completed` events within the bucket.
460
+ paymentsFailed:
461
+ type: integer
462
+ minimum: -9007199254740991
463
+ maximum: 9007199254740991
464
+ description: Count of `payment_failed` events within the bucket.
465
+ errors:
466
+ type: integer
467
+ minimum: -9007199254740991
468
+ maximum: 9007199254740991
469
+ description: Count of `error_occurred` + `exception_caught` events within
470
+ the bucket.
471
+ eventCounts:
472
+ type: object
473
+ propertyNames:
474
+ type: string
475
+ additionalProperties:
476
+ type: integer
477
+ minimum: -9007199254740991
478
+ maximum: 9007199254740991
479
+ description: Per-event-name counts — key is the event name (canonical or
480
+ custom), value is the count within the bucket.
481
+ id:
482
+ readOnly: true
483
+ description: (Read-only) Firestore document ID = `YYYY-MM-DD`.
484
+ type:
485
+ - string
486
+ - 'null'
487
+ companyId:
488
+ type: string
489
+ x-immutable: true
490
+ description: (Immutable) FK → Company document ID.
491
+ siteId:
492
+ type: string
493
+ x-immutable: true
494
+ description: (Immutable) FK → Site document ID (D40).
495
+ date:
496
+ type: string
497
+ x-immutable: true
498
+ description: (Immutable) `YYYY-MM-DD` UTC — matches document ID.
499
+ computedAt:
500
+ $ref: '#/components/schemas/FirestoreTimestamp'
501
+ description: (Read-only) When this rollup was last (re)computed. Updated
502
+ on every set/merge.
503
+ readOnly: true
504
+ sourceEventCount:
505
+ readOnly: true
506
+ description: (Read-only, Optional) Total source events scanned when producing
507
+ this rollup. Useful for dry-run diffing.
508
+ type:
509
+ - integer
510
+ - 'null'
511
+ minimum: -9007199254740991
512
+ maximum: 9007199254740991
513
+ required:
514
+ - totalEvents
515
+ - pageViews
516
+ - sessions
517
+ - uniqueUsers
518
+ - anonymousSessions
519
+ - orders
520
+ - paymentsCompleted
521
+ - paymentsFailed
522
+ - errors
523
+ - eventCounts
524
+ - companyId
525
+ - siteId
526
+ - date
527
+ - computedAt
528
+ additionalProperties: false
529
+ description: 'AnalyticsDaily rollup (D42 / ING-304). Collection: companies/{companyId}/sites/{siteId}/analytics_daily/{YYYY-MM-DD}.
530
+ Idempotent set/merge — reruns overwrite. Fall back to raw analytics_events
531
+ for uncovered slices.'
532
+ AnalyticsDailyCreate:
533
+ allOf:
534
+ - $ref: '#/components/schemas/AnalyticsDaily'
535
+ description: Write payload for creating a new AnalyticsDaily document. Fields
536
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
537
+ may be set once at creation.
538
+ required:
539
+ - totalEvents
540
+ - pageViews
541
+ - sessions
542
+ - uniqueUsers
543
+ - anonymousSessions
544
+ - orders
545
+ - paymentsCompleted
546
+ - paymentsFailed
547
+ - errors
548
+ - eventCounts
549
+ - companyId
550
+ - siteId
551
+ - date
552
+ AnalyticsDailyUpdate:
553
+ allOf:
554
+ - $ref: '#/components/schemas/AnalyticsDaily'
555
+ description: Write payload for partial update (PATCH) of a AnalyticsDaily document.
556
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
557
+ sent.
558
+ AnalyticsEvent:
559
+ type: object
560
+ properties:
561
+ id:
562
+ readOnly: true
563
+ description: (Read-only) Firestore document ID. Equal to eventId.
564
+ type:
565
+ - string
566
+ - 'null'
567
+ companyId:
568
+ type: string
569
+ x-immutable: true
570
+ description: (Immutable) FK → Company document ID.
571
+ siteId:
572
+ type: string
573
+ x-immutable: true
574
+ description: (Immutable) FK → Site document ID (D40 sub-tenant scope).
575
+ eventId:
576
+ type: string
577
+ x-immutable: true
578
+ description: (Immutable) Client-generated UUID. Matches document ID.
579
+ eventName:
580
+ type: string
581
+ description: Event name. Free string; canonical vocabulary is encouraged
582
+ (see CANONICAL_ANALYTICS_EVENT_NAMES).
583
+ timestamp:
584
+ type: string
585
+ description: Client-side ISO 8601 — "when it happened on device". Can diverge
586
+ from serverTimestamp for queued offline events.
587
+ serverTimestamp:
588
+ $ref: '#/components/schemas/FirestoreTimestamp'
589
+ description: (Read-only) Firestore serverTimestamp() — authoritative for
590
+ ordering.
591
+ readOnly: true
592
+ sessionId:
593
+ type: string
594
+ description: 30-minute rolling session identifier.
595
+ userId:
596
+ anyOf:
597
+ - type: string
598
+ - type: 'null'
599
+ description: Stable user ID if identified; explicitly null (not omitted)
600
+ for anonymous sessions — simplifies querying.
601
+ properties:
602
+ type: object
603
+ propertyNames:
604
+ type: string
605
+ additionalProperties: {}
606
+ description: 'Free-form event-specific payload. Contract is at the event-name
607
+ level. PII posture: follow-up issue pending.'
608
+ context:
609
+ type: object
610
+ properties:
611
+ url:
612
+ description: Full URL at event time.
613
+ type: string
614
+ origin:
615
+ description: Protocol + host.
616
+ type: string
617
+ hostname:
618
+ description: Hostname only — useful for deployment identification.
619
+ type: string
620
+ page:
621
+ description: Current route/pathname.
622
+ type: string
623
+ title:
624
+ description: Page title.
625
+ type: string
626
+ referrer:
627
+ description: Referrer URL.
628
+ type: string
629
+ deviceType:
630
+ type: string
631
+ enum:
632
+ - mobile
633
+ - tablet
634
+ - desktop
635
+ platform:
636
+ description: OS/platform string.
637
+ type: string
638
+ screenWidth:
639
+ description: Viewport width (flattened from screen.width).
640
+ type: integer
641
+ minimum: -9007199254740991
642
+ maximum: 9007199254740991
643
+ screenHeight:
644
+ description: Viewport height (flattened from screen.height).
645
+ type: integer
646
+ minimum: -9007199254740991
647
+ maximum: 9007199254740991
648
+ appVersion:
649
+ description: Consuming app version.
650
+ type: string
651
+ online:
652
+ description: Connectivity state at event time.
653
+ type: boolean
654
+ locale:
655
+ description: User locale.
656
+ type: string
657
+ timezone:
658
+ description: IANA timezone.
659
+ type: string
660
+ utm:
661
+ description: UTM parameters; null when not a campaign-landed session.
662
+ anyOf:
663
+ - type: object
664
+ properties:
665
+ source:
666
+ type: string
667
+ medium:
668
+ type: string
669
+ campaign:
670
+ type: string
671
+ term:
672
+ type: string
673
+ content:
674
+ type: string
675
+ additionalProperties: false
676
+ - type: 'null'
677
+ additionalProperties: false
678
+ description: Environment context at event time (nested per D40).
679
+ userTraits:
680
+ description: 'Optional — included only when includeUserTraits: true on the
681
+ write. PII posture: follow-up issue pending.'
682
+ type:
683
+ - object
684
+ - 'null'
685
+ propertyNames:
686
+ type: string
687
+ additionalProperties: {}
688
+ required:
689
+ - companyId
690
+ - siteId
691
+ - eventId
692
+ - eventName
693
+ - timestamp
694
+ - serverTimestamp
695
+ - sessionId
696
+ - userId
697
+ - properties
698
+ - context
699
+ additionalProperties: false
700
+ description: 'AnalyticsEvent model (D40 / ING-304). Collection: companies/{companyId}/sites/{siteId}/analytics_events/{eventId}.
701
+ Append-only, immutable product/behavior event stream. Event names are free
702
+ strings; canonical vocabulary in CANONICAL_ANALYTICS_EVENT_NAMES.'
703
+ AnalyticsEventCreate:
704
+ allOf:
705
+ - $ref: '#/components/schemas/AnalyticsEvent'
706
+ description: Write payload for creating a new AnalyticsEvent document. Fields
707
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
708
+ may be set once at creation.
709
+ required:
710
+ - companyId
711
+ - siteId
712
+ - eventId
713
+ - eventName
714
+ - timestamp
715
+ - sessionId
716
+ - userId
717
+ - properties
718
+ - context
719
+ AnalyticsEventUpdate:
720
+ allOf:
721
+ - $ref: '#/components/schemas/AnalyticsEvent'
722
+ description: Write payload for partial update (PATCH) of a AnalyticsEvent document.
723
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
724
+ sent.
725
+ AnalyticsHourly:
726
+ type: object
727
+ properties:
728
+ totalEvents:
729
+ type: integer
730
+ minimum: -9007199254740991
731
+ maximum: 9007199254740991
732
+ description: Total event count within the bucket.
733
+ pageViews:
734
+ type: integer
735
+ minimum: -9007199254740991
736
+ maximum: 9007199254740991
737
+ description: Count of `page_view` + `screen_view` events.
738
+ sessions:
739
+ type: integer
740
+ minimum: -9007199254740991
741
+ maximum: 9007199254740991
742
+ description: Distinct session count within the bucket (by `sessionId`).
743
+ uniqueUsers:
744
+ type: integer
745
+ minimum: -9007199254740991
746
+ maximum: 9007199254740991
747
+ description: Distinct identified `userId` count. Anonymous sessions (userId=null)
748
+ are excluded.
749
+ anonymousSessions:
750
+ type: integer
751
+ minimum: -9007199254740991
752
+ maximum: 9007199254740991
753
+ description: Distinct session count where `userId` was null for the entire
754
+ session.
755
+ orders:
756
+ type: integer
757
+ minimum: -9007199254740991
758
+ maximum: 9007199254740991
759
+ description: Count of `order_submitted` events (or Order documents scoped
760
+ to this site, D43) within the bucket.
761
+ paymentsCompleted:
762
+ type: integer
763
+ minimum: -9007199254740991
764
+ maximum: 9007199254740991
765
+ description: Count of `payment_completed` events within the bucket.
766
+ paymentsFailed:
767
+ type: integer
768
+ minimum: -9007199254740991
769
+ maximum: 9007199254740991
770
+ description: Count of `payment_failed` events within the bucket.
771
+ errors:
772
+ type: integer
773
+ minimum: -9007199254740991
774
+ maximum: 9007199254740991
775
+ description: Count of `error_occurred` + `exception_caught` events within
776
+ the bucket.
777
+ eventCounts:
778
+ type: object
779
+ propertyNames:
780
+ type: string
781
+ additionalProperties:
782
+ type: integer
783
+ minimum: -9007199254740991
784
+ maximum: 9007199254740991
785
+ description: Per-event-name counts — key is the event name (canonical or
786
+ custom), value is the count within the bucket.
787
+ id:
788
+ readOnly: true
789
+ description: (Read-only) Firestore document ID = `YYYY-MM-DD-HH`.
790
+ type:
791
+ - string
792
+ - 'null'
793
+ companyId:
794
+ type: string
795
+ x-immutable: true
796
+ description: (Immutable) FK → Company document ID.
797
+ siteId:
798
+ type: string
799
+ x-immutable: true
800
+ description: (Immutable) FK → Site document ID (D40).
801
+ date:
802
+ type: string
803
+ x-immutable: true
804
+ description: (Immutable) `YYYY-MM-DD` UTC for the bucket day.
805
+ hour:
806
+ type: integer
807
+ minimum: -9007199254740991
808
+ maximum: 9007199254740991
809
+ x-immutable: true
810
+ description: (Immutable) UTC hour of day, 0–23.
811
+ computedAt:
812
+ $ref: '#/components/schemas/FirestoreTimestamp'
813
+ description: (Read-only) When this rollup was last (re)computed.
814
+ readOnly: true
815
+ sourceEventCount:
816
+ readOnly: true
817
+ description: (Read-only, Optional) Total source events scanned when producing
818
+ this rollup.
819
+ type:
820
+ - integer
821
+ - 'null'
822
+ minimum: -9007199254740991
823
+ maximum: 9007199254740991
824
+ required:
825
+ - totalEvents
826
+ - pageViews
827
+ - sessions
828
+ - uniqueUsers
829
+ - anonymousSessions
830
+ - orders
831
+ - paymentsCompleted
832
+ - paymentsFailed
833
+ - errors
834
+ - eventCounts
835
+ - companyId
836
+ - siteId
837
+ - date
838
+ - hour
839
+ - computedAt
840
+ additionalProperties: false
841
+ description: 'AnalyticsHourly rollup (D42 / ING-304). Collection: companies/{companyId}/sites/{siteId}/analytics_hourly/{YYYY-MM-DD-HH}.
842
+ Scheduled-CF writer; idempotent set/merge.'
843
+ AnalyticsHourlyCreate:
844
+ allOf:
845
+ - $ref: '#/components/schemas/AnalyticsHourly'
846
+ description: Write payload for creating a new AnalyticsHourly document. Fields
847
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
848
+ may be set once at creation.
849
+ required:
850
+ - totalEvents
851
+ - pageViews
852
+ - sessions
853
+ - uniqueUsers
854
+ - anonymousSessions
855
+ - orders
856
+ - paymentsCompleted
857
+ - paymentsFailed
858
+ - errors
859
+ - eventCounts
860
+ - companyId
861
+ - siteId
862
+ - date
863
+ - hour
864
+ AnalyticsHourlyUpdate:
865
+ allOf:
866
+ - $ref: '#/components/schemas/AnalyticsHourly'
867
+ description: Write payload for partial update (PATCH) of a AnalyticsHourly document.
868
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
869
+ sent.
183
870
  Booking:
184
871
  type: object
185
872
  properties:
@@ -1598,6 +2285,112 @@ components:
1598
2285
  description: Write payload for partial update (PATCH) of a LoyaltyTransaction
1599
2286
  document. All fields optional. Fields marked `readOnly` or `x-immutable` must
1600
2287
  not be sent.
2288
+ MagicLinkRequest:
2289
+ type: object
2290
+ properties:
2291
+ id:
2292
+ readOnly: true
2293
+ description: (Read-only) Firestore document ID. 16-byte random hex.
2294
+ type:
2295
+ - string
2296
+ - 'null'
2297
+ companyId:
2298
+ type: string
2299
+ x-immutable: true
2300
+ description: (Immutable) FK → Company document ID.
2301
+ siteId:
2302
+ type: string
2303
+ x-immutable: true
2304
+ description: (Immutable) FK → Site document ID (D40 sub-tenant scope).
2305
+ email:
2306
+ type: string
2307
+ description: Populated if identifier is email; empty string if phone. Mutually
2308
+ exclusive with phone per document.
2309
+ phone:
2310
+ type: string
2311
+ description: Populated if identifier is E.164 format (starts with +); empty
2312
+ string if email.
2313
+ ip:
2314
+ type: string
2315
+ description: Client IP from X-Forwarded-For or RemoteAddr.
2316
+ allowed:
2317
+ type: boolean
2318
+ description: Whether the user passed the access control check.
2319
+ link:
2320
+ type: string
2321
+ description: Magic-link URL — non-empty only when DEV_MODE=true; empty in
2322
+ production (audit integrity).
2323
+ tier:
2324
+ type: string
2325
+ description: Access tier selected by the user. Free string per site (ING-304
2326
+ open question — tier values are site-specific today).
2327
+ authStatusReason:
2328
+ type: string
2329
+ description: 'Outcome reason code: whitelist | allowed_users | payment_redirect
2330
+ | payment_complete | not_allowed | redirected_to_payment.'
2331
+ referredBy:
2332
+ type: string
2333
+ description: Contact identifier of the referrer, resolved from submitted
2334
+ referral_code; empty if none.
2335
+ requestedAt:
2336
+ $ref: '#/components/schemas/FirestoreTimestamp'
2337
+ description: RFC3339Nano UTC at request time.
2338
+ verifyReason:
2339
+ description: 'Verify outcome reason: mirrors authStatusReason on success,
2340
+ or link_expired | link_invalid | not_allowed.'
2341
+ type:
2342
+ - string
2343
+ - 'null'
2344
+ verified:
2345
+ description: Whether the verify attempt succeeded.
2346
+ type:
2347
+ - boolean
2348
+ - 'null'
2349
+ verifiedAt:
2350
+ anyOf:
2351
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2352
+ - type: 'null'
2353
+ description: RFC3339Nano UTC at verify time.
2354
+ required:
2355
+ - companyId
2356
+ - siteId
2357
+ - email
2358
+ - phone
2359
+ - ip
2360
+ - allowed
2361
+ - link
2362
+ - tier
2363
+ - authStatusReason
2364
+ - referredBy
2365
+ - requestedAt
2366
+ additionalProperties: false
2367
+ description: 'MagicLinkRequest model (D40 / ING-304). Collection: companies/{companyId}/sites/{siteId}/magic_link_requests/{requestId}.
2368
+ Authentication audit log — every request is logged regardless of outcome.
2369
+ Two-stage write: Log() then LogVerify().'
2370
+ MagicLinkRequestCreate:
2371
+ allOf:
2372
+ - $ref: '#/components/schemas/MagicLinkRequest'
2373
+ description: Write payload for creating a new MagicLinkRequest document. Fields
2374
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
2375
+ may be set once at creation.
2376
+ required:
2377
+ - companyId
2378
+ - siteId
2379
+ - email
2380
+ - phone
2381
+ - ip
2382
+ - allowed
2383
+ - link
2384
+ - tier
2385
+ - authStatusReason
2386
+ - referredBy
2387
+ - requestedAt
2388
+ MagicLinkRequestUpdate:
2389
+ allOf:
2390
+ - $ref: '#/components/schemas/MagicLinkRequest'
2391
+ description: Write payload for partial update (PATCH) of a MagicLinkRequest
2392
+ document. All fields optional. Fields marked `readOnly` or `x-immutable` must
2393
+ not be sent.
1601
2394
  MetricsCurrent:
1602
2395
  type: object
1603
2396
  properties:
@@ -2205,6 +2998,23 @@ components:
2205
2998
  type: string
2206
2999
  x-immutable: true
2207
3000
  description: (Immutable) FK → Company document ID. Scopes all queries.
3001
+ siteId:
3002
+ x-immutable: true
3003
+ x-note: 'D43 / ADR-003: optional site attribution. Absent/null means company-wide
3004
+ (legacy). When set, the order is attributed to a specific site for analytics
3005
+ and site-scoped dashboards. No migration — existing orders keep null.
3006
+ Flat path; Order continues to live at `companies/{companyId}/orders/{orderId}`.'
3007
+ x-see:
3008
+ decisions:
3009
+ - D43
3010
+ x-when: 'Set at order creation when the client knows which site the order
3011
+ originated from (e.g. SR Single, Lifesense). Leave null for legacy/company-wide
3012
+ orders. Composite index: (companyId, siteId, createdAt).'
3013
+ description: (Immutable, Optional) FK → Site document ID (D43 / ADR-003).
3014
+ null = company-wide.
3015
+ type:
3016
+ - string
3017
+ - 'null'
2208
3018
  orderNumber:
2209
3019
  type: string
2210
3020
  readOnly: true
@@ -2827,6 +3637,264 @@ components:
2827
3637
  - $ref: '#/components/schemas/Sale'
2828
3638
  description: Write payload for partial update (PATCH) of a Sale document. All
2829
3639
  fields optional. Fields marked `readOnly` or `x-immutable` must not be sent.
3640
+ Site:
3641
+ type: object
3642
+ properties:
3643
+ id:
3644
+ readOnly: true
3645
+ description: (Read-only) Firestore document ID, auto-generated.
3646
+ type:
3647
+ - string
3648
+ - 'null'
3649
+ companyId:
3650
+ type: string
3651
+ x-immutable: true
3652
+ description: (Immutable) FK → Company document ID. Scopes all site sub-collections.
3653
+ name:
3654
+ type: string
3655
+ description: Human-readable site name shown in dashboards.
3656
+ description:
3657
+ description: Optional freeform description.
3658
+ type:
3659
+ - string
3660
+ - 'null'
3661
+ status:
3662
+ $ref: '#/components/schemas/SiteStatus'
3663
+ description: Lifecycle status (D41). Clients filter by ACTIVE.
3664
+ x-note: ACTIVE = live and serving traffic. INACTIVE = intentionally disabled
3665
+ by operators. EXPIRED = past expiresAt (derived when isExpired is true;
3666
+ may also be stored explicitly). ARCHIVED = soft-deleted; retained for
3667
+ audit but not listed by default.
3668
+ x-see:
3669
+ decisions:
3670
+ - D41
3671
+ x-when: Written by operators via dashboard or by server triggers (expiration).
3672
+ Clients filter by ACTIVE.
3673
+ deploymentLinks:
3674
+ type: array
3675
+ items:
3676
+ type: object
3677
+ properties:
3678
+ label:
3679
+ type: string
3680
+ description: Human-readable label shown in dashboards (e.g. "Production",
3681
+ "Staging").
3682
+ url:
3683
+ type: string
3684
+ description: Absolute URL or store link.
3685
+ type:
3686
+ $ref: '#/components/schemas/DeploymentLinkType'
3687
+ description: Link category — drives icon/handler selection.
3688
+ x-note: Lowercase by convention — deployment link types are display-oriented
3689
+ labels, not lifecycle states.
3690
+ x-see:
3691
+ decisions:
3692
+ - D41
3693
+ isPrimary:
3694
+ description: If true, this link is used as the canonical deployment
3695
+ URL for the Site.
3696
+ type: boolean
3697
+ required:
3698
+ - label
3699
+ - url
3700
+ - type
3701
+ additionalProperties: false
3702
+ description: Deployment link entry embedded on Site.deploymentLinks[]
3703
+ (D41).
3704
+ description: Ordered list of deployment URLs (web, mobile, PWA, store links).
3705
+ expiresAt:
3706
+ anyOf:
3707
+ - $ref: '#/components/schemas/FirestoreTimestamp'
3708
+ - type: 'null'
3709
+ description: Optional expiration timestamp. When set and elapsed, `isExpired`
3710
+ flips true and status typically moves to EXPIRED.
3711
+ isExpired:
3712
+ readOnly: true
3713
+ description: (Read-only) Derived — true when `expiresAt` is in the past.
3714
+ Maintained by server trigger.
3715
+ type:
3716
+ - boolean
3717
+ - 'null'
3718
+ createdAt:
3719
+ anyOf:
3720
+ - $ref: '#/components/schemas/FirestoreTimestamp'
3721
+ - type: 'null'
3722
+ readOnly: true
3723
+ description: (Read-only) Server-generated creation timestamp.
3724
+ updatedAt:
3725
+ anyOf:
3726
+ - $ref: '#/components/schemas/FirestoreTimestamp'
3727
+ - type: 'null'
3728
+ readOnly: true
3729
+ description: (Read-only) Server-generated update timestamp.
3730
+ createdBy:
3731
+ type: string
3732
+ x-immutable: true
3733
+ description: (Immutable) FK → User/staff UID who created the site.
3734
+ analyticsEnabled:
3735
+ type: boolean
3736
+ description: Feature flag — when false, clients should not emit analytics
3737
+ events for this site.
3738
+ lastAnalyticsSync:
3739
+ anyOf:
3740
+ - $ref: '#/components/schemas/FirestoreTimestamp'
3741
+ - type: 'null'
3742
+ readOnly: true
3743
+ description: (Read-only) Last time the analytics rollup pipeline refreshed
3744
+ `cachedMetrics`.
3745
+ cachedMetrics:
3746
+ readOnly: true
3747
+ denormalized: true
3748
+ x-note: Updated by the rollup pipeline (D42). Readers should treat this
3749
+ as a hint; authoritative counts live in analytics_daily/analytics_hourly.
3750
+ x-see:
3751
+ decisions:
3752
+ - D41
3753
+ - D42
3754
+ description: (Read-only, Denormalized) Cached metrics snapshot for quick
3755
+ dashboard rendering.
3756
+ type:
3757
+ - object
3758
+ - 'null'
3759
+ properties:
3760
+ totalEvents:
3761
+ type: integer
3762
+ minimum: -9007199254740991
3763
+ maximum: 9007199254740991
3764
+ description: All-time event count cached on the Site.
3765
+ totalPageViews:
3766
+ type: integer
3767
+ minimum: -9007199254740991
3768
+ maximum: 9007199254740991
3769
+ description: All-time `page_view` + `screen_view` count.
3770
+ totalSessions:
3771
+ type: integer
3772
+ minimum: -9007199254740991
3773
+ maximum: 9007199254740991
3774
+ description: All-time distinct session count.
3775
+ totalOrders:
3776
+ type: integer
3777
+ minimum: -9007199254740991
3778
+ maximum: 9007199254740991
3779
+ description: All-time order count attributed to this site (via `Order.siteId`,
3780
+ D43).
3781
+ lastEventAt:
3782
+ $ref: '#/components/schemas/FirestoreTimestamp'
3783
+ description: Timestamp of the most recent analytics event seen for this
3784
+ site.
3785
+ required:
3786
+ - totalEvents
3787
+ - totalPageViews
3788
+ - totalSessions
3789
+ - totalOrders
3790
+ additionalProperties: false
3791
+ required:
3792
+ - companyId
3793
+ - name
3794
+ - status
3795
+ - deploymentLinks
3796
+ - createdBy
3797
+ - analyticsEnabled
3798
+ additionalProperties: false
3799
+ description: 'Site model (D41 / ING-304). Collection: companies/{companyId}/sites/{siteId}.
3800
+ Per-company product surface. Sub-collections (magic_link_requests, allowed_users,
3801
+ payments, analytics_events, analytics_daily, analytics_hourly, analytics_backfills)
3802
+ live under this document per D40/D42.'
3803
+ SiteCreate:
3804
+ allOf:
3805
+ - $ref: '#/components/schemas/Site'
3806
+ description: Write payload for creating a new Site document. Fields marked `readOnly`
3807
+ are server-set and must not be included. Fields marked `x-immutable` may be
3808
+ set once at creation.
3809
+ required:
3810
+ - companyId
3811
+ - name
3812
+ - status
3813
+ - deploymentLinks
3814
+ - createdBy
3815
+ - analyticsEnabled
3816
+ SiteUpdate:
3817
+ allOf:
3818
+ - $ref: '#/components/schemas/Site'
3819
+ description: Write payload for partial update (PATCH) of a Site document. All
3820
+ fields optional. Fields marked `readOnly` or `x-immutable` must not be sent.
3821
+ SitePayment:
3822
+ type: object
3823
+ properties:
3824
+ id:
3825
+ readOnly: true
3826
+ description: (Read-only) Firestore document ID, auto-generated.
3827
+ type:
3828
+ - string
3829
+ - 'null'
3830
+ companyId:
3831
+ type: string
3832
+ x-immutable: true
3833
+ description: (Immutable) FK → Company document ID.
3834
+ siteId:
3835
+ type: string
3836
+ x-immutable: true
3837
+ description: (Immutable) FK → Site document ID (D40 sub-tenant scope).
3838
+ contact:
3839
+ type: string
3840
+ description: Email or E.164 phone number. Matches AllowedUser.contact.
3841
+ sessionId:
3842
+ type: string
3843
+ description: Payment provider checkout session ID (e.g. Wave). Usable for
3844
+ dedup across dual writes.
3845
+ transactionId:
3846
+ type: string
3847
+ description: Payment provider transaction ID.
3848
+ tier:
3849
+ type: string
3850
+ description: Access tier. Free string per site (ING-304 open question).
3851
+ amount:
3852
+ type: number
3853
+ description: Amount paid. Generalized from amount_xof per D40 decision.
3854
+ currency:
3855
+ default: XOF
3856
+ description: Currency code (ISO 4217). Defaults to XOF for legacy SR-Single
3857
+ parity.
3858
+ type: string
3859
+ paidAt:
3860
+ $ref: '#/components/schemas/FirestoreTimestamp'
3861
+ description: RFC3339Nano UTC when payment was completed.
3862
+ required:
3863
+ - companyId
3864
+ - siteId
3865
+ - contact
3866
+ - sessionId
3867
+ - transactionId
3868
+ - tier
3869
+ - amount
3870
+ - currency
3871
+ - paidAt
3872
+ additionalProperties: false
3873
+ description: 'SitePayment model (D40 / ING-304). Collection: companies/{companyId}/sites/{siteId}/payments/{paymentId}.
3874
+ Immutable append-only transaction ledger. Distinct from AllowedUser (access
3875
+ state) and CustomerPayment (D22 — customer-level billing).'
3876
+ SitePaymentCreate:
3877
+ allOf:
3878
+ - $ref: '#/components/schemas/SitePayment'
3879
+ description: Write payload for creating a new SitePayment document. Fields marked
3880
+ `readOnly` are server-set and must not be included. Fields marked `x-immutable`
3881
+ may be set once at creation.
3882
+ required:
3883
+ - companyId
3884
+ - siteId
3885
+ - contact
3886
+ - sessionId
3887
+ - transactionId
3888
+ - tier
3889
+ - amount
3890
+ - currency
3891
+ - paidAt
3892
+ SitePaymentUpdate:
3893
+ allOf:
3894
+ - $ref: '#/components/schemas/SitePayment'
3895
+ description: Write payload for partial update (PATCH) of a SitePayment document.
3896
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
3897
+ sent.
2830
3898
  Ticket:
2831
3899
  type: object
2832
3900
  properties: