@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
@@ -4,12 +4,16 @@
4
4
  > Source of truth: Zod schemas in packages/schema/src/
5
5
  > All Firestore documents are scoped under companies/{companyId}/
6
6
 
7
- > Generated: 2026-04-06
7
+ > Generated: 2026-04-18
8
8
 
9
9
  ---
10
10
 
11
11
  ## Enums
12
12
 
13
+ ### AttentionStatus
14
+ Operational attention status for Order and Booking (D39). Controls home-page queue assignment. Server-owned — set via triggers or callable fn only.
15
+ Values: ACTIVE, STALE, ON_HOLD, ESCALATED
16
+
13
17
  ### BookingStatus
14
18
  Booking lifecycle status. COMPLETED_MIXED = some sessions completed, others cancelled/no-show.
15
19
  Values: PENDING, CONFIRMED, COMPLETED, CANCELLED, CANCELLATION_REQUESTED, COMPLETED_MIXED
@@ -26,6 +30,10 @@ Values: BOOKING, ORDER, PURCHASE
26
30
  Fulfillment channel for an order. Determines whether the customer comes to the business (ON_SITE), collects their order themselves (PICK_UP), or receives a physical delivery (DELIVERY). Drives whether fulfillmentStatus is relevant.
27
31
  Values: ON_SITE, PICK_UP, DELIVERY
28
32
 
33
+ ### DeploymentLinkType
34
+ Category of a Site deployment link (D41). Used for UI icon/labelling and deep-link handling.
35
+ Values: web, mobile, pwa, app-store, play-store, other
36
+
29
37
  ### EventStatus
30
38
  Ticketed event lifecycle (D32). Mobile-only today; Dashboard in Wave 4.
31
39
  Values: DRAFT, ACTIVE, CANCELLED, COMPLETED
@@ -54,6 +62,10 @@ Values: PENDING, APPROVED, REJECTED
54
62
  Payment lifecycle status (D01 amended). Used by Order, Sale/Purchase, Booking.
55
63
  Values: PENDING, PAID, PARTIALLY_PAID, FAILED, REFUND_PROCESSING, REFUNDED, PARTIALLY_REFUNDED
56
64
 
65
+ ### PendingIssue
66
+ Specific detected conditions on an Order or Booking requiring human action (D39). Stored as an array — multiple issues can be active simultaneously. Server-owned.
67
+ Values: PAYMENT_PROOF_PENDING, PAYMENT_PROOF_REJECTED, AMOUNT_DISCREPANCY, RETURN_UNRESOLVED, OVERDUE_DELIVERY, SESSION_OVERDUE, PAYMENT_INCOMPLETE, CANCELLATION_REQUESTED, UNREFUNDED_CANCELLATION, UPCOMING_UNPAID, NO_SHOW_UNRESOLVED
68
+
57
69
  ### ReturnStatus
58
70
  Post-sale return/exchange lifecycle (D34). Optional — null until return or exchange initiated.
59
71
  Values: RETURN_REQUESTED, RETURN_PROCESSING, RETURNED, EXCHANGE_REQUESTED, EXCHANGE_PROCESSING, EXCHANGE_COMPLETED
@@ -62,6 +74,10 @@ Values: RETURN_REQUESTED, RETURN_PROCESSING, RETURNED, EXCHANGE_REQUESTED, EXCHA
62
74
  Per-date/per-slot booking session status (D19). Dashboard is sole writer; Mobile is read-only.
63
75
  Values: PENDING, CONFIRMED, CANCELLATION_REQUESTED, COMPLETED, NO_SHOW, CANCELLED
64
76
 
77
+ ### SiteStatus
78
+ Lifecycle status for a Site (D41). Drives whether the site is reachable and whether analytics/payments flow.
79
+ Values: ACTIVE, INACTIVE, EXPIRED, ARCHIVED
80
+
65
81
  ### TicketStatus
66
82
  Event ticket status (D32). VALID = active and unused.
67
83
  Values: VALID, USED, CANCELLED
@@ -70,6 +86,224 @@ Values: VALID, USED, CANCELLED
70
86
 
71
87
  ## Models
72
88
 
89
+ ### AllowedUser
90
+ Fields: 9 (8 required)
91
+
92
+ | Field | Type | Required | Description |
93
+ |-------|------|----------|-------------|
94
+ | id | ['string', 'null'] | no | (Read-only) Firestore document ID = contact identifier (email or E.164 phone), URL-escaped. |
95
+ | companyId | string | yes | (Immutable) FK → Company document ID. |
96
+ | siteId | string | yes | (Immutable) FK → Site document ID (D40 sub-tenant scope). |
97
+ | contact | string | yes | Email or E.164 phone number. Canonical identifier for this user within the site. |
98
+ | tier | string | yes | Access tier. Free string per site (ING-304 open question — tier values are site-specific today). |
99
+ | amount | number | yes | Amount paid. Generalized from amount_xof per D40 decision. |
100
+ | currency | string | yes | Currency code (ISO 4217). Defaults to XOF for legacy SR-Single parity. |
101
+ | transactionId | string | yes | Payment provider transaction ID (e.g. Wave). |
102
+ | paidAt | FirestoreTimestamp | yes | RFC3339Nano UTC when payment was completed. |
103
+
104
+ Example:
105
+ ```json
106
+ {
107
+ "id": null,
108
+ "companyId": "comp_xyz789",
109
+ "siteId": "sit_ref123",
110
+ "contact": "contact",
111
+ "tier": "Gold",
112
+ "amount": 45000,
113
+ "currency": "XOF",
114
+ "transactionId": "tra_ref123",
115
+ "paidAt": "pai_ref123"
116
+ }
117
+ ```
118
+
119
+ ### AnalyticsBackfill
120
+ Fields: 12 (10 required)
121
+
122
+ | Field | Type | Required | Description |
123
+ |-------|------|----------|-------------|
124
+ | id | ['string', 'null'] | no | (Read-only) Firestore document ID, auto-generated. |
125
+ | companyId | string | yes | (Immutable) FK → Company document ID. |
126
+ | siteId | string | yes | (Immutable) FK → Site document ID (D40). |
127
+ | status | enum(5) | yes | Run status. |
128
+ | from | string | yes | (Immutable) Inclusive start date (`YYYY-MM-DD`, UTC). |
129
+ | to | string | yes | (Immutable) Inclusive end date (`YYYY-MM-DD`, UTC). |
130
+ | dryRun | boolean | yes | (Immutable) When true, rollup writes are skipped — only source counts are returned for diffing. |
131
+ | processedDates | array<string> | yes | Ordered list of `YYYY-MM-DD` dates already processed by this run. |
132
+ | errors | array<object> | yes | Per-date errors collected during the run. |
133
+ | errors[].date | string | yes | `YYYY-MM-DD` date that failed. |
134
+ | errors[].message | string | yes | Error message captured at failure. |
135
+ | errors[].at | FirestoreTimestamp | yes | When the error occurred. |
136
+ | triggeredBy | string | yes | (Immutable) UID of the admin who triggered the backfill. |
137
+ | startedAt | FirestoreTimestamp | yes | (Read-only) When the run actually started. |
138
+ | completedAt | any | no | (Read-only) When the run reached a terminal status. |
139
+
140
+ Example:
141
+ ```json
142
+ {
143
+ "id": null,
144
+ "companyId": "comp_xyz789",
145
+ "siteId": "sit_ref123",
146
+ "status": "pending",
147
+ "from": "from",
148
+ "to": "to",
149
+ "dryRun": true,
150
+ "processedDates": [
151
+ "example"
152
+ ],
153
+ "errors": [
154
+ {
155
+ "date": "2026-02-15",
156
+ "message": "message",
157
+ "at": "at"
158
+ }
159
+ ],
160
+ "triggeredBy": "triggeredBy",
161
+ "startedAt": "startedAt",
162
+ "completedAt": "completedAt"
163
+ }
164
+ ```
165
+
166
+ ### AnalyticsDaily
167
+ Fields: 16 (14 required)
168
+
169
+ | Field | Type | Required | Description |
170
+ |-------|------|----------|-------------|
171
+ | totalEvents | integer | yes | Total event count within the bucket. |
172
+ | pageViews | integer | yes | Count of `page_view` + `screen_view` events. |
173
+ | sessions | integer | yes | Distinct session count within the bucket (by `sessionId`). |
174
+ | uniqueUsers | integer | yes | Distinct identified `userId` count. Anonymous sessions (userId=null) are excluded. |
175
+ | anonymousSessions | integer | yes | Distinct session count where `userId` was null for the entire session. |
176
+ | orders | integer | yes | Count of `order_submitted` events (or Order documents scoped to this site, D43) within the bucket. |
177
+ | paymentsCompleted | integer | yes | Count of `payment_completed` events within the bucket. |
178
+ | paymentsFailed | integer | yes | Count of `payment_failed` events within the bucket. |
179
+ | errors | integer | yes | Count of `error_occurred` + `exception_caught` events within the bucket. |
180
+ | eventCounts | record<string,integer> | yes | Per-event-name counts — key is the event name (canonical or custom), value is the count within the bucket. |
181
+ | id | ['string', 'null'] | no | (Read-only) Firestore document ID = `YYYY-MM-DD`. |
182
+ | companyId | string | yes | (Immutable) FK → Company document ID. |
183
+ | siteId | string | yes | (Immutable) FK → Site document ID (D40). |
184
+ | date | string | yes | (Immutable) `YYYY-MM-DD` UTC — matches document ID. |
185
+ | computedAt | FirestoreTimestamp | yes | (Read-only) When this rollup was last (re)computed. Updated on every set/merge. |
186
+ | sourceEventCount | ['integer', 'null'] | no | (Read-only, Optional) Total source events scanned when producing this rollup. Useful for dry-run diffing. |
187
+
188
+ Example:
189
+ ```json
190
+ {
191
+ "totalEvents": 0,
192
+ "pageViews": 0,
193
+ "sessions": 0,
194
+ "uniqueUsers": 0,
195
+ "anonymousSessions": 0,
196
+ "orders": 0,
197
+ "paymentsCompleted": 0,
198
+ "paymentsFailed": 0,
199
+ "errors": 0,
200
+ "eventCounts": {},
201
+ "id": null,
202
+ "companyId": "comp_xyz789",
203
+ "siteId": "sit_ref123",
204
+ "date": "2026-02-15",
205
+ "computedAt": "computedAt",
206
+ "sourceEventCount": null
207
+ }
208
+ ```
209
+
210
+ ### AnalyticsEvent
211
+ Fields: 12 (10 required)
212
+
213
+ | Field | Type | Required | Description |
214
+ |-------|------|----------|-------------|
215
+ | id | ['string', 'null'] | no | (Read-only) Firestore document ID. Equal to eventId. |
216
+ | companyId | string | yes | (Immutable) FK → Company document ID. |
217
+ | siteId | string | yes | (Immutable) FK → Site document ID (D40 sub-tenant scope). |
218
+ | eventId | string | yes | (Immutable) Client-generated UUID. Matches document ID. |
219
+ | eventName | string | yes | Event name. Free string; canonical vocabulary is encouraged (see CANONICAL_ANALYTICS_EVENT_NAMES). |
220
+ | timestamp | string | yes | Client-side ISO 8601 — "when it happened on device". Can diverge from serverTimestamp for queued offline events. |
221
+ | serverTimestamp | FirestoreTimestamp | yes | (Read-only) Firestore serverTimestamp() — authoritative for ordering. |
222
+ | sessionId | string | yes | 30-minute rolling session identifier. |
223
+ | userId | any | yes | Stable user ID if identified; explicitly null (not omitted) for anonymous sessions — simplifies querying. |
224
+ | properties | object | yes | Free-form event-specific payload. Contract is at the event-name level. PII posture: follow-up issue pending. |
225
+ | context | object | yes | Environment context at event time (nested per D40). |
226
+ | context.url | string | no | Full URL at event time. |
227
+ | context.origin | string | no | Protocol + host. |
228
+ | context.hostname | string | no | Hostname only — useful for deployment identification. |
229
+ | context.page | string | no | Current route/pathname. |
230
+ | context.title | string | no | Page title. |
231
+ | context.referrer | string | no | Referrer URL. |
232
+ | context.deviceType | enum(3) | no | |
233
+ | context.platform | string | no | OS/platform string. |
234
+ | context.screenWidth | integer | no | Viewport width (flattened from screen.width). |
235
+ | context.screenHeight | integer | no | Viewport height (flattened from screen.height). |
236
+ | context.appVersion | string | no | Consuming app version. |
237
+ | context.online | boolean | no | Connectivity state at event time. |
238
+ | context.locale | string | no | User locale. |
239
+ | context.timezone | string | no | IANA timezone. |
240
+ | context.utm | any | no | UTM parameters; null when not a campaign-landed session. |
241
+ | userTraits | ['object', 'null'] | no | Optional — included only when includeUserTraits: true on the write. PII posture: follow-up issue pending. |
242
+
243
+ Example:
244
+ ```json
245
+ {
246
+ "id": null,
247
+ "companyId": "comp_xyz789",
248
+ "siteId": "sit_ref123",
249
+ "eventId": "eve_ref123",
250
+ "eventName": "eventName",
251
+ "timestamp": "timestamp",
252
+ "serverTimestamp": "serverTimestamp",
253
+ "sessionId": "ses_ref123",
254
+ "userId": "use_ref123",
255
+ "properties": {},
256
+ "context": {},
257
+ "userTraits": null
258
+ }
259
+ ```
260
+
261
+ ### AnalyticsHourly
262
+ Fields: 17 (15 required)
263
+
264
+ | Field | Type | Required | Description |
265
+ |-------|------|----------|-------------|
266
+ | totalEvents | integer | yes | Total event count within the bucket. |
267
+ | pageViews | integer | yes | Count of `page_view` + `screen_view` events. |
268
+ | sessions | integer | yes | Distinct session count within the bucket (by `sessionId`). |
269
+ | uniqueUsers | integer | yes | Distinct identified `userId` count. Anonymous sessions (userId=null) are excluded. |
270
+ | anonymousSessions | integer | yes | Distinct session count where `userId` was null for the entire session. |
271
+ | orders | integer | yes | Count of `order_submitted` events (or Order documents scoped to this site, D43) within the bucket. |
272
+ | paymentsCompleted | integer | yes | Count of `payment_completed` events within the bucket. |
273
+ | paymentsFailed | integer | yes | Count of `payment_failed` events within the bucket. |
274
+ | errors | integer | yes | Count of `error_occurred` + `exception_caught` events within the bucket. |
275
+ | eventCounts | record<string,integer> | yes | Per-event-name counts — key is the event name (canonical or custom), value is the count within the bucket. |
276
+ | id | ['string', 'null'] | no | (Read-only) Firestore document ID = `YYYY-MM-DD-HH`. |
277
+ | companyId | string | yes | (Immutable) FK → Company document ID. |
278
+ | siteId | string | yes | (Immutable) FK → Site document ID (D40). |
279
+ | date | string | yes | (Immutable) `YYYY-MM-DD` UTC for the bucket day. |
280
+ | hour | integer | yes | (Immutable) UTC hour of day, 0–23. |
281
+ | computedAt | FirestoreTimestamp | yes | (Read-only) When this rollup was last (re)computed. |
282
+ | sourceEventCount | ['integer', 'null'] | no | (Read-only, Optional) Total source events scanned when producing this rollup. |
283
+
284
+ Example:
285
+ ```json
286
+ {
287
+ "totalEvents": 0,
288
+ "pageViews": 0,
289
+ "sessions": 0,
290
+ "uniqueUsers": 0,
291
+ "anonymousSessions": 0,
292
+ "orders": 0,
293
+ "paymentsCompleted": 0,
294
+ "paymentsFailed": 0,
295
+ "errors": 0,
296
+ "eventCounts": {},
297
+ "id": null,
298
+ "companyId": "comp_xyz789",
299
+ "siteId": "sit_ref123",
300
+ "date": "2026-02-15",
301
+ "hour": 0,
302
+ "computedAt": "computedAt",
303
+ "sourceEventCount": null
304
+ }
305
+ ```
306
+
73
307
  ### Booking
74
308
  Fields: 52 (7 required)
75
309
 
@@ -586,6 +820,48 @@ Example:
586
820
  }
587
821
  ```
588
822
 
823
+ ### MagicLinkRequest
824
+ Fields: 15 (11 required)
825
+
826
+ | Field | Type | Required | Description |
827
+ |-------|------|----------|-------------|
828
+ | id | ['string', 'null'] | no | (Read-only) Firestore document ID. 16-byte random hex. |
829
+ | companyId | string | yes | (Immutable) FK → Company document ID. |
830
+ | siteId | string | yes | (Immutable) FK → Site document ID (D40 sub-tenant scope). |
831
+ | email | string | yes | Populated if identifier is email; empty string if phone. Mutually exclusive with phone per document. |
832
+ | phone | string | yes | Populated if identifier is E.164 format (starts with +); empty string if email. |
833
+ | ip | string | yes | Client IP from X-Forwarded-For or RemoteAddr. |
834
+ | allowed | boolean | yes | Whether the user passed the access control check. |
835
+ | link | string | yes | Magic-link URL — non-empty only when DEV_MODE=true; empty in production (audit integrity). |
836
+ | tier | string | yes | Access tier selected by the user. Free string per site (ING-304 open question — tier values are site-specific today). |
837
+ | authStatusReason | string | yes | Outcome reason code: whitelist | allowed_users | payment_redirect | payment_complete | not_allowed | redirected_to_payment. |
838
+ | referredBy | string | yes | Contact identifier of the referrer, resolved from submitted referral_code; empty if none. |
839
+ | requestedAt | FirestoreTimestamp | yes | RFC3339Nano UTC at request time. |
840
+ | verifyReason | ['string', 'null'] | no | Verify outcome reason: mirrors authStatusReason on success, or link_expired | link_invalid | not_allowed. |
841
+ | verified | ['boolean', 'null'] | no | Whether the verify attempt succeeded. |
842
+ | verifiedAt | any | no | RFC3339Nano UTC at verify time. |
843
+
844
+ Example:
845
+ ```json
846
+ {
847
+ "id": null,
848
+ "companyId": "comp_xyz789",
849
+ "siteId": "sit_ref123",
850
+ "email": "amadou@example.com",
851
+ "phone": "+225 07 00 11 22",
852
+ "ip": "ip",
853
+ "allowed": true,
854
+ "link": "link",
855
+ "tier": "Gold",
856
+ "authStatusReason": "Customer request",
857
+ "referredBy": "referredBy",
858
+ "requestedAt": "requestedAt",
859
+ "verifyReason": null,
860
+ "verified": null,
861
+ "verifiedAt": "verifiedAt"
862
+ }
863
+ ```
864
+
589
865
  ### MetricsCurrent
590
866
  Fields: 25 (25 required)
591
867
 
@@ -777,13 +1053,14 @@ Example:
777
1053
  ```
778
1054
 
779
1055
  ### Order
780
- Fields: 47 (8 required)
1056
+ Fields: 48 (8 required)
781
1057
 
782
1058
  | Field | Type | Required | Description |
783
1059
  |-------|------|----------|-------------|
784
1060
  | id | string | yes | (Read-only) Firestore document ID. Note: some models also have uid; see ID conventions. |
785
1061
  | uid | string | yes | (Read-only) Entity UID. Often mirrors id. |
786
1062
  | companyId | string | yes | (Immutable) FK → Company document ID. Scopes all queries. |
1063
+ | siteId | ['string', 'null'] | no | (Immutable, Optional) FK → Site document ID (D43 / ADR-003). null = company-wide. |
787
1064
  | orderNumber | string | yes | (Read-only) Server-generated order number. |
788
1065
  | status | OrderStatus | yes | Core lifecycle status (D34, MIG-11). See OrderStatus enum for the legacy value migration mapping. |
789
1066
  | paymentStatus | any | no | Payment lifecycle (D34). Null until payment is initiated. |
@@ -835,6 +1112,7 @@ Example:
835
1112
  "id": "bk_abc123def456",
836
1113
  "uid": "user_u8x92kqm",
837
1114
  "companyId": "comp_xyz789",
1115
+ "siteId": null,
838
1116
  "orderNumber": "ORD-2026-0042",
839
1117
  "status": "status",
840
1118
  "paymentStatus": "paymentStatus",
@@ -954,6 +1232,88 @@ Example:
954
1232
  }
955
1233
  ```
956
1234
 
1235
+ ### Site
1236
+ Fields: 14 (6 required)
1237
+
1238
+ | Field | Type | Required | Description |
1239
+ |-------|------|----------|-------------|
1240
+ | id | ['string', 'null'] | no | (Read-only) Firestore document ID, auto-generated. |
1241
+ | companyId | string | yes | (Immutable) FK → Company document ID. Scopes all site sub-collections. |
1242
+ | name | string | yes | Human-readable site name shown in dashboards. |
1243
+ | description | ['string', 'null'] | no | Optional freeform description. |
1244
+ | status | SiteStatus | yes | Lifecycle status (D41). Clients filter by ACTIVE. |
1245
+ | deploymentLinks | array<object> | yes | Ordered list of deployment URLs (web, mobile, PWA, store links). |
1246
+ | deploymentLinks[].label | string | yes | Human-readable label shown in dashboards (e.g. "Production", "Staging"). |
1247
+ | deploymentLinks[].url | string | yes | Absolute URL or store link. |
1248
+ | deploymentLinks[].type | DeploymentLinkType | yes | Link category — drives icon/handler selection. |
1249
+ | deploymentLinks[].isPrimary | boolean | no | If true, this link is used as the canonical deployment URL for the Site. |
1250
+ | expiresAt | any | no | Optional expiration timestamp. When set and elapsed, `isExpired` flips true and status typically moves to EXPIRED. |
1251
+ | isExpired | ['boolean', 'null'] | no | (Read-only) Derived — true when `expiresAt` is in the past. Maintained by server trigger. |
1252
+ | createdAt | any | no | (Read-only) Server-generated creation timestamp. |
1253
+ | updatedAt | any | no | (Read-only) Server-generated update timestamp. |
1254
+ | createdBy | string | yes | (Immutable) FK → User/staff UID who created the site. |
1255
+ | analyticsEnabled | boolean | yes | Feature flag — when false, clients should not emit analytics events for this site. |
1256
+ | lastAnalyticsSync | any | no | (Read-only) Last time the analytics rollup pipeline refreshed `cachedMetrics`. |
1257
+ | cachedMetrics | ['object', 'null'] | no | (Read-only, Denormalized) Cached metrics snapshot for quick dashboard rendering. |
1258
+
1259
+ Example:
1260
+ ```json
1261
+ {
1262
+ "id": null,
1263
+ "companyId": "comp_xyz789",
1264
+ "name": "Amadou Diallo",
1265
+ "description": null,
1266
+ "status": "status",
1267
+ "deploymentLinks": [
1268
+ {
1269
+ "label": "label",
1270
+ "url": "https://storage.example.com/url.jpg",
1271
+ "type": "phone"
1272
+ }
1273
+ ],
1274
+ "expiresAt": "expiresAt",
1275
+ "isExpired": null,
1276
+ "createdAt": "createdAt",
1277
+ "updatedAt": "updatedAt",
1278
+ "createdBy": "staff_k0f1",
1279
+ "analyticsEnabled": true,
1280
+ "lastAnalyticsSync": "lastAnalyticsSync",
1281
+ "cachedMetrics": null
1282
+ }
1283
+ ```
1284
+
1285
+ ### SitePayment
1286
+ Fields: 10 (9 required)
1287
+
1288
+ | Field | Type | Required | Description |
1289
+ |-------|------|----------|-------------|
1290
+ | id | ['string', 'null'] | no | (Read-only) Firestore document ID, auto-generated. |
1291
+ | companyId | string | yes | (Immutable) FK → Company document ID. |
1292
+ | siteId | string | yes | (Immutable) FK → Site document ID (D40 sub-tenant scope). |
1293
+ | contact | string | yes | Email or E.164 phone number. Matches AllowedUser.contact. |
1294
+ | sessionId | string | yes | Payment provider checkout session ID (e.g. Wave). Usable for dedup across dual writes. |
1295
+ | transactionId | string | yes | Payment provider transaction ID. |
1296
+ | tier | string | yes | Access tier. Free string per site (ING-304 open question). |
1297
+ | amount | number | yes | Amount paid. Generalized from amount_xof per D40 decision. |
1298
+ | currency | string | yes | Currency code (ISO 4217). Defaults to XOF for legacy SR-Single parity. |
1299
+ | paidAt | FirestoreTimestamp | yes | RFC3339Nano UTC when payment was completed. |
1300
+
1301
+ Example:
1302
+ ```json
1303
+ {
1304
+ "id": null,
1305
+ "companyId": "comp_xyz789",
1306
+ "siteId": "sit_ref123",
1307
+ "contact": "contact",
1308
+ "sessionId": "ses_ref123",
1309
+ "transactionId": "tra_ref123",
1310
+ "tier": "Gold",
1311
+ "amount": 45000,
1312
+ "currency": "XOF",
1313
+ "paidAt": "pai_ref123"
1314
+ }
1315
+ ```
1316
+
957
1317
  ### Ticket
958
1318
  Fields: 16 (6 required)
959
1319