@ingenx-io/valets-schema-mcp-server 0.2.4 → 0.2.6
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/data/docs/collections/firestore-paths.md +32 -20
- package/data/docs/enums/app-status.md +24 -0
- package/data/docs/enums/attention-status.md +2 -2
- package/data/docs/enums/booking-status.md +2 -2
- package/data/docs/enums/contract-status.md +24 -0
- package/data/docs/enums/customer-payment-status.md +2 -2
- package/data/docs/enums/customer-payment-target-type.md +2 -2
- package/data/docs/enums/delivery-type.md +2 -2
- package/data/docs/enums/deployment-link-type.md +2 -2
- package/data/docs/enums/event-status.md +2 -2
- package/data/docs/enums/fulfillment-status.md +2 -2
- package/data/docs/enums/loyalty-transaction-type.md +2 -2
- package/data/docs/enums/milestone-status.md +23 -0
- package/data/docs/enums/notification-channel.md +2 -2
- package/data/docs/enums/notification-entity-type.md +2 -2
- package/data/docs/enums/notification-status.md +2 -2
- package/data/docs/enums/order-status.md +2 -2
- package/data/docs/enums/outbound-message-format.md +2 -2
- package/data/docs/enums/outbound-message-purpose.md +2 -2
- package/data/docs/enums/outbound-message-status.md +2 -2
- package/data/docs/enums/payment-method.md +2 -2
- package/data/docs/enums/payment-proof-status.md +2 -2
- package/data/docs/enums/payment-status.md +2 -2
- package/data/docs/enums/pending-issue.md +2 -2
- package/data/docs/enums/return-status.md +2 -2
- package/data/docs/enums/session-status.md +2 -2
- package/data/docs/enums/site-status.md +2 -2
- package/data/docs/enums/stocktake-frequency.md +2 -2
- package/data/docs/enums/stocktake-item-status.md +2 -2
- package/data/docs/enums/stocktake-status.md +2 -2
- package/data/docs/enums/ticket-status.md +2 -2
- package/data/docs/enums/waba-label.md +3 -3
- package/data/docs/enums/whatsapp-button-sub-type.md +2 -2
- package/data/docs/enums/whatsapp-template-component.md +2 -2
- package/data/docs/enums/whatsapp-template-status.md +2 -2
- package/data/docs/index.md +17 -7
- package/data/docs/models/allowed-user.md +7 -7
- package/data/docs/models/analytics-backfill.md +7 -7
- package/data/docs/models/analytics-daily.md +6 -6
- package/data/docs/models/analytics-event.md +7 -7
- package/data/docs/models/analytics-hourly.md +6 -6
- package/data/docs/models/app-payment.md +200 -0
- package/data/docs/models/app.md +585 -0
- package/data/docs/models/booking-version.md +2 -2
- package/data/docs/models/booking.md +127 -127
- package/data/docs/models/contract.md +454 -0
- package/data/docs/models/customer-payment-allocation.md +20 -20
- package/data/docs/models/customer-payment.md +23 -23
- package/data/docs/models/customer.md +11 -11
- package/data/docs/models/event.md +22 -22
- package/data/docs/models/loyalty-config.md +4 -4
- package/data/docs/models/loyalty-reward.md +3 -3
- package/data/docs/models/loyalty-status.md +6 -6
- package/data/docs/models/loyalty-transaction.md +2 -2
- package/data/docs/models/magic-link-request.md +9 -9
- package/data/docs/models/metrics-current.md +169 -37
- package/data/docs/models/metrics-daily.md +172 -40
- package/data/docs/models/metrics-monthly.md +172 -40
- package/data/docs/models/notification-record.md +3 -3
- package/data/docs/models/order-item.md +6 -6
- package/data/docs/models/order.md +314 -294
- package/data/docs/models/outbound-payment-allocation.md +195 -0
- package/data/docs/models/outbound-payment.md +318 -0
- package/data/docs/models/payment-webhook-endpoint.md +191 -0
- package/data/docs/models/sale.md +18 -18
- package/data/docs/models/site-payment.md +2 -2
- package/data/docs/models/site.md +2 -2
- package/data/docs/models/stocktake-item.md +4 -4
- package/data/docs/models/stocktake.md +5 -5
- package/data/docs/models/ticket.md +3 -3
- package/data/docs/models/user.md +249 -0
- package/data/docs/models/whatsapp-inbound-message.md +6 -2
- package/data/docs/models/whatsapp-outbound-lifecycle-event.md +2 -2
- package/data/docs/models/whatsapp-outbound-message.md +43 -27
- package/data/docs/models/whatsapp-template.md +6 -2
- package/data/static/llms.txt +322 -36
- package/data/static/openapi.yaml +1074 -66
- package/data/static/schemas.json +1183 -104
- package/package.json +1 -1
package/data/static/schemas.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"description": "@valets/schema \u2014 consolidated schema bundle",
|
|
4
|
-
"generated": "2026-05-
|
|
4
|
+
"generated": "2026-05-30T13:38:45.949066+00:00",
|
|
5
5
|
"schemas": {
|
|
6
6
|
"allowed-user": {
|
|
7
7
|
"type": "object",
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
"x-immutable": true,
|
|
20
20
|
"description": "(Immutable) FK \u2192 Company document ID."
|
|
21
21
|
},
|
|
22
|
-
"
|
|
22
|
+
"appId": {
|
|
23
23
|
"type": "string",
|
|
24
24
|
"x-immutable": true,
|
|
25
|
-
"description": "(Immutable) FK \u2192
|
|
25
|
+
"description": "(Immutable) FK \u2192 App document ID (D40 sub-tenant scope, renamed per D44)."
|
|
26
26
|
},
|
|
27
27
|
"contact": {
|
|
28
28
|
"type": "string",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"required": [
|
|
54
54
|
"companyId",
|
|
55
|
-
"
|
|
55
|
+
"appId",
|
|
56
56
|
"contact",
|
|
57
57
|
"tier",
|
|
58
58
|
"amount",
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
"paidAt"
|
|
62
62
|
],
|
|
63
63
|
"additionalProperties": false,
|
|
64
|
-
"description": "AllowedUser model (D40 / ING-304). Collection: companies/{companyId}/
|
|
64
|
+
"description": "AllowedUser model (D40 / ING-304). Collection: companies/{companyId}/apps/{appId}/allowed_users/{contactId}. Authoritative paid-access allowlist. Upsert semantics \u2014 tier upgrades overwrite. Source of truth for access checks and referral code resolution.",
|
|
65
65
|
"example": {
|
|
66
66
|
"id": null,
|
|
67
67
|
"companyId": "comp_xyz789",
|
|
68
|
-
"
|
|
68
|
+
"appId": "app_ref123",
|
|
69
69
|
"contact": "contact",
|
|
70
70
|
"tier": "Gold",
|
|
71
71
|
"amount": 45000,
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"x-immutable": true,
|
|
91
91
|
"description": "(Immutable) FK \u2192 Company document ID."
|
|
92
92
|
},
|
|
93
|
-
"
|
|
93
|
+
"appId": {
|
|
94
94
|
"type": "string",
|
|
95
95
|
"x-immutable": true,
|
|
96
96
|
"description": "(Immutable) FK \u2192 Site document ID (D40)."
|
|
@@ -180,7 +180,7 @@
|
|
|
180
180
|
},
|
|
181
181
|
"required": [
|
|
182
182
|
"companyId",
|
|
183
|
-
"
|
|
183
|
+
"appId",
|
|
184
184
|
"status",
|
|
185
185
|
"from",
|
|
186
186
|
"to",
|
|
@@ -191,11 +191,11 @@
|
|
|
191
191
|
"startedAt"
|
|
192
192
|
],
|
|
193
193
|
"additionalProperties": false,
|
|
194
|
-
"description": "AnalyticsBackfill run (D42 / ING-304). Collection: companies/{companyId}/
|
|
194
|
+
"description": "AnalyticsBackfill run (D42 / ING-304). Collection: companies/{companyId}/apps/{appId}/analytics_backfills/{runId}. Tracks admin-triggered rollup backfill jobs; supports dry-run.",
|
|
195
195
|
"example": {
|
|
196
196
|
"id": null,
|
|
197
197
|
"companyId": "comp_xyz789",
|
|
198
|
-
"
|
|
198
|
+
"appId": "app_ref123",
|
|
199
199
|
"status": "pending",
|
|
200
200
|
"from": "from",
|
|
201
201
|
"to": "to",
|
|
@@ -297,7 +297,7 @@
|
|
|
297
297
|
"x-immutable": true,
|
|
298
298
|
"description": "(Immutable) FK \u2192 Company document ID."
|
|
299
299
|
},
|
|
300
|
-
"
|
|
300
|
+
"appId": {
|
|
301
301
|
"type": "string",
|
|
302
302
|
"x-immutable": true,
|
|
303
303
|
"description": "(Immutable) FK \u2192 Site document ID (D40)."
|
|
@@ -335,12 +335,12 @@
|
|
|
335
335
|
"errors",
|
|
336
336
|
"eventCounts",
|
|
337
337
|
"companyId",
|
|
338
|
-
"
|
|
338
|
+
"appId",
|
|
339
339
|
"date",
|
|
340
340
|
"computedAt"
|
|
341
341
|
],
|
|
342
342
|
"additionalProperties": false,
|
|
343
|
-
"description": "AnalyticsDaily rollup (D42 / ING-304). Collection: companies/{companyId}/
|
|
343
|
+
"description": "AnalyticsDaily rollup (D42 / ING-304). Collection: companies/{companyId}/apps/{appId}/analytics_daily/{YYYY-MM-DD}. Idempotent set/merge \u2014 reruns overwrite. Fall back to raw analytics_events for uncovered slices.",
|
|
344
344
|
"example": {
|
|
345
345
|
"totalEvents": 0,
|
|
346
346
|
"pageViews": 0,
|
|
@@ -354,7 +354,7 @@
|
|
|
354
354
|
"eventCounts": {},
|
|
355
355
|
"id": null,
|
|
356
356
|
"companyId": "comp_xyz789",
|
|
357
|
-
"
|
|
357
|
+
"appId": "app_ref123",
|
|
358
358
|
"date": "2026-02-15",
|
|
359
359
|
"computedAt": "computedAt",
|
|
360
360
|
"sourceEventCount": null
|
|
@@ -376,10 +376,10 @@
|
|
|
376
376
|
"x-immutable": true,
|
|
377
377
|
"description": "(Immutable) FK \u2192 Company document ID."
|
|
378
378
|
},
|
|
379
|
-
"
|
|
379
|
+
"appId": {
|
|
380
380
|
"type": "string",
|
|
381
381
|
"x-immutable": true,
|
|
382
|
-
"description": "(Immutable) FK \u2192
|
|
382
|
+
"description": "(Immutable) FK \u2192 App document ID (D40 sub-tenant scope, renamed per D44)."
|
|
383
383
|
},
|
|
384
384
|
"eventId": {
|
|
385
385
|
"type": "string",
|
|
@@ -536,7 +536,7 @@
|
|
|
536
536
|
},
|
|
537
537
|
"required": [
|
|
538
538
|
"companyId",
|
|
539
|
-
"
|
|
539
|
+
"appId",
|
|
540
540
|
"eventId",
|
|
541
541
|
"eventName",
|
|
542
542
|
"timestamp",
|
|
@@ -547,11 +547,11 @@
|
|
|
547
547
|
"context"
|
|
548
548
|
],
|
|
549
549
|
"additionalProperties": false,
|
|
550
|
-
"description": "AnalyticsEvent model (D40 / ING-304). Collection: companies/{companyId}/
|
|
550
|
+
"description": "AnalyticsEvent model (D40 / ING-304). Collection: companies/{companyId}/apps/{appId}/analytics_events/{eventId}. Append-only, immutable product/behavior event stream. Event names are free strings; canonical vocabulary in CANONICAL_ANALYTICS_EVENT_NAMES.",
|
|
551
551
|
"example": {
|
|
552
552
|
"id": null,
|
|
553
553
|
"companyId": "comp_xyz789",
|
|
554
|
-
"
|
|
554
|
+
"appId": "app_ref123",
|
|
555
555
|
"eventId": "eve_ref123",
|
|
556
556
|
"eventName": "eventName",
|
|
557
557
|
"timestamp": "timestamp",
|
|
@@ -645,7 +645,7 @@
|
|
|
645
645
|
"x-immutable": true,
|
|
646
646
|
"description": "(Immutable) FK \u2192 Company document ID."
|
|
647
647
|
},
|
|
648
|
-
"
|
|
648
|
+
"appId": {
|
|
649
649
|
"type": "string",
|
|
650
650
|
"x-immutable": true,
|
|
651
651
|
"description": "(Immutable) FK \u2192 Site document ID (D40)."
|
|
@@ -690,13 +690,13 @@
|
|
|
690
690
|
"errors",
|
|
691
691
|
"eventCounts",
|
|
692
692
|
"companyId",
|
|
693
|
-
"
|
|
693
|
+
"appId",
|
|
694
694
|
"date",
|
|
695
695
|
"hour",
|
|
696
696
|
"computedAt"
|
|
697
697
|
],
|
|
698
698
|
"additionalProperties": false,
|
|
699
|
-
"description": "AnalyticsHourly rollup (D42 / ING-304). Collection: companies/{companyId}/
|
|
699
|
+
"description": "AnalyticsHourly rollup (D42 / ING-304). Collection: companies/{companyId}/apps/{appId}/analytics_hourly/{YYYY-MM-DD-HH}. Scheduled-CF writer; idempotent set/merge.",
|
|
700
700
|
"example": {
|
|
701
701
|
"totalEvents": 0,
|
|
702
702
|
"pageViews": 0,
|
|
@@ -710,13 +710,342 @@
|
|
|
710
710
|
"eventCounts": {},
|
|
711
711
|
"id": null,
|
|
712
712
|
"companyId": "comp_xyz789",
|
|
713
|
-
"
|
|
713
|
+
"appId": "app_ref123",
|
|
714
714
|
"date": "2026-02-15",
|
|
715
715
|
"hour": 0,
|
|
716
716
|
"computedAt": "computedAt",
|
|
717
717
|
"sourceEventCount": null
|
|
718
718
|
}
|
|
719
719
|
},
|
|
720
|
+
"app": {
|
|
721
|
+
"type": "object",
|
|
722
|
+
"properties": {
|
|
723
|
+
"id": {
|
|
724
|
+
"readOnly": true,
|
|
725
|
+
"description": "(Read-only) Firestore document ID, auto-generated.",
|
|
726
|
+
"type": [
|
|
727
|
+
"string",
|
|
728
|
+
"null"
|
|
729
|
+
]
|
|
730
|
+
},
|
|
731
|
+
"companyId": {
|
|
732
|
+
"type": "string",
|
|
733
|
+
"x-immutable": true,
|
|
734
|
+
"description": "(Immutable) FK \u2192 Company document ID. Scopes all app sub-collections."
|
|
735
|
+
},
|
|
736
|
+
"name": {
|
|
737
|
+
"type": "string",
|
|
738
|
+
"description": "Human-readable app name shown in dashboards."
|
|
739
|
+
},
|
|
740
|
+
"description": {
|
|
741
|
+
"description": "Optional freeform description.",
|
|
742
|
+
"type": [
|
|
743
|
+
"string",
|
|
744
|
+
"null"
|
|
745
|
+
]
|
|
746
|
+
},
|
|
747
|
+
"status": {
|
|
748
|
+
"anyOf": [
|
|
749
|
+
{
|
|
750
|
+
"$ref": "#/definitions/app-status"
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
"type": "null"
|
|
754
|
+
}
|
|
755
|
+
],
|
|
756
|
+
"x-note": "Real Firestore documents may be missing this field on pre-backfill records (#26). Treat absent as ACTIVE. A backfill script will populate status=ACTIVE on all existing documents; once confirmed, this field will be promoted to required.",
|
|
757
|
+
"x-when": "Written by operators via dashboard or by server triggers (expiration). Clients must not use server-side `where(status == ACTIVE)` until backfill is complete \u2014 filter client-side instead, treating absent as ACTIVE.",
|
|
758
|
+
"description": "Lifecycle status (D41/D44). Optional pending backfill of pre-existing records (#26) \u2014 treat absent as ACTIVE. Clients filter by ACTIVE.",
|
|
759
|
+
"x-see": {
|
|
760
|
+
"decisions": [
|
|
761
|
+
"D41",
|
|
762
|
+
"D44"
|
|
763
|
+
]
|
|
764
|
+
}
|
|
765
|
+
},
|
|
766
|
+
"deploymentLinks": {
|
|
767
|
+
"type": "array",
|
|
768
|
+
"items": {
|
|
769
|
+
"type": "object",
|
|
770
|
+
"properties": {
|
|
771
|
+
"label": {
|
|
772
|
+
"type": "string",
|
|
773
|
+
"description": "Human-readable label shown in dashboards (e.g. \"Production\", \"Staging\")."
|
|
774
|
+
},
|
|
775
|
+
"url": {
|
|
776
|
+
"type": "string",
|
|
777
|
+
"description": "Absolute URL or store link."
|
|
778
|
+
},
|
|
779
|
+
"type": {
|
|
780
|
+
"$ref": "#/definitions/deployment-link-type",
|
|
781
|
+
"description": "Link category \u2014 drives icon/handler selection.",
|
|
782
|
+
"x-note": "Lowercase by convention \u2014 deployment link types are display-oriented labels, not lifecycle states.",
|
|
783
|
+
"x-see": {
|
|
784
|
+
"decisions": [
|
|
785
|
+
"D41"
|
|
786
|
+
]
|
|
787
|
+
}
|
|
788
|
+
},
|
|
789
|
+
"isPrimary": {
|
|
790
|
+
"description": "If true, this link is used as the canonical deployment URL for the App.",
|
|
791
|
+
"type": "boolean"
|
|
792
|
+
}
|
|
793
|
+
},
|
|
794
|
+
"required": [
|
|
795
|
+
"label",
|
|
796
|
+
"url",
|
|
797
|
+
"type"
|
|
798
|
+
],
|
|
799
|
+
"additionalProperties": false,
|
|
800
|
+
"description": "Deployment link entry embedded on App.deploymentLinks[] (D41)."
|
|
801
|
+
},
|
|
802
|
+
"description": "Ordered list of deployment URLs (web, mobile, PWA, store links)."
|
|
803
|
+
},
|
|
804
|
+
"expiresAt": {
|
|
805
|
+
"anyOf": [
|
|
806
|
+
{
|
|
807
|
+
"$ref": "#/definitions/firestore-timestamp"
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
"type": "null"
|
|
811
|
+
}
|
|
812
|
+
],
|
|
813
|
+
"description": "Optional expiration timestamp. When set and elapsed, `isExpired` flips true and status typically moves to EXPIRED."
|
|
814
|
+
},
|
|
815
|
+
"isExpired": {
|
|
816
|
+
"readOnly": true,
|
|
817
|
+
"description": "(Read-only) Derived \u2014 true when `expiresAt` is in the past. Maintained by server trigger.",
|
|
818
|
+
"type": [
|
|
819
|
+
"boolean",
|
|
820
|
+
"null"
|
|
821
|
+
]
|
|
822
|
+
},
|
|
823
|
+
"createdAt": {
|
|
824
|
+
"anyOf": [
|
|
825
|
+
{
|
|
826
|
+
"$ref": "#/definitions/firestore-timestamp"
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
"type": "null"
|
|
830
|
+
}
|
|
831
|
+
],
|
|
832
|
+
"readOnly": true,
|
|
833
|
+
"description": "(Read-only) Server-generated creation timestamp."
|
|
834
|
+
},
|
|
835
|
+
"updatedAt": {
|
|
836
|
+
"anyOf": [
|
|
837
|
+
{
|
|
838
|
+
"$ref": "#/definitions/firestore-timestamp"
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
"type": "null"
|
|
842
|
+
}
|
|
843
|
+
],
|
|
844
|
+
"readOnly": true,
|
|
845
|
+
"description": "(Read-only) Server-generated update timestamp."
|
|
846
|
+
},
|
|
847
|
+
"createdBy": {
|
|
848
|
+
"type": "string",
|
|
849
|
+
"x-immutable": true,
|
|
850
|
+
"description": "(Immutable) FK \u2192 User/staff UID who created the app."
|
|
851
|
+
},
|
|
852
|
+
"analyticsEnabled": {
|
|
853
|
+
"type": "boolean",
|
|
854
|
+
"description": "Feature flag \u2014 when false, clients should not emit analytics events for this app."
|
|
855
|
+
},
|
|
856
|
+
"lastAnalyticsSync": {
|
|
857
|
+
"anyOf": [
|
|
858
|
+
{
|
|
859
|
+
"$ref": "#/definitions/firestore-timestamp"
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
"type": "null"
|
|
863
|
+
}
|
|
864
|
+
],
|
|
865
|
+
"readOnly": true,
|
|
866
|
+
"description": "(Read-only) Last time the analytics rollup pipeline refreshed `cachedMetrics`."
|
|
867
|
+
},
|
|
868
|
+
"cachedMetrics": {
|
|
869
|
+
"readOnly": true,
|
|
870
|
+
"denormalized": true,
|
|
871
|
+
"x-note": "Updated by the rollup pipeline (D42). Readers should treat this as a hint; authoritative counts live in analytics_daily/analytics_hourly.",
|
|
872
|
+
"x-see": {
|
|
873
|
+
"decisions": [
|
|
874
|
+
"D41",
|
|
875
|
+
"D42"
|
|
876
|
+
]
|
|
877
|
+
},
|
|
878
|
+
"description": "(Read-only, Denormalized) Cached metrics snapshot for quick dashboard rendering.",
|
|
879
|
+
"type": [
|
|
880
|
+
"object",
|
|
881
|
+
"null"
|
|
882
|
+
],
|
|
883
|
+
"properties": {
|
|
884
|
+
"totalEvents": {
|
|
885
|
+
"type": "integer",
|
|
886
|
+
"minimum": -9007199254740991,
|
|
887
|
+
"maximum": 9007199254740991,
|
|
888
|
+
"description": "All-time event count cached on the App."
|
|
889
|
+
},
|
|
890
|
+
"totalPageViews": {
|
|
891
|
+
"type": "integer",
|
|
892
|
+
"minimum": -9007199254740991,
|
|
893
|
+
"maximum": 9007199254740991,
|
|
894
|
+
"description": "All-time `page_view` + `screen_view` count."
|
|
895
|
+
},
|
|
896
|
+
"totalSessions": {
|
|
897
|
+
"type": "integer",
|
|
898
|
+
"minimum": -9007199254740991,
|
|
899
|
+
"maximum": 9007199254740991,
|
|
900
|
+
"description": "All-time distinct session count."
|
|
901
|
+
},
|
|
902
|
+
"totalOrders": {
|
|
903
|
+
"type": "integer",
|
|
904
|
+
"minimum": -9007199254740991,
|
|
905
|
+
"maximum": 9007199254740991,
|
|
906
|
+
"description": "All-time order count attributed to this app (via `Order.appId`, D43)."
|
|
907
|
+
},
|
|
908
|
+
"lastEventAt": {
|
|
909
|
+
"$ref": "#/definitions/firestore-timestamp",
|
|
910
|
+
"description": "Timestamp of the most recent analytics event seen for this app."
|
|
911
|
+
}
|
|
912
|
+
},
|
|
913
|
+
"required": [
|
|
914
|
+
"totalEvents",
|
|
915
|
+
"totalPageViews",
|
|
916
|
+
"totalSessions",
|
|
917
|
+
"totalOrders"
|
|
918
|
+
],
|
|
919
|
+
"additionalProperties": false
|
|
920
|
+
}
|
|
921
|
+
},
|
|
922
|
+
"required": [
|
|
923
|
+
"companyId",
|
|
924
|
+
"name",
|
|
925
|
+
"deploymentLinks",
|
|
926
|
+
"createdBy",
|
|
927
|
+
"analyticsEnabled"
|
|
928
|
+
],
|
|
929
|
+
"additionalProperties": false,
|
|
930
|
+
"description": "App model (D41 / ING-304). Collection: companies/{companyId}/apps/{appId}. Per-company product surface. Sub-collections (magic_link_requests, allowed_users, payments, analytics_events, analytics_daily, analytics_hourly, analytics_backfills) live under this document per D40/D42.",
|
|
931
|
+
"example": {
|
|
932
|
+
"id": null,
|
|
933
|
+
"companyId": "comp_xyz789",
|
|
934
|
+
"name": "Amadou Diallo",
|
|
935
|
+
"description": null,
|
|
936
|
+
"status": "status",
|
|
937
|
+
"deploymentLinks": [
|
|
938
|
+
{
|
|
939
|
+
"label": "label",
|
|
940
|
+
"url": "https://storage.example.com/url.jpg",
|
|
941
|
+
"type": "phone"
|
|
942
|
+
}
|
|
943
|
+
],
|
|
944
|
+
"expiresAt": "expiresAt",
|
|
945
|
+
"isExpired": null,
|
|
946
|
+
"createdAt": "createdAt",
|
|
947
|
+
"updatedAt": "updatedAt",
|
|
948
|
+
"createdBy": "staff_k0f1",
|
|
949
|
+
"analyticsEnabled": true,
|
|
950
|
+
"lastAnalyticsSync": "lastAnalyticsSync",
|
|
951
|
+
"cachedMetrics": null
|
|
952
|
+
}
|
|
953
|
+
},
|
|
954
|
+
"app-payment": {
|
|
955
|
+
"type": "object",
|
|
956
|
+
"properties": {
|
|
957
|
+
"id": {
|
|
958
|
+
"readOnly": true,
|
|
959
|
+
"description": "(Read-only) Firestore document ID, auto-generated.",
|
|
960
|
+
"type": [
|
|
961
|
+
"string",
|
|
962
|
+
"null"
|
|
963
|
+
]
|
|
964
|
+
},
|
|
965
|
+
"companyId": {
|
|
966
|
+
"type": "string",
|
|
967
|
+
"x-immutable": true,
|
|
968
|
+
"description": "(Immutable) FK \u2192 Company document ID."
|
|
969
|
+
},
|
|
970
|
+
"appId": {
|
|
971
|
+
"type": "string",
|
|
972
|
+
"x-immutable": true,
|
|
973
|
+
"description": "(Immutable) FK \u2192 App document ID (D40 sub-tenant scope, renamed from siteId per D44)."
|
|
974
|
+
},
|
|
975
|
+
"contact": {
|
|
976
|
+
"type": "string",
|
|
977
|
+
"description": "Email or E.164 phone number. Matches AllowedUser.contact."
|
|
978
|
+
},
|
|
979
|
+
"sessionId": {
|
|
980
|
+
"type": "string",
|
|
981
|
+
"description": "Payment provider checkout session ID (e.g. Wave). Usable for dedup across dual writes."
|
|
982
|
+
},
|
|
983
|
+
"transactionId": {
|
|
984
|
+
"type": "string",
|
|
985
|
+
"description": "Payment provider transaction ID."
|
|
986
|
+
},
|
|
987
|
+
"tier": {
|
|
988
|
+
"type": "string",
|
|
989
|
+
"description": "Access tier. Free string per app (ING-304 open question)."
|
|
990
|
+
},
|
|
991
|
+
"amount": {
|
|
992
|
+
"type": "number",
|
|
993
|
+
"description": "Amount paid. Generalized from amount_xof per D40 decision."
|
|
994
|
+
},
|
|
995
|
+
"currency": {
|
|
996
|
+
"default": "XOF",
|
|
997
|
+
"description": "Currency code (ISO 4217). Defaults to XOF for legacy SR-Single parity.",
|
|
998
|
+
"type": "string"
|
|
999
|
+
},
|
|
1000
|
+
"paidAt": {
|
|
1001
|
+
"$ref": "#/definitions/firestore-timestamp",
|
|
1002
|
+
"description": "RFC3339Nano UTC when payment was completed."
|
|
1003
|
+
}
|
|
1004
|
+
},
|
|
1005
|
+
"required": [
|
|
1006
|
+
"companyId",
|
|
1007
|
+
"appId",
|
|
1008
|
+
"contact",
|
|
1009
|
+
"sessionId",
|
|
1010
|
+
"transactionId",
|
|
1011
|
+
"tier",
|
|
1012
|
+
"amount",
|
|
1013
|
+
"currency",
|
|
1014
|
+
"paidAt"
|
|
1015
|
+
],
|
|
1016
|
+
"additionalProperties": false,
|
|
1017
|
+
"description": "AppPayment model (D40 / ING-304). Collection: companies/{companyId}/apps/{appId}/payments/{paymentId}. Immutable append-only transaction ledger. Distinct from AllowedUser (access state) and CustomerPayment (D22 \u2014 customer-level billing).",
|
|
1018
|
+
"example": {
|
|
1019
|
+
"id": null,
|
|
1020
|
+
"companyId": "comp_xyz789",
|
|
1021
|
+
"appId": "app_ref123",
|
|
1022
|
+
"contact": "contact",
|
|
1023
|
+
"sessionId": "ses_ref123",
|
|
1024
|
+
"transactionId": "tra_ref123",
|
|
1025
|
+
"tier": "Gold",
|
|
1026
|
+
"amount": 45000,
|
|
1027
|
+
"currency": "XOF",
|
|
1028
|
+
"paidAt": "pai_ref123"
|
|
1029
|
+
}
|
|
1030
|
+
},
|
|
1031
|
+
"app-status": {
|
|
1032
|
+
"type": "string",
|
|
1033
|
+
"enum": [
|
|
1034
|
+
"ACTIVE",
|
|
1035
|
+
"INACTIVE",
|
|
1036
|
+
"EXPIRED",
|
|
1037
|
+
"ARCHIVED"
|
|
1038
|
+
],
|
|
1039
|
+
"description": "Lifecycle status for an App (formerly Site \u2014 renamed per decision #40 / D44). Drives whether the app is reachable and whether analytics/payments flow.",
|
|
1040
|
+
"x-note": "ACTIVE = live and serving traffic. INACTIVE = intentionally disabled by operators. EXPIRED = past expiresAt (derived when isExpired is true; may also be stored explicitly). ARCHIVED = soft-deleted; retained for audit but not listed by default.",
|
|
1041
|
+
"x-see": {
|
|
1042
|
+
"decisions": [
|
|
1043
|
+
"D41",
|
|
1044
|
+
"D44"
|
|
1045
|
+
]
|
|
1046
|
+
},
|
|
1047
|
+
"x-when": "Written by operators via dashboard or by server triggers (expiration). Clients filter by ACTIVE."
|
|
1048
|
+
},
|
|
720
1049
|
"attention-status": {
|
|
721
1050
|
"type": "string",
|
|
722
1051
|
"enum": [
|
|
@@ -826,7 +1155,7 @@
|
|
|
826
1155
|
},
|
|
827
1156
|
"_deletedAt": {
|
|
828
1157
|
"$ref": "#/definitions/firestore-timestamp",
|
|
829
|
-
"description": "Firestore Timestamp
|
|
1158
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
830
1159
|
},
|
|
831
1160
|
"_deletedBy": {
|
|
832
1161
|
"description": "FK \u2192 User/staff UID who soft-deleted this item.",
|
|
@@ -834,7 +1163,7 @@
|
|
|
834
1163
|
},
|
|
835
1164
|
"_lastModifiedAt": {
|
|
836
1165
|
"$ref": "#/definitions/firestore-timestamp",
|
|
837
|
-
"description": "Firestore Timestamp
|
|
1166
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
838
1167
|
},
|
|
839
1168
|
"_lastModifiedBy": {
|
|
840
1169
|
"description": "FK \u2192 User/staff UID who last modified this item.",
|
|
@@ -916,7 +1245,7 @@
|
|
|
916
1245
|
},
|
|
917
1246
|
"additionalProperties": {
|
|
918
1247
|
"$ref": "#/definitions/firestore-timestamp",
|
|
919
|
-
"description": "Firestore Timestamp
|
|
1248
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
920
1249
|
}
|
|
921
1250
|
},
|
|
922
1251
|
"slotStatusUpdatedBy": {
|
|
@@ -934,7 +1263,7 @@
|
|
|
934
1263
|
},
|
|
935
1264
|
"_deletedAt": {
|
|
936
1265
|
"$ref": "#/definitions/firestore-timestamp",
|
|
937
|
-
"description": "Firestore Timestamp
|
|
1266
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
938
1267
|
},
|
|
939
1268
|
"_deletedBy": {
|
|
940
1269
|
"description": "FK \u2192 User/staff UID who soft-deleted this item.",
|
|
@@ -942,7 +1271,7 @@
|
|
|
942
1271
|
},
|
|
943
1272
|
"_lastModifiedAt": {
|
|
944
1273
|
"$ref": "#/definitions/firestore-timestamp",
|
|
945
|
-
"description": "Firestore Timestamp
|
|
1274
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
946
1275
|
},
|
|
947
1276
|
"_lastModifiedBy": {
|
|
948
1277
|
"description": "FK \u2192 User/staff UID who last modified this item.",
|
|
@@ -1151,7 +1480,7 @@
|
|
|
1151
1480
|
},
|
|
1152
1481
|
"_deletedAt": {
|
|
1153
1482
|
"$ref": "#/definitions/firestore-timestamp",
|
|
1154
|
-
"description": "Firestore Timestamp
|
|
1483
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
1155
1484
|
},
|
|
1156
1485
|
"_deletedBy": {
|
|
1157
1486
|
"description": "FK \u2192 User/staff UID who soft-deleted this item.",
|
|
@@ -1159,7 +1488,7 @@
|
|
|
1159
1488
|
},
|
|
1160
1489
|
"_lastModifiedAt": {
|
|
1161
1490
|
"$ref": "#/definitions/firestore-timestamp",
|
|
1162
|
-
"description": "Firestore Timestamp
|
|
1491
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
1163
1492
|
},
|
|
1164
1493
|
"_lastModifiedBy": {
|
|
1165
1494
|
"description": "FK \u2192 User/staff UID who last modified this item.",
|
|
@@ -1243,7 +1572,7 @@
|
|
|
1243
1572
|
"type": "null"
|
|
1244
1573
|
}
|
|
1245
1574
|
],
|
|
1246
|
-
"description": "Firestore Timestamp
|
|
1575
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
1247
1576
|
},
|
|
1248
1577
|
"paymentProofUrl": {
|
|
1249
1578
|
"description": "URL to uploaded payment proof image/document.",
|
|
@@ -1272,7 +1601,7 @@
|
|
|
1272
1601
|
"type": "null"
|
|
1273
1602
|
}
|
|
1274
1603
|
],
|
|
1275
|
-
"description": "Firestore Timestamp
|
|
1604
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
1276
1605
|
},
|
|
1277
1606
|
"paymentProofAddedBy": {
|
|
1278
1607
|
"description": "FK \u2192 User/staff UID who uploaded the payment proof.",
|
|
@@ -1297,7 +1626,7 @@
|
|
|
1297
1626
|
"type": "null"
|
|
1298
1627
|
}
|
|
1299
1628
|
],
|
|
1300
|
-
"description": "Firestore Timestamp
|
|
1629
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
1301
1630
|
},
|
|
1302
1631
|
"paymentProofRejectionReason": {
|
|
1303
1632
|
"type": [
|
|
@@ -1334,7 +1663,7 @@
|
|
|
1334
1663
|
"type": "null"
|
|
1335
1664
|
}
|
|
1336
1665
|
],
|
|
1337
|
-
"description": "Firestore Timestamp
|
|
1666
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
1338
1667
|
},
|
|
1339
1668
|
"cancelledByRole": {
|
|
1340
1669
|
"type": [
|
|
@@ -1544,58 +1873,244 @@
|
|
|
1544
1873
|
"UNKNOWN"
|
|
1545
1874
|
]
|
|
1546
1875
|
},
|
|
1547
|
-
"fieldsChanged": {
|
|
1548
|
-
"x-immutable": true,
|
|
1549
|
-
"readOnly": true,
|
|
1550
|
-
"x-note": "Dot-notation paths for nested fields (e.g. \"bookingDates.0.status\"). Populated by diff in Firebase trigger. Null for CREATE versions.",
|
|
1551
|
-
"description": "(Immutable, Read-only) List of field paths that changed in this write. Populated by the Firebase trigger diff. Null for CREATE records.",
|
|
1876
|
+
"fieldsChanged": {
|
|
1877
|
+
"x-immutable": true,
|
|
1878
|
+
"readOnly": true,
|
|
1879
|
+
"x-note": "Dot-notation paths for nested fields (e.g. \"bookingDates.0.status\"). Populated by diff in Firebase trigger. Null for CREATE versions.",
|
|
1880
|
+
"description": "(Immutable, Read-only) List of field paths that changed in this write. Populated by the Firebase trigger diff. Null for CREATE records.",
|
|
1881
|
+
"type": [
|
|
1882
|
+
"array",
|
|
1883
|
+
"null"
|
|
1884
|
+
],
|
|
1885
|
+
"items": {
|
|
1886
|
+
"type": "string"
|
|
1887
|
+
}
|
|
1888
|
+
},
|
|
1889
|
+
"snapshot": {
|
|
1890
|
+
"type": "object",
|
|
1891
|
+
"propertyNames": {
|
|
1892
|
+
"type": "string"
|
|
1893
|
+
},
|
|
1894
|
+
"additionalProperties": {},
|
|
1895
|
+
"x-immutable": true,
|
|
1896
|
+
"readOnly": true,
|
|
1897
|
+
"x-note": "Full Booking document at the time of write. Typed as a string-keyed record to avoid circular schema reference. Consumers cast to Booking at runtime.",
|
|
1898
|
+
"x-see": {
|
|
1899
|
+
"decisions": [
|
|
1900
|
+
"D18"
|
|
1901
|
+
]
|
|
1902
|
+
},
|
|
1903
|
+
"description": "(Immutable, Read-only) Full snapshot of the Booking document immediately after the write. Untyped to avoid circular schema dependency \u2014 cast to Booking at runtime."
|
|
1904
|
+
}
|
|
1905
|
+
},
|
|
1906
|
+
"required": [
|
|
1907
|
+
"id",
|
|
1908
|
+
"bookingId",
|
|
1909
|
+
"companyId",
|
|
1910
|
+
"changeType",
|
|
1911
|
+
"changedAt",
|
|
1912
|
+
"snapshot"
|
|
1913
|
+
],
|
|
1914
|
+
"additionalProperties": false,
|
|
1915
|
+
"description": "BookingVersion model (D18). Collection: companies/{companyId}/bookings/{bookingId}/versions/{versionId}. Server-owned audit trail written by Firebase trigger on every Booking mutation. Captures writes from all clients.",
|
|
1916
|
+
"example": {
|
|
1917
|
+
"id": "bk_abc123def456",
|
|
1918
|
+
"bookingId": "boo_ref123",
|
|
1919
|
+
"companyId": "comp_xyz789",
|
|
1920
|
+
"changeType": "CREATE",
|
|
1921
|
+
"changedAt": "changedAt",
|
|
1922
|
+
"changedBy": null,
|
|
1923
|
+
"changedByRole": "DASHBOARD",
|
|
1924
|
+
"fieldsChanged": null,
|
|
1925
|
+
"snapshot": {}
|
|
1926
|
+
}
|
|
1927
|
+
},
|
|
1928
|
+
"contract": {
|
|
1929
|
+
"type": "object",
|
|
1930
|
+
"properties": {
|
|
1931
|
+
"id": {
|
|
1932
|
+
"readOnly": true,
|
|
1933
|
+
"description": "(Read-only) Firestore document ID, auto-generated.",
|
|
1934
|
+
"type": [
|
|
1935
|
+
"string",
|
|
1936
|
+
"null"
|
|
1937
|
+
]
|
|
1938
|
+
},
|
|
1939
|
+
"companyId": {
|
|
1940
|
+
"type": "string",
|
|
1941
|
+
"x-immutable": true,
|
|
1942
|
+
"description": "(Immutable) FK \u2192 Company document ID."
|
|
1943
|
+
},
|
|
1944
|
+
"payeeId": {
|
|
1945
|
+
"type": "string",
|
|
1946
|
+
"x-immutable": true,
|
|
1947
|
+
"description": "(Immutable) FK \u2192 Payee document ID. Full Payee model tracked in #20."
|
|
1948
|
+
},
|
|
1949
|
+
"title": {
|
|
1950
|
+
"type": "string",
|
|
1951
|
+
"description": "Contract title or reference name shown in dashboards."
|
|
1952
|
+
},
|
|
1953
|
+
"description": {
|
|
1954
|
+
"description": "Optional freeform description of the contract scope.",
|
|
1955
|
+
"type": [
|
|
1956
|
+
"string",
|
|
1957
|
+
"null"
|
|
1958
|
+
]
|
|
1959
|
+
},
|
|
1960
|
+
"status": {
|
|
1961
|
+
"$ref": "#/definitions/contract-status",
|
|
1962
|
+
"description": "Contract lifecycle status (#14). Query by ACTIVE \u2014 do not use an isActive boolean."
|
|
1963
|
+
},
|
|
1964
|
+
"currency": {
|
|
1965
|
+
"type": "string",
|
|
1966
|
+
"const": "XOF",
|
|
1967
|
+
"description": "Currency code. Locked to XOF (#18). Multi-currency support requires a deliberate schema version bump."
|
|
1968
|
+
},
|
|
1969
|
+
"totalAmount": {
|
|
1970
|
+
"type": "number",
|
|
1971
|
+
"description": "Total contract value (XOF)."
|
|
1972
|
+
},
|
|
1973
|
+
"startDate": {
|
|
1974
|
+
"type": "string",
|
|
1975
|
+
"description": "Contract start date (ISO 8601 YYYY-MM-DD)."
|
|
1976
|
+
},
|
|
1977
|
+
"endDate": {
|
|
1978
|
+
"description": "Contract end date (ISO 8601 YYYY-MM-DD). Absent for open-ended contracts.",
|
|
1979
|
+
"type": [
|
|
1980
|
+
"string",
|
|
1981
|
+
"null"
|
|
1982
|
+
]
|
|
1983
|
+
},
|
|
1984
|
+
"milestones": {
|
|
1985
|
+
"description": "Ordered list of payment milestones. Embedded in the contract document (#17).",
|
|
1986
|
+
"type": [
|
|
1987
|
+
"array",
|
|
1988
|
+
"null"
|
|
1989
|
+
],
|
|
1990
|
+
"items": {
|
|
1991
|
+
"type": "object",
|
|
1992
|
+
"properties": {
|
|
1993
|
+
"id": {
|
|
1994
|
+
"type": "string",
|
|
1995
|
+
"description": "Client-generated milestone ID (UUID or slug). Unique within the contract."
|
|
1996
|
+
},
|
|
1997
|
+
"title": {
|
|
1998
|
+
"type": "string",
|
|
1999
|
+
"description": "Milestone description or deliverable name."
|
|
2000
|
+
},
|
|
2001
|
+
"amount": {
|
|
2002
|
+
"type": "number",
|
|
2003
|
+
"description": "Amount due at this milestone (XOF)."
|
|
2004
|
+
},
|
|
2005
|
+
"dueDate": {
|
|
2006
|
+
"description": "ISO 8601 date string (YYYY-MM-DD) when the milestone is due. OVERDUE is derived at read time from dueDate \u2014 not stored.",
|
|
2007
|
+
"type": "string"
|
|
2008
|
+
},
|
|
2009
|
+
"status": {
|
|
2010
|
+
"$ref": "#/definitions/milestone-status",
|
|
2011
|
+
"x-note": "Existing Firestore documents may have lowercase values (pending, invoiced, paid) from before this enum was introduced (#17). Normalize on read or via migration script.",
|
|
2012
|
+
"description": "Milestone payment status (#17). OVERDUE is derived at read time from dueDate, not stored."
|
|
2013
|
+
},
|
|
2014
|
+
"invoicedAt": {
|
|
2015
|
+
"$ref": "#/definitions/firestore-timestamp",
|
|
2016
|
+
"description": "When the invoice was issued for this milestone."
|
|
2017
|
+
},
|
|
2018
|
+
"paidAt": {
|
|
2019
|
+
"$ref": "#/definitions/firestore-timestamp",
|
|
2020
|
+
"description": "When payment was received for this milestone."
|
|
2021
|
+
},
|
|
2022
|
+
"notes": {
|
|
2023
|
+
"description": "Optional notes for this milestone.",
|
|
2024
|
+
"type": "string"
|
|
2025
|
+
}
|
|
2026
|
+
},
|
|
2027
|
+
"required": [
|
|
2028
|
+
"id",
|
|
2029
|
+
"title",
|
|
2030
|
+
"amount",
|
|
2031
|
+
"status"
|
|
2032
|
+
],
|
|
2033
|
+
"additionalProperties": false,
|
|
2034
|
+
"description": "ContractMilestone \u2014 embedded sub-object on Contract (#17). Not a separate collection."
|
|
2035
|
+
}
|
|
2036
|
+
},
|
|
2037
|
+
"notes": {
|
|
2038
|
+
"description": "Optional internal notes.",
|
|
1552
2039
|
"type": [
|
|
1553
|
-
"
|
|
2040
|
+
"string",
|
|
1554
2041
|
"null"
|
|
1555
|
-
]
|
|
1556
|
-
"items": {
|
|
1557
|
-
"type": "string"
|
|
1558
|
-
}
|
|
2042
|
+
]
|
|
1559
2043
|
},
|
|
1560
|
-
"
|
|
1561
|
-
"type": "
|
|
1562
|
-
"propertyNames": {
|
|
1563
|
-
"type": "string"
|
|
1564
|
-
},
|
|
1565
|
-
"additionalProperties": {},
|
|
2044
|
+
"createdBy": {
|
|
2045
|
+
"type": "string",
|
|
1566
2046
|
"x-immutable": true,
|
|
2047
|
+
"description": "(Immutable) FK \u2192 User/staff UID who created the contract."
|
|
2048
|
+
},
|
|
2049
|
+
"createdAt": {
|
|
2050
|
+
"anyOf": [
|
|
2051
|
+
{
|
|
2052
|
+
"$ref": "#/definitions/firestore-timestamp"
|
|
2053
|
+
},
|
|
2054
|
+
{
|
|
2055
|
+
"type": "null"
|
|
2056
|
+
}
|
|
2057
|
+
],
|
|
1567
2058
|
"readOnly": true,
|
|
1568
|
-
"
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
2059
|
+
"description": "(Read-only) Server-generated creation timestamp."
|
|
2060
|
+
},
|
|
2061
|
+
"updatedAt": {
|
|
2062
|
+
"anyOf": [
|
|
2063
|
+
{
|
|
2064
|
+
"$ref": "#/definitions/firestore-timestamp"
|
|
2065
|
+
},
|
|
2066
|
+
{
|
|
2067
|
+
"type": "null"
|
|
2068
|
+
}
|
|
2069
|
+
],
|
|
2070
|
+
"readOnly": true,
|
|
2071
|
+
"description": "(Read-only) Server-generated update timestamp."
|
|
1575
2072
|
}
|
|
1576
2073
|
},
|
|
1577
2074
|
"required": [
|
|
1578
|
-
"id",
|
|
1579
|
-
"bookingId",
|
|
1580
2075
|
"companyId",
|
|
1581
|
-
"
|
|
1582
|
-
"
|
|
1583
|
-
"
|
|
2076
|
+
"payeeId",
|
|
2077
|
+
"title",
|
|
2078
|
+
"status",
|
|
2079
|
+
"currency",
|
|
2080
|
+
"totalAmount",
|
|
2081
|
+
"startDate",
|
|
2082
|
+
"createdBy"
|
|
1584
2083
|
],
|
|
1585
2084
|
"additionalProperties": false,
|
|
1586
|
-
"description": "
|
|
2085
|
+
"description": "Contract model (GH#14/#17/#18). Collection: companies/{companyId}/contracts/{contractId}. Service/supplier contract between a company and a Payee. No isActive field \u2014 query by status. Currency locked to XOF. Milestones use MilestoneStatus enum (no stored OVERDUE).",
|
|
1587
2086
|
"example": {
|
|
1588
|
-
"id":
|
|
1589
|
-
"bookingId": "boo_ref123",
|
|
2087
|
+
"id": null,
|
|
1590
2088
|
"companyId": "comp_xyz789",
|
|
1591
|
-
"
|
|
1592
|
-
"
|
|
1593
|
-
"
|
|
1594
|
-
"
|
|
1595
|
-
"
|
|
1596
|
-
"
|
|
2089
|
+
"payeeId": "pay_ref123",
|
|
2090
|
+
"title": "title",
|
|
2091
|
+
"description": null,
|
|
2092
|
+
"status": "status",
|
|
2093
|
+
"currency": "XOF",
|
|
2094
|
+
"totalAmount": 45000,
|
|
2095
|
+
"startDate": "2026-02-15",
|
|
2096
|
+
"endDate": null,
|
|
2097
|
+
"milestones": null,
|
|
2098
|
+
"notes": null,
|
|
2099
|
+
"createdBy": "staff_k0f1",
|
|
2100
|
+
"createdAt": "createdAt",
|
|
2101
|
+
"updatedAt": "updatedAt"
|
|
1597
2102
|
}
|
|
1598
2103
|
},
|
|
2104
|
+
"contract-status": {
|
|
2105
|
+
"type": "string",
|
|
2106
|
+
"enum": [
|
|
2107
|
+
"DRAFT",
|
|
2108
|
+
"ACTIVE",
|
|
2109
|
+
"COMPLETED",
|
|
2110
|
+
"TERMINATED"
|
|
2111
|
+
],
|
|
2112
|
+
"description": "Lifecycle status of a Contract. DRAFT = not yet signed. ACTIVE = in force. COMPLETED = all milestones paid and obligations met. TERMINATED = ended early by either party."
|
|
2113
|
+
},
|
|
1599
2114
|
"customer": {
|
|
1600
2115
|
"type": "object",
|
|
1601
2116
|
"properties": {
|
|
@@ -1656,7 +2171,7 @@
|
|
|
1656
2171
|
"properties": {
|
|
1657
2172
|
"timestamp": {
|
|
1658
2173
|
"$ref": "#/definitions/firestore-timestamp",
|
|
1659
|
-
"description": "Firestore Timestamp
|
|
2174
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
1660
2175
|
},
|
|
1661
2176
|
"type": {
|
|
1662
2177
|
"type": "string",
|
|
@@ -1815,7 +2330,7 @@
|
|
|
1815
2330
|
},
|
|
1816
2331
|
"paymentDate": {
|
|
1817
2332
|
"$ref": "#/definitions/firestore-timestamp",
|
|
1818
|
-
"description": "Firestore Timestamp
|
|
2333
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
1819
2334
|
},
|
|
1820
2335
|
"paymentMethod": {
|
|
1821
2336
|
"$ref": "#/definitions/payment-method",
|
|
@@ -1983,7 +2498,7 @@
|
|
|
1983
2498
|
"type": "null"
|
|
1984
2499
|
}
|
|
1985
2500
|
],
|
|
1986
|
-
"description": "Firestore Timestamp
|
|
2501
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
1987
2502
|
},
|
|
1988
2503
|
"createdBy": {
|
|
1989
2504
|
"type": "string",
|
|
@@ -2112,7 +2627,7 @@
|
|
|
2112
2627
|
},
|
|
2113
2628
|
"startDate": {
|
|
2114
2629
|
"$ref": "#/definitions/firestore-timestamp",
|
|
2115
|
-
"description": "Firestore Timestamp
|
|
2630
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
2116
2631
|
},
|
|
2117
2632
|
"endDate": {
|
|
2118
2633
|
"anyOf": [
|
|
@@ -2123,7 +2638,7 @@
|
|
|
2123
2638
|
"type": "null"
|
|
2124
2639
|
}
|
|
2125
2640
|
],
|
|
2126
|
-
"description": "Firestore Timestamp
|
|
2641
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
2127
2642
|
},
|
|
2128
2643
|
"status": {
|
|
2129
2644
|
"$ref": "#/definitions/event-status",
|
|
@@ -2727,10 +3242,10 @@
|
|
|
2727
3242
|
"x-immutable": true,
|
|
2728
3243
|
"description": "(Immutable) FK \u2192 Company document ID."
|
|
2729
3244
|
},
|
|
2730
|
-
"
|
|
3245
|
+
"appId": {
|
|
2731
3246
|
"type": "string",
|
|
2732
3247
|
"x-immutable": true,
|
|
2733
|
-
"description": "(Immutable) FK \u2192
|
|
3248
|
+
"description": "(Immutable) FK \u2192 App document ID (D40 sub-tenant scope, renamed per D44)."
|
|
2734
3249
|
},
|
|
2735
3250
|
"email": {
|
|
2736
3251
|
"type": "string",
|
|
@@ -2796,7 +3311,7 @@
|
|
|
2796
3311
|
},
|
|
2797
3312
|
"required": [
|
|
2798
3313
|
"companyId",
|
|
2799
|
-
"
|
|
3314
|
+
"appId",
|
|
2800
3315
|
"email",
|
|
2801
3316
|
"phone",
|
|
2802
3317
|
"ip",
|
|
@@ -2808,11 +3323,11 @@
|
|
|
2808
3323
|
"requestedAt"
|
|
2809
3324
|
],
|
|
2810
3325
|
"additionalProperties": false,
|
|
2811
|
-
"description": "MagicLinkRequest model (D40 / ING-304). Collection: companies/{companyId}/
|
|
3326
|
+
"description": "MagicLinkRequest model (D40 / ING-304). Collection: companies/{companyId}/apps/{appId}/magic_link_requests/{requestId}. Authentication audit log \u2014 every request is logged regardless of outcome. Two-stage write: Log() then LogVerify().",
|
|
2812
3327
|
"example": {
|
|
2813
3328
|
"id": null,
|
|
2814
3329
|
"companyId": "comp_xyz789",
|
|
2815
|
-
"
|
|
3330
|
+
"appId": "app_ref123",
|
|
2816
3331
|
"email": "amadou@example.com",
|
|
2817
3332
|
"phone": "+225 07 00 11 22",
|
|
2818
3333
|
"ip": "ip",
|
|
@@ -2971,6 +3486,65 @@
|
|
|
2971
3486
|
"readOnly": true,
|
|
2972
3487
|
"description": "(Read-only) Sum of amount for ACTIVE + MONTHLY recurring payments. Always full recalc."
|
|
2973
3488
|
},
|
|
3489
|
+
"escalatedOrdersCount": {
|
|
3490
|
+
"readOnly": true,
|
|
3491
|
+
"x-note": "Absent on tenants whose metrics/current was last written before aggregator v2. Backfill required for universal presence (#8).",
|
|
3492
|
+
"description": "(Read-only, Optional) Orders currently in an escalated state. Present once the current aggregator rewrites the document.",
|
|
3493
|
+
"type": [
|
|
3494
|
+
"integer",
|
|
3495
|
+
"null"
|
|
3496
|
+
],
|
|
3497
|
+
"minimum": -9007199254740991,
|
|
3498
|
+
"maximum": 9007199254740991
|
|
3499
|
+
},
|
|
3500
|
+
"staleOrdersCount": {
|
|
3501
|
+
"readOnly": true,
|
|
3502
|
+
"x-note": "Absent on tenants whose metrics/current was last written before aggregator v2. Backfill required for universal presence (#8).",
|
|
3503
|
+
"description": "(Read-only, Optional) Orders that have been in an incomplete status beyond the stale threshold.",
|
|
3504
|
+
"type": [
|
|
3505
|
+
"integer",
|
|
3506
|
+
"null"
|
|
3507
|
+
],
|
|
3508
|
+
"minimum": -9007199254740991,
|
|
3509
|
+
"maximum": 9007199254740991
|
|
3510
|
+
},
|
|
3511
|
+
"staleBookingsCount": {
|
|
3512
|
+
"readOnly": true,
|
|
3513
|
+
"x-note": "Absent on tenants whose metrics/current was last written before aggregator v2. Backfill required for universal presence (#8).",
|
|
3514
|
+
"description": "(Read-only, Optional) Bookings that have been in an incomplete status beyond the stale threshold.",
|
|
3515
|
+
"type": [
|
|
3516
|
+
"integer",
|
|
3517
|
+
"null"
|
|
3518
|
+
],
|
|
3519
|
+
"minimum": -9007199254740991,
|
|
3520
|
+
"maximum": 9007199254740991
|
|
3521
|
+
},
|
|
3522
|
+
"onHoldOrdersCount": {
|
|
3523
|
+
"readOnly": true,
|
|
3524
|
+
"x-note": "Absent on tenants whose metrics/current was last written before aggregator v2. Backfill required for universal presence (#8).",
|
|
3525
|
+
"description": "(Read-only, Optional) Orders currently in an on-hold state.",
|
|
3526
|
+
"type": [
|
|
3527
|
+
"integer",
|
|
3528
|
+
"null"
|
|
3529
|
+
],
|
|
3530
|
+
"minimum": -9007199254740991,
|
|
3531
|
+
"maximum": 9007199254740991
|
|
3532
|
+
},
|
|
3533
|
+
"paymentsByMethod": {
|
|
3534
|
+
"readOnly": true,
|
|
3535
|
+
"x-note": "Always {} until the first real payment is recorded. Present on all tenants once rewritten by current aggregator (#8).",
|
|
3536
|
+
"description": "(Read-only, Optional) Map of PaymentMethod \u2192 total amount for the current day. Empty map {} until first payment.",
|
|
3537
|
+
"type": [
|
|
3538
|
+
"object",
|
|
3539
|
+
"null"
|
|
3540
|
+
],
|
|
3541
|
+
"propertyNames": {
|
|
3542
|
+
"type": "string"
|
|
3543
|
+
},
|
|
3544
|
+
"additionalProperties": {
|
|
3545
|
+
"type": "number"
|
|
3546
|
+
}
|
|
3547
|
+
},
|
|
2974
3548
|
"computedForDay": {
|
|
2975
3549
|
"type": "string",
|
|
2976
3550
|
"readOnly": true,
|
|
@@ -3035,6 +3609,11 @@
|
|
|
3035
3609
|
"lowStockItemsCount": 2,
|
|
3036
3610
|
"activeRecurringPaymentsCount": 2,
|
|
3037
3611
|
"monthlyRecurringRevenue": 0,
|
|
3612
|
+
"escalatedOrdersCount": null,
|
|
3613
|
+
"staleOrdersCount": null,
|
|
3614
|
+
"staleBookingsCount": null,
|
|
3615
|
+
"onHoldOrdersCount": null,
|
|
3616
|
+
"paymentsByMethod": null,
|
|
3038
3617
|
"computedForDay": "computedForDay",
|
|
3039
3618
|
"generatedAt": "generatedAt"
|
|
3040
3619
|
}
|
|
@@ -3183,6 +3762,65 @@
|
|
|
3183
3762
|
"readOnly": true,
|
|
3184
3763
|
"description": "(Read-only) Sum of amount for ACTIVE + MONTHLY recurring payments. Always full recalc."
|
|
3185
3764
|
},
|
|
3765
|
+
"escalatedOrdersCount": {
|
|
3766
|
+
"readOnly": true,
|
|
3767
|
+
"x-note": "Absent on tenants whose metrics/current was last written before aggregator v2. Backfill required for universal presence (#8).",
|
|
3768
|
+
"description": "(Read-only, Optional) Orders currently in an escalated state. Present once the current aggregator rewrites the document.",
|
|
3769
|
+
"type": [
|
|
3770
|
+
"integer",
|
|
3771
|
+
"null"
|
|
3772
|
+
],
|
|
3773
|
+
"minimum": -9007199254740991,
|
|
3774
|
+
"maximum": 9007199254740991
|
|
3775
|
+
},
|
|
3776
|
+
"staleOrdersCount": {
|
|
3777
|
+
"readOnly": true,
|
|
3778
|
+
"x-note": "Absent on tenants whose metrics/current was last written before aggregator v2. Backfill required for universal presence (#8).",
|
|
3779
|
+
"description": "(Read-only, Optional) Orders that have been in an incomplete status beyond the stale threshold.",
|
|
3780
|
+
"type": [
|
|
3781
|
+
"integer",
|
|
3782
|
+
"null"
|
|
3783
|
+
],
|
|
3784
|
+
"minimum": -9007199254740991,
|
|
3785
|
+
"maximum": 9007199254740991
|
|
3786
|
+
},
|
|
3787
|
+
"staleBookingsCount": {
|
|
3788
|
+
"readOnly": true,
|
|
3789
|
+
"x-note": "Absent on tenants whose metrics/current was last written before aggregator v2. Backfill required for universal presence (#8).",
|
|
3790
|
+
"description": "(Read-only, Optional) Bookings that have been in an incomplete status beyond the stale threshold.",
|
|
3791
|
+
"type": [
|
|
3792
|
+
"integer",
|
|
3793
|
+
"null"
|
|
3794
|
+
],
|
|
3795
|
+
"minimum": -9007199254740991,
|
|
3796
|
+
"maximum": 9007199254740991
|
|
3797
|
+
},
|
|
3798
|
+
"onHoldOrdersCount": {
|
|
3799
|
+
"readOnly": true,
|
|
3800
|
+
"x-note": "Absent on tenants whose metrics/current was last written before aggregator v2. Backfill required for universal presence (#8).",
|
|
3801
|
+
"description": "(Read-only, Optional) Orders currently in an on-hold state.",
|
|
3802
|
+
"type": [
|
|
3803
|
+
"integer",
|
|
3804
|
+
"null"
|
|
3805
|
+
],
|
|
3806
|
+
"minimum": -9007199254740991,
|
|
3807
|
+
"maximum": 9007199254740991
|
|
3808
|
+
},
|
|
3809
|
+
"paymentsByMethod": {
|
|
3810
|
+
"readOnly": true,
|
|
3811
|
+
"x-note": "Always {} until the first real payment is recorded. Present on all tenants once rewritten by current aggregator (#8).",
|
|
3812
|
+
"description": "(Read-only, Optional) Map of PaymentMethod \u2192 total amount for the current day. Empty map {} until first payment.",
|
|
3813
|
+
"type": [
|
|
3814
|
+
"object",
|
|
3815
|
+
"null"
|
|
3816
|
+
],
|
|
3817
|
+
"propertyNames": {
|
|
3818
|
+
"type": "string"
|
|
3819
|
+
},
|
|
3820
|
+
"additionalProperties": {
|
|
3821
|
+
"type": "number"
|
|
3822
|
+
}
|
|
3823
|
+
},
|
|
3186
3824
|
"computedForDay": {
|
|
3187
3825
|
"type": "string",
|
|
3188
3826
|
"readOnly": true,
|
|
@@ -3253,6 +3891,11 @@
|
|
|
3253
3891
|
"lowStockItemsCount": 2,
|
|
3254
3892
|
"activeRecurringPaymentsCount": 2,
|
|
3255
3893
|
"monthlyRecurringRevenue": 0,
|
|
3894
|
+
"escalatedOrdersCount": null,
|
|
3895
|
+
"staleOrdersCount": null,
|
|
3896
|
+
"staleBookingsCount": null,
|
|
3897
|
+
"onHoldOrdersCount": null,
|
|
3898
|
+
"paymentsByMethod": null,
|
|
3256
3899
|
"computedForDay": "computedForDay",
|
|
3257
3900
|
"generatedAt": "generatedAt",
|
|
3258
3901
|
"date": "2026-02-15"
|
|
@@ -3402,6 +4045,65 @@
|
|
|
3402
4045
|
"readOnly": true,
|
|
3403
4046
|
"description": "(Read-only) Sum of amount for ACTIVE + MONTHLY recurring payments. Always full recalc."
|
|
3404
4047
|
},
|
|
4048
|
+
"escalatedOrdersCount": {
|
|
4049
|
+
"readOnly": true,
|
|
4050
|
+
"x-note": "Absent on tenants whose metrics/current was last written before aggregator v2. Backfill required for universal presence (#8).",
|
|
4051
|
+
"description": "(Read-only, Optional) Orders currently in an escalated state. Present once the current aggregator rewrites the document.",
|
|
4052
|
+
"type": [
|
|
4053
|
+
"integer",
|
|
4054
|
+
"null"
|
|
4055
|
+
],
|
|
4056
|
+
"minimum": -9007199254740991,
|
|
4057
|
+
"maximum": 9007199254740991
|
|
4058
|
+
},
|
|
4059
|
+
"staleOrdersCount": {
|
|
4060
|
+
"readOnly": true,
|
|
4061
|
+
"x-note": "Absent on tenants whose metrics/current was last written before aggregator v2. Backfill required for universal presence (#8).",
|
|
4062
|
+
"description": "(Read-only, Optional) Orders that have been in an incomplete status beyond the stale threshold.",
|
|
4063
|
+
"type": [
|
|
4064
|
+
"integer",
|
|
4065
|
+
"null"
|
|
4066
|
+
],
|
|
4067
|
+
"minimum": -9007199254740991,
|
|
4068
|
+
"maximum": 9007199254740991
|
|
4069
|
+
},
|
|
4070
|
+
"staleBookingsCount": {
|
|
4071
|
+
"readOnly": true,
|
|
4072
|
+
"x-note": "Absent on tenants whose metrics/current was last written before aggregator v2. Backfill required for universal presence (#8).",
|
|
4073
|
+
"description": "(Read-only, Optional) Bookings that have been in an incomplete status beyond the stale threshold.",
|
|
4074
|
+
"type": [
|
|
4075
|
+
"integer",
|
|
4076
|
+
"null"
|
|
4077
|
+
],
|
|
4078
|
+
"minimum": -9007199254740991,
|
|
4079
|
+
"maximum": 9007199254740991
|
|
4080
|
+
},
|
|
4081
|
+
"onHoldOrdersCount": {
|
|
4082
|
+
"readOnly": true,
|
|
4083
|
+
"x-note": "Absent on tenants whose metrics/current was last written before aggregator v2. Backfill required for universal presence (#8).",
|
|
4084
|
+
"description": "(Read-only, Optional) Orders currently in an on-hold state.",
|
|
4085
|
+
"type": [
|
|
4086
|
+
"integer",
|
|
4087
|
+
"null"
|
|
4088
|
+
],
|
|
4089
|
+
"minimum": -9007199254740991,
|
|
4090
|
+
"maximum": 9007199254740991
|
|
4091
|
+
},
|
|
4092
|
+
"paymentsByMethod": {
|
|
4093
|
+
"readOnly": true,
|
|
4094
|
+
"x-note": "Always {} until the first real payment is recorded. Present on all tenants once rewritten by current aggregator (#8).",
|
|
4095
|
+
"description": "(Read-only, Optional) Map of PaymentMethod \u2192 total amount for the current day. Empty map {} until first payment.",
|
|
4096
|
+
"type": [
|
|
4097
|
+
"object",
|
|
4098
|
+
"null"
|
|
4099
|
+
],
|
|
4100
|
+
"propertyNames": {
|
|
4101
|
+
"type": "string"
|
|
4102
|
+
},
|
|
4103
|
+
"additionalProperties": {
|
|
4104
|
+
"type": "number"
|
|
4105
|
+
}
|
|
4106
|
+
},
|
|
3405
4107
|
"computedForDay": {
|
|
3406
4108
|
"type": "string",
|
|
3407
4109
|
"readOnly": true,
|
|
@@ -3472,11 +4174,26 @@
|
|
|
3472
4174
|
"lowStockItemsCount": 2,
|
|
3473
4175
|
"activeRecurringPaymentsCount": 2,
|
|
3474
4176
|
"monthlyRecurringRevenue": 0,
|
|
4177
|
+
"escalatedOrdersCount": null,
|
|
4178
|
+
"staleOrdersCount": null,
|
|
4179
|
+
"staleBookingsCount": null,
|
|
4180
|
+
"onHoldOrdersCount": null,
|
|
4181
|
+
"paymentsByMethod": null,
|
|
3475
4182
|
"computedForDay": "computedForDay",
|
|
3476
4183
|
"generatedAt": "generatedAt",
|
|
3477
4184
|
"month": "month"
|
|
3478
4185
|
}
|
|
3479
4186
|
},
|
|
4187
|
+
"milestone-status": {
|
|
4188
|
+
"type": "string",
|
|
4189
|
+
"enum": [
|
|
4190
|
+
"PENDING",
|
|
4191
|
+
"INVOICED",
|
|
4192
|
+
"PAID"
|
|
4193
|
+
],
|
|
4194
|
+
"x-note": "OVERDUE is intentionally excluded \u2014 due-date derivation should be computed at read time from dueDate, not stored. Stored OVERDUE would require a cron to keep it consistent. Existing lowercase values (pending, invoiced, paid) from pre-schema documents need normalization on read or via migration (#17).",
|
|
4195
|
+
"description": "Status of a ContractMilestone (#17). PENDING = created, not yet invoiced. INVOICED = invoice issued, awaiting payment. PAID = payment received."
|
|
4196
|
+
},
|
|
3480
4197
|
"notification-channel": {
|
|
3481
4198
|
"type": "string",
|
|
3482
4199
|
"enum": [
|
|
@@ -3702,7 +4419,7 @@
|
|
|
3702
4419
|
"createdAt"
|
|
3703
4420
|
],
|
|
3704
4421
|
"additionalProperties": false,
|
|
3705
|
-
"description": "NotificationRecord \u2014 backend-written, multi-channel notification audit log (GH#48). Paths: companies/{cid}/
|
|
4422
|
+
"description": "NotificationRecord \u2014 backend-written, multi-channel notification audit log (GH#48). Paths: companies/{cid}/notification_records/{id} (always) and companies/{cid}/orders/{oid}/notification_records/{id} (dual-write when relatedEntity.type=order). Consumers read only. Distinct from WhatsappOutboundMessage (GH#43). Collection renamed from notifications \u2192 notification_records per decision #55 (2026-05-30).",
|
|
3706
4423
|
"example": {
|
|
3707
4424
|
"id": null,
|
|
3708
4425
|
"type": "phone",
|
|
@@ -3752,16 +4469,17 @@
|
|
|
3752
4469
|
"x-immutable": true,
|
|
3753
4470
|
"description": "(Immutable) FK \u2192 Company document ID. Scopes all queries."
|
|
3754
4471
|
},
|
|
3755
|
-
"
|
|
4472
|
+
"appId": {
|
|
3756
4473
|
"x-immutable": true,
|
|
3757
|
-
"x-note": "D43 / ADR-003: optional
|
|
4474
|
+
"x-note": "D43 / ADR-003: optional app attribution (renamed from siteId per D44). Absent/null means company-wide (legacy). When set, the order is attributed to a specific app for analytics and app-scoped dashboards. No migration \u2014 existing orders keep null. Flat path; Order continues to live at `companies/{companyId}/orders/{orderId}`.",
|
|
3758
4475
|
"x-see": {
|
|
3759
4476
|
"decisions": [
|
|
3760
|
-
"D43"
|
|
4477
|
+
"D43",
|
|
4478
|
+
"D44"
|
|
3761
4479
|
]
|
|
3762
4480
|
},
|
|
3763
|
-
"x-when": "Set at order creation when the client knows which
|
|
3764
|
-
"description": "(Immutable, Optional) FK \u2192
|
|
4481
|
+
"x-when": "Set at order creation when the client knows which app the order originated from (e.g. SR Single, Lifesense). Leave null for legacy/company-wide orders. Composite index: (companyId, appId, createdAt).",
|
|
4482
|
+
"description": "(Immutable, Optional) FK \u2192 App document ID (D43 / ADR-003, renamed from siteId per D44). null = company-wide.",
|
|
3765
4483
|
"type": [
|
|
3766
4484
|
"string",
|
|
3767
4485
|
"null"
|
|
@@ -3772,6 +4490,15 @@
|
|
|
3772
4490
|
"readOnly": true,
|
|
3773
4491
|
"description": "(Read-only) Server-generated order number."
|
|
3774
4492
|
},
|
|
4493
|
+
"followUpUrl": {
|
|
4494
|
+
"x-note": "Standardized casing is followUpUrl (not followUpURL). Mobile legacy code reads both casings for backward compat but writes followUpUrl going forward (#6).",
|
|
4495
|
+
"x-when": "Used by mobile to generate QR codes on printed receipts. If absent, the mobile app derives a deterministic fallback: https://zahoui.web.app/reviews?orderNumber={orderNumber}.",
|
|
4496
|
+
"description": "Optional URL for receipt QR code / post-order follow-up. Canonical casing: followUpUrl (#6).",
|
|
4497
|
+
"type": [
|
|
4498
|
+
"string",
|
|
4499
|
+
"null"
|
|
4500
|
+
]
|
|
4501
|
+
},
|
|
3775
4502
|
"status": {
|
|
3776
4503
|
"$ref": "#/definitions/order-status",
|
|
3777
4504
|
"description": "Core lifecycle status (D34, MIG-11). See OrderStatus enum for the legacy value migration mapping.",
|
|
@@ -4039,7 +4766,7 @@
|
|
|
4039
4766
|
},
|
|
4040
4767
|
"orderDate": {
|
|
4041
4768
|
"$ref": "#/definitions/firestore-timestamp",
|
|
4042
|
-
"description": "Firestore Timestamp
|
|
4769
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
4043
4770
|
},
|
|
4044
4771
|
"PROCESSING_ON": {
|
|
4045
4772
|
"anyOf": [
|
|
@@ -4113,7 +4840,7 @@
|
|
|
4113
4840
|
"type": "null"
|
|
4114
4841
|
}
|
|
4115
4842
|
],
|
|
4116
|
-
"description": "Firestore Timestamp
|
|
4843
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
4117
4844
|
},
|
|
4118
4845
|
"shippingCost": {
|
|
4119
4846
|
"type": [
|
|
@@ -4243,7 +4970,7 @@
|
|
|
4243
4970
|
},
|
|
4244
4971
|
"paymentDate": {
|
|
4245
4972
|
"$ref": "#/definitions/firestore-timestamp",
|
|
4246
|
-
"description": "Firestore Timestamp
|
|
4973
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
4247
4974
|
},
|
|
4248
4975
|
"referenceNumber": {
|
|
4249
4976
|
"description": "Payment reference (receipt number, transaction ID, etc.).",
|
|
@@ -4356,8 +5083,9 @@
|
|
|
4356
5083
|
"id": "bk_abc123def456",
|
|
4357
5084
|
"uid": "user_u8x92kqm",
|
|
4358
5085
|
"companyId": "comp_xyz789",
|
|
4359
|
-
"
|
|
5086
|
+
"appId": null,
|
|
4360
5087
|
"orderNumber": "ORD-2026-0042",
|
|
5088
|
+
"followUpUrl": null,
|
|
4361
5089
|
"status": "status",
|
|
4362
5090
|
"paymentStatus": "paymentStatus",
|
|
4363
5091
|
"fulfillmentStatus": "fulfillmentStatus",
|
|
@@ -4667,18 +5395,203 @@
|
|
|
4667
5395
|
"adhoc",
|
|
4668
5396
|
"conversational"
|
|
4669
5397
|
],
|
|
4670
|
-
"description": "Business purpose of an outbound WhatsApp message \u2014 orthogonal to its format (see OutboundMessageFormat). `otp`/`review_request`/`adhoc` are system-originated; `conversational` is a human agent reply from the dashboard inbox. Optional signal carried for analytics/triggers (GH#43; supersedes the GH#38 OutboundMessageKind)."
|
|
5398
|
+
"description": "Business purpose of an outbound WhatsApp message \u2014 orthogonal to its format (see OutboundMessageFormat). `otp`/`review_request`/`adhoc` are system-originated; `conversational` is a human agent reply from the dashboard inbox. Optional signal carried for analytics/triggers (GH#43; supersedes the GH#38 OutboundMessageKind)."
|
|
5399
|
+
},
|
|
5400
|
+
"outbound-message-status": {
|
|
5401
|
+
"type": "string",
|
|
5402
|
+
"enum": [
|
|
5403
|
+
"queued",
|
|
5404
|
+
"sent",
|
|
5405
|
+
"delivered",
|
|
5406
|
+
"read",
|
|
5407
|
+
"failed"
|
|
5408
|
+
],
|
|
5409
|
+
"description": "Outbound WhatsApp message delivery status. Lifecycle: queued \u2192 sent \u2192 delivered \u2192 read, or queued \u2192 failed. The dashboard creates the message with status `queued`; the whatsapp-server backend owns every transition after, updating in place from Meta delivery callbacks (GH#43)."
|
|
5410
|
+
},
|
|
5411
|
+
"outbound-payment": {
|
|
5412
|
+
"type": "object",
|
|
5413
|
+
"properties": {
|
|
5414
|
+
"id": {
|
|
5415
|
+
"readOnly": true,
|
|
5416
|
+
"description": "(Read-only) Firestore document ID, auto-generated.",
|
|
5417
|
+
"type": [
|
|
5418
|
+
"string",
|
|
5419
|
+
"null"
|
|
5420
|
+
]
|
|
5421
|
+
},
|
|
5422
|
+
"companyId": {
|
|
5423
|
+
"type": "string",
|
|
5424
|
+
"x-immutable": true,
|
|
5425
|
+
"description": "(Immutable) FK \u2192 Company document ID."
|
|
5426
|
+
},
|
|
5427
|
+
"payeeId": {
|
|
5428
|
+
"type": "string",
|
|
5429
|
+
"x-immutable": true,
|
|
5430
|
+
"description": "(Immutable) FK \u2192 Payee document ID. Full Payee model tracked in #20."
|
|
5431
|
+
},
|
|
5432
|
+
"contractId": {
|
|
5433
|
+
"x-immutable": true,
|
|
5434
|
+
"description": "(Immutable, Optional) FK \u2192 Contract document ID. Absent for ad-hoc payments outside a formal contract.",
|
|
5435
|
+
"type": [
|
|
5436
|
+
"string",
|
|
5437
|
+
"null"
|
|
5438
|
+
]
|
|
5439
|
+
},
|
|
5440
|
+
"amount": {
|
|
5441
|
+
"type": "number",
|
|
5442
|
+
"description": "Amount paid (XOF)."
|
|
5443
|
+
},
|
|
5444
|
+
"currency": {
|
|
5445
|
+
"type": "string",
|
|
5446
|
+
"const": "XOF",
|
|
5447
|
+
"description": "Currency code. Locked to XOF \u2014 consistent with Contract and CustomerPayment."
|
|
5448
|
+
},
|
|
5449
|
+
"method": {
|
|
5450
|
+
"$ref": "#/definitions/payment-method",
|
|
5451
|
+
"description": "Payment method used (e.g. BANK_TRANSFER, MOBILE_MONEY, CHECK)."
|
|
5452
|
+
},
|
|
5453
|
+
"reference": {
|
|
5454
|
+
"description": "Optional payment reference \u2014 bank transaction number, check number, Wave ref, etc.",
|
|
5455
|
+
"type": [
|
|
5456
|
+
"string",
|
|
5457
|
+
"null"
|
|
5458
|
+
]
|
|
5459
|
+
},
|
|
5460
|
+
"paidAt": {
|
|
5461
|
+
"$ref": "#/definitions/firestore-timestamp",
|
|
5462
|
+
"description": "When the payment was made."
|
|
5463
|
+
},
|
|
5464
|
+
"notes": {
|
|
5465
|
+
"description": "Optional internal notes.",
|
|
5466
|
+
"type": [
|
|
5467
|
+
"string",
|
|
5468
|
+
"null"
|
|
5469
|
+
]
|
|
5470
|
+
},
|
|
5471
|
+
"createdBy": {
|
|
5472
|
+
"type": "string",
|
|
5473
|
+
"x-immutable": true,
|
|
5474
|
+
"description": "(Immutable) FK \u2192 User/staff UID who recorded the payment."
|
|
5475
|
+
},
|
|
5476
|
+
"createdAt": {
|
|
5477
|
+
"anyOf": [
|
|
5478
|
+
{
|
|
5479
|
+
"$ref": "#/definitions/firestore-timestamp"
|
|
5480
|
+
},
|
|
5481
|
+
{
|
|
5482
|
+
"type": "null"
|
|
5483
|
+
}
|
|
5484
|
+
],
|
|
5485
|
+
"readOnly": true,
|
|
5486
|
+
"description": "(Read-only) Server-generated creation timestamp."
|
|
5487
|
+
},
|
|
5488
|
+
"updatedAt": {
|
|
5489
|
+
"anyOf": [
|
|
5490
|
+
{
|
|
5491
|
+
"$ref": "#/definitions/firestore-timestamp"
|
|
5492
|
+
},
|
|
5493
|
+
{
|
|
5494
|
+
"type": "null"
|
|
5495
|
+
}
|
|
5496
|
+
],
|
|
5497
|
+
"readOnly": true,
|
|
5498
|
+
"description": "(Read-only) Server-generated update timestamp."
|
|
5499
|
+
}
|
|
5500
|
+
},
|
|
5501
|
+
"required": [
|
|
5502
|
+
"companyId",
|
|
5503
|
+
"payeeId",
|
|
5504
|
+
"amount",
|
|
5505
|
+
"currency",
|
|
5506
|
+
"method",
|
|
5507
|
+
"paidAt",
|
|
5508
|
+
"createdBy"
|
|
5509
|
+
],
|
|
5510
|
+
"additionalProperties": false,
|
|
5511
|
+
"description": "OutboundPayment (GH#15). Collection: companies/{companyId}/outboundPayments/{paymentId}. Single outbound transfer to a Payee. Canonical name chosen over SupplierPayment/VendorPayment \u2014 direction-based, agnostic of payee type. Currency locked to XOF.",
|
|
5512
|
+
"example": {
|
|
5513
|
+
"id": null,
|
|
5514
|
+
"companyId": "comp_xyz789",
|
|
5515
|
+
"payeeId": "pay_ref123",
|
|
5516
|
+
"contractId": null,
|
|
5517
|
+
"amount": 45000,
|
|
5518
|
+
"currency": "XOF",
|
|
5519
|
+
"method": "method",
|
|
5520
|
+
"reference": null,
|
|
5521
|
+
"paidAt": "pai_ref123",
|
|
5522
|
+
"notes": null,
|
|
5523
|
+
"createdBy": "staff_k0f1",
|
|
5524
|
+
"createdAt": "createdAt",
|
|
5525
|
+
"updatedAt": "updatedAt"
|
|
5526
|
+
}
|
|
4671
5527
|
},
|
|
4672
|
-
"outbound-
|
|
4673
|
-
"type": "
|
|
4674
|
-
"
|
|
4675
|
-
"
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
5528
|
+
"outbound-payment-allocation": {
|
|
5529
|
+
"type": "object",
|
|
5530
|
+
"properties": {
|
|
5531
|
+
"id": {
|
|
5532
|
+
"readOnly": true,
|
|
5533
|
+
"description": "(Read-only) Firestore document ID, auto-generated.",
|
|
5534
|
+
"type": [
|
|
5535
|
+
"string",
|
|
5536
|
+
"null"
|
|
5537
|
+
]
|
|
5538
|
+
},
|
|
5539
|
+
"outboundPaymentId": {
|
|
5540
|
+
"type": "string",
|
|
5541
|
+
"x-immutable": true,
|
|
5542
|
+
"description": "(Immutable) FK \u2192 OutboundPayment document ID (parent)."
|
|
5543
|
+
},
|
|
5544
|
+
"expenseId": {
|
|
5545
|
+
"type": "string",
|
|
5546
|
+
"x-immutable": true,
|
|
5547
|
+
"description": "(Immutable) FK \u2192 Expense document ID. Full Expense model tracked in #20."
|
|
5548
|
+
},
|
|
5549
|
+
"companyId": {
|
|
5550
|
+
"type": "string",
|
|
5551
|
+
"x-immutable": true,
|
|
5552
|
+
"description": "(Immutable) FK \u2192 Company document ID. Denormalized for collection-group queries."
|
|
5553
|
+
},
|
|
5554
|
+
"amount": {
|
|
5555
|
+
"type": "number",
|
|
5556
|
+
"description": "Amount of this payment allocated to the referenced expense (XOF)."
|
|
5557
|
+
},
|
|
5558
|
+
"notes": {
|
|
5559
|
+
"description": "Optional notes about this allocation.",
|
|
5560
|
+
"type": [
|
|
5561
|
+
"string",
|
|
5562
|
+
"null"
|
|
5563
|
+
]
|
|
5564
|
+
},
|
|
5565
|
+
"allocatedAt": {
|
|
5566
|
+
"anyOf": [
|
|
5567
|
+
{
|
|
5568
|
+
"$ref": "#/definitions/firestore-timestamp"
|
|
5569
|
+
},
|
|
5570
|
+
{
|
|
5571
|
+
"type": "null"
|
|
5572
|
+
}
|
|
5573
|
+
],
|
|
5574
|
+
"readOnly": true,
|
|
5575
|
+
"description": "(Read-only) When this allocation was recorded."
|
|
5576
|
+
}
|
|
5577
|
+
},
|
|
5578
|
+
"required": [
|
|
5579
|
+
"outboundPaymentId",
|
|
5580
|
+
"expenseId",
|
|
5581
|
+
"companyId",
|
|
5582
|
+
"amount"
|
|
4680
5583
|
],
|
|
4681
|
-
"
|
|
5584
|
+
"additionalProperties": false,
|
|
5585
|
+
"description": "OutboundPaymentAllocation (GH#15). Subcollection: companies/{companyId}/outboundPayments/{paymentId}/allocations/{allocationId}. Links an OutboundPayment to an Expense. Supports partial and multi-expense allocations.",
|
|
5586
|
+
"example": {
|
|
5587
|
+
"id": null,
|
|
5588
|
+
"outboundPaymentId": "out_ref123",
|
|
5589
|
+
"expenseId": "exp_ref123",
|
|
5590
|
+
"companyId": "comp_xyz789",
|
|
5591
|
+
"amount": 45000,
|
|
5592
|
+
"notes": null,
|
|
5593
|
+
"allocatedAt": "allocatedAt"
|
|
5594
|
+
}
|
|
4682
5595
|
},
|
|
4683
5596
|
"payment-method": {
|
|
4684
5597
|
"type": "string",
|
|
@@ -4718,6 +5631,70 @@
|
|
|
4718
5631
|
],
|
|
4719
5632
|
"description": "Payment lifecycle status (D01 amended). Used by Order, Sale/Purchase, Booking."
|
|
4720
5633
|
},
|
|
5634
|
+
"payment-webhook-endpoint": {
|
|
5635
|
+
"type": "object",
|
|
5636
|
+
"properties": {
|
|
5637
|
+
"token": {
|
|
5638
|
+
"type": "string",
|
|
5639
|
+
"x-immutable": true,
|
|
5640
|
+
"description": "(Immutable) base64url-encoded 24-byte random token. Also the Firestore document ID and the URL path segment."
|
|
5641
|
+
},
|
|
5642
|
+
"company": {
|
|
5643
|
+
"type": "string",
|
|
5644
|
+
"x-immutable": true,
|
|
5645
|
+
"description": "(Immutable) Company slug (e.g. gerko_studios). Identifies the tenant that owns this endpoint."
|
|
5646
|
+
},
|
|
5647
|
+
"provider": {
|
|
5648
|
+
"type": "string",
|
|
5649
|
+
"enum": [
|
|
5650
|
+
"wave",
|
|
5651
|
+
"jeko"
|
|
5652
|
+
],
|
|
5653
|
+
"x-immutable": true,
|
|
5654
|
+
"description": "(Immutable) Payment provider whose webhooks this endpoint accepts."
|
|
5655
|
+
},
|
|
5656
|
+
"secret": {
|
|
5657
|
+
"type": "string",
|
|
5658
|
+
"x-note": "Provider-specific webhook signing secret. Used to verify HMAC-SHA256 signatures on inbound payloads. Never expose to clients.",
|
|
5659
|
+
"readOnly": true,
|
|
5660
|
+
"description": "Webhook signing secret for HMAC verification. Backend-only \u2014 never returned to dashboard or mobile clients."
|
|
5661
|
+
},
|
|
5662
|
+
"active": {
|
|
5663
|
+
"type": "boolean",
|
|
5664
|
+
"description": "Whether this endpoint is currently active. Inactive endpoints reject incoming webhooks."
|
|
5665
|
+
},
|
|
5666
|
+
"createdAt": {
|
|
5667
|
+
"anyOf": [
|
|
5668
|
+
{
|
|
5669
|
+
"$ref": "#/definitions/firestore-timestamp"
|
|
5670
|
+
},
|
|
5671
|
+
{
|
|
5672
|
+
"type": "null"
|
|
5673
|
+
}
|
|
5674
|
+
],
|
|
5675
|
+
"readOnly": true,
|
|
5676
|
+
"description": "(Read-only) When this endpoint was provisioned."
|
|
5677
|
+
}
|
|
5678
|
+
},
|
|
5679
|
+
"required": [
|
|
5680
|
+
"token",
|
|
5681
|
+
"company",
|
|
5682
|
+
"provider",
|
|
5683
|
+
"secret",
|
|
5684
|
+
"active"
|
|
5685
|
+
],
|
|
5686
|
+
"additionalProperties": false,
|
|
5687
|
+
"x-internal": true,
|
|
5688
|
+
"description": "PaymentWebhookEndpoint (GH#41). Collection: payment_webhook_endpoints/{token}. Top-level, not tenant-scoped. Provisioned by superadmin; used by the backend to verify inbound payment webhooks from Wave and Jeko. The delivery log collection (raw webhook payloads + verification outcomes) is tracked in GH#41 \u2014 shape TBD.",
|
|
5689
|
+
"example": {
|
|
5690
|
+
"token": "token",
|
|
5691
|
+
"company": "company",
|
|
5692
|
+
"provider": "wave",
|
|
5693
|
+
"secret": "secret",
|
|
5694
|
+
"active": true,
|
|
5695
|
+
"createdAt": "createdAt"
|
|
5696
|
+
}
|
|
5697
|
+
},
|
|
4721
5698
|
"pending-issue": {
|
|
4722
5699
|
"type": "string",
|
|
4723
5700
|
"enum": [
|
|
@@ -4834,7 +5811,7 @@
|
|
|
4834
5811
|
},
|
|
4835
5812
|
"purchaseDate": {
|
|
4836
5813
|
"$ref": "#/definitions/firestore-timestamp",
|
|
4837
|
-
"description": "Firestore Timestamp
|
|
5814
|
+
"description": "Firestore Timestamp \u2014 Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10)."
|
|
4838
5815
|
},
|
|
4839
5816
|
"createdAt": {
|
|
4840
5817
|
"anyOf": [
|
|
@@ -5814,13 +6791,109 @@
|
|
|
5814
6791
|
],
|
|
5815
6792
|
"description": "Event ticket status (D32). VALID = active and unused."
|
|
5816
6793
|
},
|
|
6794
|
+
"user": {
|
|
6795
|
+
"type": "object",
|
|
6796
|
+
"properties": {
|
|
6797
|
+
"id": {
|
|
6798
|
+
"readOnly": true,
|
|
6799
|
+
"description": "(Read-only) Firebase Auth UID. Matches the Firestore document ID.",
|
|
6800
|
+
"type": [
|
|
6801
|
+
"string",
|
|
6802
|
+
"null"
|
|
6803
|
+
]
|
|
6804
|
+
},
|
|
6805
|
+
"displayName": {
|
|
6806
|
+
"description": "User display name from Firebase Auth or manually set.",
|
|
6807
|
+
"type": [
|
|
6808
|
+
"string",
|
|
6809
|
+
"null"
|
|
6810
|
+
]
|
|
6811
|
+
},
|
|
6812
|
+
"email": {
|
|
6813
|
+
"description": "Email address. Present for email/Google auth users.",
|
|
6814
|
+
"type": [
|
|
6815
|
+
"string",
|
|
6816
|
+
"null"
|
|
6817
|
+
]
|
|
6818
|
+
},
|
|
6819
|
+
"phoneE164": {
|
|
6820
|
+
"x-note": "E.164 format with leading + (e.g. +2250777471485). Canonical phone identity field (decision #27). Distinct from wa_id (Meta conversation key). Server must normalize at write time: 8-digit CI local \u2192 prepend +225.",
|
|
6821
|
+
"x-when": "Set when the user authenticated via phone (WhatsApp OTP) or when a staff member's phone is known. Required for WhatsApp OTP lookup \u2014 add a Firestore composite index on phoneE164.",
|
|
6822
|
+
"description": "E.164-normalized phone number (e.g. +2250777471485). Canonical phone identity field; used for WhatsApp OTP lookup via indexed query.",
|
|
6823
|
+
"type": [
|
|
6824
|
+
"string",
|
|
6825
|
+
"null"
|
|
6826
|
+
]
|
|
6827
|
+
},
|
|
6828
|
+
"companyId": {
|
|
6829
|
+
"x-immutable": true,
|
|
6830
|
+
"description": "(Immutable) FK \u2192 Company document ID. Present for staff accounts scoped to a company.",
|
|
6831
|
+
"type": [
|
|
6832
|
+
"string",
|
|
6833
|
+
"null"
|
|
6834
|
+
]
|
|
6835
|
+
},
|
|
6836
|
+
"role": {
|
|
6837
|
+
"description": "Staff role within the company (e.g. admin, manager, staff). Application-defined.",
|
|
6838
|
+
"type": [
|
|
6839
|
+
"string",
|
|
6840
|
+
"null"
|
|
6841
|
+
]
|
|
6842
|
+
},
|
|
6843
|
+
"isActive": {
|
|
6844
|
+
"description": "Whether this user account is active. Inactive accounts are denied access.",
|
|
6845
|
+
"type": [
|
|
6846
|
+
"boolean",
|
|
6847
|
+
"null"
|
|
6848
|
+
]
|
|
6849
|
+
},
|
|
6850
|
+
"createdAt": {
|
|
6851
|
+
"anyOf": [
|
|
6852
|
+
{
|
|
6853
|
+
"$ref": "#/definitions/firestore-timestamp"
|
|
6854
|
+
},
|
|
6855
|
+
{
|
|
6856
|
+
"type": "null"
|
|
6857
|
+
}
|
|
6858
|
+
],
|
|
6859
|
+
"readOnly": true,
|
|
6860
|
+
"description": "(Read-only) When the user document was created."
|
|
6861
|
+
},
|
|
6862
|
+
"updatedAt": {
|
|
6863
|
+
"anyOf": [
|
|
6864
|
+
{
|
|
6865
|
+
"$ref": "#/definitions/firestore-timestamp"
|
|
6866
|
+
},
|
|
6867
|
+
{
|
|
6868
|
+
"type": "null"
|
|
6869
|
+
}
|
|
6870
|
+
],
|
|
6871
|
+
"readOnly": true,
|
|
6872
|
+
"description": "(Read-only) When the user document was last updated."
|
|
6873
|
+
}
|
|
6874
|
+
},
|
|
6875
|
+
"additionalProperties": false,
|
|
6876
|
+
"description": "User / staff account. Collection: users/{uid}. Document ID is always the Firebase Auth UID (decision #27). phoneE164 is the canonical E.164 phone field for OTP lookup. Legacy phone-keyed documents must be migrated to uid-keyed docs with phoneE164 set.",
|
|
6877
|
+
"example": {
|
|
6878
|
+
"id": null,
|
|
6879
|
+
"displayName": null,
|
|
6880
|
+
"email": null,
|
|
6881
|
+
"phoneE164": null,
|
|
6882
|
+
"companyId": null,
|
|
6883
|
+
"role": null,
|
|
6884
|
+
"isActive": null,
|
|
6885
|
+
"createdAt": "createdAt",
|
|
6886
|
+
"updatedAt": "updatedAt"
|
|
6887
|
+
}
|
|
6888
|
+
},
|
|
5817
6889
|
"waba-label": {
|
|
5818
6890
|
"type": "string",
|
|
5819
6891
|
"enum": [
|
|
5820
6892
|
"cmz",
|
|
5821
6893
|
"val"
|
|
5822
6894
|
],
|
|
5823
|
-
"
|
|
6895
|
+
"x-note": "cmz = \"IngenX - Chez Miss Zahoui\", phone number ID 446424085225188, used for chez_miss_zahoui_* companies. val = \"IngenX - Valets\", phone number ID 425582173979125, used for all other companies. Templates are approved at WABA level \u2014 if cmz and val share a WABA ID, all templates are available to both. Routing logic: functions/src/orders/order.ts#isSender2Company. See messaging/whatsapp-platform-constraints (#50).",
|
|
6896
|
+
"description": "WABA label identifying which Meta Business Account sent/received the message. cmz = Chez Miss Zahoui sender; val = Valets sender. See WhatsApp platform constraints doc (#50)."
|
|
5824
6897
|
},
|
|
5825
6898
|
"whatsapp-button-sub-type": {
|
|
5826
6899
|
"type": "string",
|
|
@@ -5848,6 +6921,7 @@
|
|
|
5848
6921
|
},
|
|
5849
6922
|
"waba": {
|
|
5850
6923
|
"$ref": "#/definitions/waba-label",
|
|
6924
|
+
"x-note": "cmz = \"IngenX - Chez Miss Zahoui\", phone number ID 446424085225188, used for chez_miss_zahoui_* companies. val = \"IngenX - Valets\", phone number ID 425582173979125, used for all other companies. Templates are approved at WABA level \u2014 if cmz and val share a WABA ID, all templates are available to both. Routing logic: functions/src/orders/order.ts#isSender2Company. See messaging/whatsapp-platform-constraints (#50).",
|
|
5851
6925
|
"description": "(Immutable) Human-readable WABA label (cmz | val). See WabaLabel enum.",
|
|
5852
6926
|
"x-immutable": true
|
|
5853
6927
|
},
|
|
@@ -6105,10 +7179,13 @@
|
|
|
6105
7179
|
"to": {
|
|
6106
7180
|
"type": "string",
|
|
6107
7181
|
"x-immutable": true,
|
|
6108
|
-
"
|
|
7182
|
+
"x-note": "This is the Meta wa_id (digits only, no leading +), NOT a literal E.164 string. Meta normalizes phone numbers in non-obvious ways (e.g. +2250777471485 \u2192 wa_id 22577471485, dropping the trunk zero). The only reliable source is Meta's contacts[].wa_id from the send response \u2014 do not derive from E.164 by stripping + alone. The dashboard conversational sends and the whatsapp-server OTP path both use the wa_id form. Confirmed in prod: ING-368.",
|
|
7183
|
+
"x-when": "Set at enqueue time to the recipient's Meta wa_id. Use inbound WhatsappInboundMessage.from (also wa_id) as the join key for the conversation thread.",
|
|
7184
|
+
"description": "(Immutable) Recipient Meta wa_id \u2014 digits only, no leading + (e.g. 22577471485). Equal to the inbound `from` for the same conversation. NOT literal E.164; see x-note (#54)."
|
|
6109
7185
|
},
|
|
6110
7186
|
"waba": {
|
|
6111
7187
|
"$ref": "#/definitions/waba-label",
|
|
7188
|
+
"x-note": "cmz = \"IngenX - Chez Miss Zahoui\", phone number ID 446424085225188, used for chez_miss_zahoui_* companies. val = \"IngenX - Valets\", phone number ID 425582173979125, used for all other companies. Templates are approved at WABA level \u2014 if cmz and val share a WABA ID, all templates are available to both. Routing logic: functions/src/orders/order.ts#isSender2Company. See messaging/whatsapp-platform-constraints (#50).",
|
|
6112
7189
|
"description": "(Immutable) WABA that will send the message. Mirrors inbound `waba`.",
|
|
6113
7190
|
"x-immutable": true
|
|
6114
7191
|
},
|
|
@@ -6215,6 +7292,7 @@
|
|
|
6215
7292
|
},
|
|
6216
7293
|
"wamid": {
|
|
6217
7294
|
"readOnly": true,
|
|
7295
|
+
"x-note": "Useful for audit and dedup. Does NOT enable reply-threading for template messages \u2014 Meta silently ignores context.message_id on templates. Threading only works for kind=text within the 24-hour service window (#50).",
|
|
6218
7296
|
"description": "(Read-only) WhatsApp message ID returned by Meta once the backend sends. Absent while queued or on failure.",
|
|
6219
7297
|
"type": [
|
|
6220
7298
|
"string",
|
|
@@ -6325,6 +7403,7 @@
|
|
|
6325
7403
|
},
|
|
6326
7404
|
"waba": {
|
|
6327
7405
|
"$ref": "#/definitions/waba-label",
|
|
7406
|
+
"x-note": "cmz = \"IngenX - Chez Miss Zahoui\", phone number ID 446424085225188, used for chez_miss_zahoui_* companies. val = \"IngenX - Valets\", phone number ID 425582173979125, used for all other companies. Templates are approved at WABA level \u2014 if cmz and val share a WABA ID, all templates are available to both. Routing logic: functions/src/orders/order.ts#isSender2Company. See messaging/whatsapp-platform-constraints (#50).",
|
|
6328
7407
|
"description": "(Read-only) WABA label this template is approved on. Templates are WABA-scoped. Mirrors WhatsappOutboundMessage.waba.",
|
|
6329
7408
|
"readOnly": true
|
|
6330
7409
|
},
|