@ingenx-io/valets-schema-mcp-server 0.2.5 → 0.2.7

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 (82) hide show
  1. package/data/docs/collections/firestore-paths.md +20 -0
  2. package/data/docs/enums/app-status.md +1 -1
  3. package/data/docs/enums/attention-status.md +1 -1
  4. package/data/docs/enums/booking-status.md +1 -1
  5. package/data/docs/enums/contract-status.md +24 -0
  6. package/data/docs/enums/customer-payment-status.md +2 -2
  7. package/data/docs/enums/customer-payment-target-type.md +2 -2
  8. package/data/docs/enums/delivery-type.md +2 -2
  9. package/data/docs/enums/deployment-link-type.md +2 -2
  10. package/data/docs/enums/event-status.md +2 -2
  11. package/data/docs/enums/expense-payment-status.md +24 -0
  12. package/data/docs/enums/fulfillment-status.md +2 -2
  13. package/data/docs/enums/loyalty-transaction-type.md +2 -2
  14. package/data/docs/enums/milestone-status.md +23 -0
  15. package/data/docs/enums/notification-channel.md +2 -2
  16. package/data/docs/enums/notification-entity-type.md +2 -2
  17. package/data/docs/enums/notification-status.md +2 -2
  18. package/data/docs/enums/order-status.md +2 -2
  19. package/data/docs/enums/outbound-message-format.md +2 -2
  20. package/data/docs/enums/outbound-message-purpose.md +2 -2
  21. package/data/docs/enums/outbound-message-status.md +2 -2
  22. package/data/docs/enums/payment-method.md +4 -3
  23. package/data/docs/enums/payment-proof-status.md +2 -2
  24. package/data/docs/enums/payment-status.md +2 -2
  25. package/data/docs/enums/pending-issue.md +2 -2
  26. package/data/docs/enums/return-status.md +2 -2
  27. package/data/docs/enums/session-status.md +2 -2
  28. package/data/docs/enums/site-status.md +2 -2
  29. package/data/docs/enums/stocktake-frequency.md +2 -2
  30. package/data/docs/enums/stocktake-item-status.md +2 -2
  31. package/data/docs/enums/stocktake-status.md +2 -2
  32. package/data/docs/enums/ticket-status.md +2 -2
  33. package/data/docs/enums/waba-label.md +3 -3
  34. package/data/docs/enums/whatsapp-button-sub-type.md +2 -2
  35. package/data/docs/enums/whatsapp-template-component.md +2 -2
  36. package/data/docs/enums/whatsapp-template-status.md +2 -2
  37. package/data/docs/index.md +15 -6
  38. package/data/docs/models/allowed-user.md +1 -1
  39. package/data/docs/models/analytics-backfill.md +1 -1
  40. package/data/docs/models/analytics-daily.md +1 -1
  41. package/data/docs/models/analytics-event.md +1 -1
  42. package/data/docs/models/analytics-hourly.md +1 -1
  43. package/data/docs/models/app-payment.md +1 -1
  44. package/data/docs/models/app.md +45 -21
  45. package/data/docs/models/booking-version.md +1 -1
  46. package/data/docs/models/booking.md +1 -1
  47. package/data/docs/models/contract.md +454 -0
  48. package/data/docs/models/customer-payment-allocation.md +2 -2
  49. package/data/docs/models/customer-payment.md +23 -22
  50. package/data/docs/models/customer.md +2 -2
  51. package/data/docs/models/event.md +2 -2
  52. package/data/docs/models/expense.md +434 -0
  53. package/data/docs/models/loyalty-config.md +2 -2
  54. package/data/docs/models/loyalty-reward.md +2 -2
  55. package/data/docs/models/loyalty-status.md +2 -2
  56. package/data/docs/models/loyalty-transaction.md +2 -2
  57. package/data/docs/models/magic-link-request.md +2 -2
  58. package/data/docs/models/metrics-current.md +22 -2
  59. package/data/docs/models/metrics-daily.md +92 -41
  60. package/data/docs/models/metrics-monthly.md +22 -2
  61. package/data/docs/models/notification-record.md +2 -2
  62. package/data/docs/models/order-item.md +2 -2
  63. package/data/docs/models/order.md +291 -270
  64. package/data/docs/models/outbound-payment-allocation.md +195 -0
  65. package/data/docs/models/outbound-payment.md +319 -0
  66. package/data/docs/models/payment-webhook-delivery.md +321 -0
  67. package/data/docs/models/payment-webhook-endpoint.md +191 -0
  68. package/data/docs/models/sale.md +2 -2
  69. package/data/docs/models/site-payment.md +2 -2
  70. package/data/docs/models/site.md +2 -2
  71. package/data/docs/models/stocktake-item.md +2 -2
  72. package/data/docs/models/stocktake.md +2 -2
  73. package/data/docs/models/ticket.md +2 -2
  74. package/data/docs/models/user.md +2 -2
  75. package/data/docs/models/whatsapp-inbound-message.md +6 -2
  76. package/data/docs/models/whatsapp-outbound-lifecycle-event.md +2 -2
  77. package/data/docs/models/whatsapp-outbound-message.md +39 -23
  78. package/data/docs/models/whatsapp-template.md +6 -2
  79. package/data/static/llms.txt +242 -10
  80. package/data/static/openapi.yaml +826 -23
  81. package/data/static/schemas.json +877 -71
  82. package/package.json +1 -1
@@ -51,6 +51,16 @@ components:
51
51
  - COMPLETED_MIXED
52
52
  description: Booking lifecycle status. COMPLETED_MIXED = some sessions completed,
53
53
  others cancelled/no-show.
54
+ ContractStatus:
55
+ type: string
56
+ enum:
57
+ - DRAFT
58
+ - ACTIVE
59
+ - COMPLETED
60
+ - TERMINATED
61
+ description: Lifecycle status of a Contract. DRAFT = not yet signed. ACTIVE
62
+ = in force. COMPLETED = all milestones paid and obligations met. TERMINATED
63
+ = ended early by either party.
54
64
  CustomerPaymentStatus:
55
65
  type: string
56
66
  enum:
@@ -99,6 +109,15 @@ components:
99
109
  - COMPLETED
100
110
  description: Ticketed event lifecycle (D32). Mobile-only today; Dashboard in
101
111
  Wave 4.
112
+ ExpensePaymentStatus:
113
+ type: string
114
+ enum:
115
+ - PENDING
116
+ - PARTIALLY_PAID
117
+ - PAID
118
+ - FAILED
119
+ description: Payment status of an Expense. Stored enum — OVERDUE is not stored,
120
+ it is derived at read time from dueDate (#13).
102
121
  FulfillmentStatus:
103
122
  type: string
104
123
  enum:
@@ -120,6 +139,14 @@ components:
120
139
  - BONUS
121
140
  - REFUND
122
141
  description: Loyalty point transaction type (D07). SCREAMING_SNAKE past tense.
142
+ MilestoneStatus:
143
+ type: string
144
+ enum:
145
+ - PENDING
146
+ - INVOICED
147
+ - PAID
148
+ description: Status of a ContractMilestone (#17). PENDING = created, not yet
149
+ invoiced. INVOICED = invoice issued, awaiting payment. PAID = payment received.
123
150
  NotificationChannel:
124
151
  type: string
125
152
  enum:
@@ -204,7 +231,11 @@ components:
204
231
  - PAYPAL
205
232
  - STRIPE
206
233
  - OTHER
207
- description: Unified payment method set with African + global methods (D02).
234
+ - OM
235
+ description: 'Unified payment method set with African + global methods (D02).
236
+ Note: the metrics writer historically emits "OM" instead of "ORANGE_MONEY"
237
+ as a paymentsByMethod map key — treat OM as deprecated; canonical value is
238
+ ORANGE_MONEY (#21).'
208
239
  PaymentProofStatus:
209
240
  type: string
210
241
  enum:
@@ -309,8 +340,9 @@ components:
309
340
  enum:
310
341
  - cmz
311
342
  - val
312
- description: Human-readable WABA label identifying which Meta business number
313
- received the message (GH#36).
343
+ description: WABA label identifying which Meta Business Account sent/received
344
+ the message. cmz = Chez Miss Zahoui sender; val = Valets sender. See WhatsApp
345
+ platform constraints doc (#50).
314
346
  WhatsappButtonSubType:
315
347
  type: string
316
348
  enum:
@@ -1022,18 +1054,22 @@ components:
1022
1054
  - string
1023
1055
  - 'null'
1024
1056
  status:
1025
- $ref: '#/components/schemas/AppStatus'
1026
- description: Lifecycle status (D41/D44). Clients filter by ACTIVE.
1027
- x-note: ACTIVE = live and serving traffic. INACTIVE = intentionally disabled
1028
- by operators. EXPIRED = past expiresAt (derived when isExpired is true;
1029
- may also be stored explicitly). ARCHIVED = soft-deleted; retained for
1030
- audit but not listed by default.
1057
+ anyOf:
1058
+ - $ref: '#/components/schemas/AppStatus'
1059
+ - type: 'null'
1060
+ x-note: Real Firestore documents may be missing this field on pre-backfill
1061
+ records (#26). Treat absent as ACTIVE. A backfill script will populate
1062
+ status=ACTIVE on all existing documents; once confirmed, this field will
1063
+ be promoted to required.
1064
+ x-when: Written by operators via dashboard or by server triggers (expiration).
1065
+ Clients must not use server-side `where(status == ACTIVE)` until backfill
1066
+ is complete — filter client-side instead, treating absent as ACTIVE.
1067
+ description: Lifecycle status (D41/D44). Optional pending backfill of pre-existing
1068
+ records (#26) — treat absent as ACTIVE. Clients filter by ACTIVE.
1031
1069
  x-see:
1032
1070
  decisions:
1033
1071
  - D41
1034
1072
  - D44
1035
- x-when: Written by operators via dashboard or by server triggers (expiration).
1036
- Clients filter by ACTIVE.
1037
1073
  deploymentLinks:
1038
1074
  type: array
1039
1075
  items:
@@ -1154,7 +1190,6 @@ components:
1154
1190
  required:
1155
1191
  - companyId
1156
1192
  - name
1157
- - status
1158
1193
  - deploymentLinks
1159
1194
  - createdBy
1160
1195
  - analyticsEnabled
@@ -1172,7 +1207,6 @@ components:
1172
1207
  required:
1173
1208
  - companyId
1174
1209
  - name
1175
- - status
1176
1210
  - deploymentLinks
1177
1211
  - createdBy
1178
1212
  - analyticsEnabled
@@ -1882,6 +1916,156 @@ components:
1882
1916
  description: 'BookingVersion model (D18). Collection: companies/{companyId}/bookings/{bookingId}/versions/{versionId}.
1883
1917
  Server-owned audit trail written by Firebase trigger on every Booking mutation.
1884
1918
  Captures writes from all clients.'
1919
+ Contract:
1920
+ type: object
1921
+ properties:
1922
+ id:
1923
+ readOnly: true
1924
+ description: (Read-only) Firestore document ID, auto-generated.
1925
+ type:
1926
+ - string
1927
+ - 'null'
1928
+ companyId:
1929
+ type: string
1930
+ x-immutable: true
1931
+ description: (Immutable) FK → Company document ID.
1932
+ payeeId:
1933
+ type: string
1934
+ x-immutable: true
1935
+ description: '(Immutable) FK → Payee document ID. Full Payee model tracked
1936
+ in #20.'
1937
+ title:
1938
+ type: string
1939
+ description: Contract title or reference name shown in dashboards.
1940
+ description:
1941
+ description: Optional freeform description of the contract scope.
1942
+ type:
1943
+ - string
1944
+ - 'null'
1945
+ status:
1946
+ $ref: '#/components/schemas/ContractStatus'
1947
+ description: Contract lifecycle status (#14). Query by ACTIVE — do not use
1948
+ an isActive boolean.
1949
+ currency:
1950
+ type: string
1951
+ const: XOF
1952
+ description: Currency code. Locked to XOF (#18). Multi-currency support
1953
+ requires a deliberate schema version bump.
1954
+ totalAmount:
1955
+ type: number
1956
+ description: Total contract value (XOF).
1957
+ startDate:
1958
+ type: string
1959
+ description: Contract start date (ISO 8601 YYYY-MM-DD).
1960
+ endDate:
1961
+ description: Contract end date (ISO 8601 YYYY-MM-DD). Absent for open-ended
1962
+ contracts.
1963
+ type:
1964
+ - string
1965
+ - 'null'
1966
+ milestones:
1967
+ description: Ordered list of payment milestones. Embedded in the contract
1968
+ document (#17).
1969
+ type:
1970
+ - array
1971
+ - 'null'
1972
+ items:
1973
+ type: object
1974
+ properties:
1975
+ id:
1976
+ type: string
1977
+ description: Client-generated milestone ID (UUID or slug). Unique
1978
+ within the contract.
1979
+ title:
1980
+ type: string
1981
+ description: Milestone description or deliverable name.
1982
+ amount:
1983
+ type: number
1984
+ description: Amount due at this milestone (XOF).
1985
+ dueDate:
1986
+ description: ISO 8601 date string (YYYY-MM-DD) when the milestone
1987
+ is due. OVERDUE is derived at read time from dueDate — not stored.
1988
+ type: string
1989
+ status:
1990
+ $ref: '#/components/schemas/MilestoneStatus'
1991
+ x-note: Existing Firestore documents may have lowercase values (pending,
1992
+ invoiced, paid) from before this enum was introduced (#17). Normalize
1993
+ on read or via migration script.
1994
+ description: Milestone payment status (#17). OVERDUE is derived at
1995
+ read time from dueDate, not stored.
1996
+ invoicedAt:
1997
+ $ref: '#/components/schemas/FirestoreTimestamp'
1998
+ description: When the invoice was issued for this milestone.
1999
+ paidAt:
2000
+ $ref: '#/components/schemas/FirestoreTimestamp'
2001
+ description: When payment was received for this milestone.
2002
+ notes:
2003
+ description: Optional notes for this milestone.
2004
+ type: string
2005
+ required:
2006
+ - id
2007
+ - title
2008
+ - amount
2009
+ - status
2010
+ additionalProperties: false
2011
+ description: ContractMilestone — embedded sub-object on Contract (#17).
2012
+ Not a separate collection.
2013
+ notes:
2014
+ description: Optional internal notes.
2015
+ type:
2016
+ - string
2017
+ - 'null'
2018
+ createdBy:
2019
+ type: string
2020
+ x-immutable: true
2021
+ description: (Immutable) FK → User/staff UID who created the contract.
2022
+ createdAt:
2023
+ anyOf:
2024
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2025
+ - type: 'null'
2026
+ readOnly: true
2027
+ description: (Read-only) Server-generated creation timestamp.
2028
+ updatedAt:
2029
+ anyOf:
2030
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2031
+ - type: 'null'
2032
+ readOnly: true
2033
+ description: (Read-only) Server-generated update timestamp.
2034
+ required:
2035
+ - companyId
2036
+ - payeeId
2037
+ - title
2038
+ - status
2039
+ - currency
2040
+ - totalAmount
2041
+ - startDate
2042
+ - createdBy
2043
+ additionalProperties: false
2044
+ description: 'Contract model (GH#14/#17/#18). Collection: companies/{companyId}/contracts/{contractId}.
2045
+ Service/supplier contract between a company and a Payee. No isActive field
2046
+ — query by status. Currency locked to XOF. Milestones use MilestoneStatus
2047
+ enum (no stored OVERDUE).'
2048
+ ContractCreate:
2049
+ allOf:
2050
+ - $ref: '#/components/schemas/Contract'
2051
+ description: Write payload for creating a new Contract document. Fields marked
2052
+ `readOnly` are server-set and must not be included. Fields marked `x-immutable`
2053
+ may be set once at creation.
2054
+ required:
2055
+ - companyId
2056
+ - payeeId
2057
+ - title
2058
+ - status
2059
+ - currency
2060
+ - totalAmount
2061
+ - startDate
2062
+ - createdBy
2063
+ ContractUpdate:
2064
+ allOf:
2065
+ - $ref: '#/components/schemas/Contract'
2066
+ description: Write payload for partial update (PATCH) of a Contract document.
2067
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
2068
+ sent.
1885
2069
  Customer:
1886
2070
  type: object
1887
2071
  properties:
@@ -2052,7 +2236,10 @@ components:
2052
2236
  notes (#10).'
2053
2237
  paymentMethod:
2054
2238
  $ref: '#/components/schemas/PaymentMethod'
2055
- description: Unified payment method set with African + global methods (D02).
2239
+ description: 'Unified payment method set with African + global methods (D02).
2240
+ Note: the metrics writer historically emits "OM" instead of "ORANGE_MONEY"
2241
+ as a paymentsByMethod map key — treat OM as deprecated; canonical value
2242
+ is ORANGE_MONEY (#21).'
2056
2243
  referenceNumber:
2057
2244
  type: string
2058
2245
  description: Unique payment reference (receipt number, transaction ID, etc.).
@@ -2349,6 +2536,146 @@ components:
2349
2536
  - $ref: '#/components/schemas/Event'
2350
2537
  description: Write payload for partial update (PATCH) of a Event document. All
2351
2538
  fields optional. Fields marked `readOnly` or `x-immutable` must not be sent.
2539
+ Expense:
2540
+ type: object
2541
+ properties:
2542
+ id:
2543
+ readOnly: true
2544
+ description: (Read-only) Firestore document ID, auto-generated.
2545
+ type:
2546
+ - string
2547
+ - 'null'
2548
+ companyId:
2549
+ type: string
2550
+ x-immutable: true
2551
+ description: (Immutable) FK → Company document ID.
2552
+ title:
2553
+ type: string
2554
+ description: Human-readable expense title (e.g. "Loyer mars 2026", "Facture
2555
+ EAU").
2556
+ description:
2557
+ description: Optional longer description or notes.
2558
+ type:
2559
+ - string
2560
+ - 'null'
2561
+ amount:
2562
+ type: number
2563
+ description: Total expense amount (XOF).
2564
+ currency:
2565
+ type: string
2566
+ const: XOF
2567
+ description: Currency code. Locked to XOF.
2568
+ dueDate:
2569
+ anyOf:
2570
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2571
+ - type: 'null'
2572
+ description: When the expense is due. Used to compute OVERDUE state at read
2573
+ time (not stored in paymentStatus).
2574
+ paymentStatus:
2575
+ $ref: '#/components/schemas/ExpensePaymentStatus'
2576
+ x-note: 'OVERDUE is intentionally excluded — it is never stored in Firestore.
2577
+ Compute it at read time: expense.paymentStatus !== PAID && expense.dueDate
2578
+ < now() (#13). Helper: isExpenseOverdue(expense: Expense): boolean.'
2579
+ description: Current payment status. OVERDUE is never stored — derive from
2580
+ dueDate at read time (#13).
2581
+ amountDue:
2582
+ x-note: Authoritative for simple (non-allocation) expenses. Legacy field
2583
+ maintained for backward compatibility when no OutboundPaymentAllocation
2584
+ records exist (#12).
2585
+ description: Amount still owed. Authoritative when no OutboundPaymentAllocation
2586
+ records exist for this expense.
2587
+ type:
2588
+ - number
2589
+ - 'null'
2590
+ amountPaid:
2591
+ description: Amount paid to date (simple tracking, no allocations).
2592
+ type:
2593
+ - number
2594
+ - 'null'
2595
+ allocatedAmount:
2596
+ readOnly: true
2597
+ x-note: Server-set — computed from OutboundPaymentAllocation records. Clients
2598
+ must never write this field (#12).
2599
+ description: (Read-only) Total amount allocated via OutboundPaymentAllocation
2600
+ records. Authoritative when allocations exist.
2601
+ type:
2602
+ - number
2603
+ - 'null'
2604
+ balance:
2605
+ readOnly: true
2606
+ x-note: Server-set — computed as amount - allocatedAmount. Authoritative
2607
+ remaining amount when allocations exist. Prefer this over amountDue when
2608
+ OutboundPaymentAllocation records are present (#12).
2609
+ description: '(Read-only) Remaining balance: amount minus allocatedAmount.
2610
+ Authoritative when OutboundPaymentAllocation records exist.'
2611
+ type:
2612
+ - number
2613
+ - 'null'
2614
+ paidDate:
2615
+ anyOf:
2616
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2617
+ - type: 'null'
2618
+ x-when: Set when paymentStatus transitions to PAID (fully settled). Null
2619
+ for partially paid or unpaid expenses. For multi-payment history, read
2620
+ OutboundPaymentAllocation records (#19).
2621
+ description: When the expense was fully settled (paymentStatus = PAID).
2622
+ Null otherwise.
2623
+ paymentReference:
2624
+ x-note: Reference for the most recent payment transaction (check number,
2625
+ wire ID, Wave ref, etc.). Overwritten on each payment. For full payment
2626
+ history read OutboundPaymentAllocation records (#19).
2627
+ description: Most recent payment transaction reference. Overwritten on each
2628
+ payment; not a full history.
2629
+ type:
2630
+ - string
2631
+ - 'null'
2632
+ createdBy:
2633
+ type: string
2634
+ x-immutable: true
2635
+ description: (Immutable) FK → User/staff UID who created this expense record.
2636
+ createdAt:
2637
+ anyOf:
2638
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2639
+ - type: 'null'
2640
+ readOnly: true
2641
+ description: (Read-only) Server-generated creation timestamp.
2642
+ updatedAt:
2643
+ anyOf:
2644
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2645
+ - type: 'null'
2646
+ readOnly: true
2647
+ description: (Read-only) Server-generated last-update timestamp.
2648
+ required:
2649
+ - companyId
2650
+ - title
2651
+ - amount
2652
+ - currency
2653
+ - paymentStatus
2654
+ - createdBy
2655
+ additionalProperties: false
2656
+ description: 'Expense (GH#12/#13/#19 partial). Collection: companies/{companyId}/expenses/{expenseId}.
2657
+ Dual balance tracking: amountPaid/amountDue for simple expenses; allocatedAmount/balance
2658
+ (server-set) for allocation-settled expenses. OVERDUE excluded from stored
2659
+ enum — derive at read time. Full domain (Payee FK, Contract FK) in #20.'
2660
+ ExpenseCreate:
2661
+ allOf:
2662
+ - $ref: '#/components/schemas/Expense'
2663
+ description: Write payload for creating a new Expense document. Fields marked
2664
+ `readOnly` are server-set and must not be included. Fields marked `x-immutable`
2665
+ may be set once at creation.
2666
+ required:
2667
+ - companyId
2668
+ - title
2669
+ - amount
2670
+ - currency
2671
+ - paymentStatus
2672
+ - createdBy
2673
+ ExpenseUpdate:
2674
+ allOf:
2675
+ - $ref: '#/components/schemas/Expense'
2676
+ description: Write payload for partial update (PATCH) of a Expense document.
2677
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
2678
+ sent.
2352
2679
  LoyaltyConfig:
2353
2680
  type: object
2354
2681
  properties:
@@ -2835,6 +3162,9 @@ components:
2835
3162
  orderCompletionRate30d:
2836
3163
  type: number
2837
3164
  readOnly: true
3165
+ x-note: Firestore REST API v1 writes integerValue when the stored value
3166
+ is whole (e.g. 0), doubleValue when fractional (e.g. 82.17). Admin SDK
3167
+ smooths this to a JS number; REST clients must coerce the union (#11).
2838
3168
  description: (Read-only) Percentage of orders completed or delivered in
2839
3169
  the last 30 days. Always full recalc.
2840
3170
  todayPurchasesCount:
@@ -2851,11 +3181,16 @@ components:
2851
3181
  averagePurchaseAmount:
2852
3182
  type: number
2853
3183
  readOnly: true
3184
+ x-note: Firestore REST API v1 writes integerValue when whole (e.g. 10070),
3185
+ doubleValue when fractional (e.g. 10006.5). Admin SDK smooths this; REST
3186
+ clients must coerce (#11).
2854
3187
  description: (Read-only) Average purchase value across last 200 purchases
2855
3188
  (rolling, not time-windowed).
2856
3189
  monthlyRevenue:
2857
3190
  type: number
2858
3191
  readOnly: true
3192
+ x-note: Observed as integerValue on all sampled tenants (whole XOF amounts).
3193
+ Will be doubleValue if fractional. REST clients must coerce (#11).
2859
3194
  description: (Read-only) Total purchase value in the current calendar month
2860
3195
  (UTC).
2861
3196
  monthlyPurchasesCount:
@@ -2892,6 +3227,8 @@ components:
2892
3227
  todayCollectedAmount:
2893
3228
  type: number
2894
3229
  readOnly: true
3230
+ x-note: Observed as integerValue "0" on all sampled tenants. Will be doubleValue
3231
+ if fractional payments occur. REST clients must coerce (#11).
2895
3232
  description: (Read-only) Sum of totalAmount for bookings where PAYMENT_PAID_AT
2896
3233
  is today. Resets at midnight.
2897
3234
  todayRevenue:
@@ -2937,6 +3274,8 @@ components:
2937
3274
  averageRating:
2938
3275
  type: number
2939
3276
  readOnly: true
3277
+ x-note: Observed as integerValue "0" on all tenants (no reviews yet). Will
3278
+ be doubleValue once real ratings exist. REST clients must coerce (#11).
2940
3279
  description: (Read-only) Average rating from the last 200 reviews (rolling).
2941
3280
  Always full recalc.
2942
3281
  lowStockItemsCount:
@@ -3084,6 +3423,9 @@ components:
3084
3423
  orderCompletionRate30d:
3085
3424
  type: number
3086
3425
  readOnly: true
3426
+ x-note: Firestore REST API v1 writes integerValue when the stored value
3427
+ is whole (e.g. 0), doubleValue when fractional (e.g. 82.17). Admin SDK
3428
+ smooths this to a JS number; REST clients must coerce the union (#11).
3087
3429
  description: (Read-only) Percentage of orders completed or delivered in
3088
3430
  the last 30 days. Always full recalc.
3089
3431
  todayPurchasesCount:
@@ -3100,11 +3442,16 @@ components:
3100
3442
  averagePurchaseAmount:
3101
3443
  type: number
3102
3444
  readOnly: true
3445
+ x-note: Firestore REST API v1 writes integerValue when whole (e.g. 10070),
3446
+ doubleValue when fractional (e.g. 10006.5). Admin SDK smooths this; REST
3447
+ clients must coerce (#11).
3103
3448
  description: (Read-only) Average purchase value across last 200 purchases
3104
3449
  (rolling, not time-windowed).
3105
3450
  monthlyRevenue:
3106
3451
  type: number
3107
3452
  readOnly: true
3453
+ x-note: Observed as integerValue on all sampled tenants (whole XOF amounts).
3454
+ Will be doubleValue if fractional. REST clients must coerce (#11).
3108
3455
  description: (Read-only) Total purchase value in the current calendar month
3109
3456
  (UTC).
3110
3457
  monthlyPurchasesCount:
@@ -3141,6 +3488,8 @@ components:
3141
3488
  todayCollectedAmount:
3142
3489
  type: number
3143
3490
  readOnly: true
3491
+ x-note: Observed as integerValue "0" on all sampled tenants. Will be doubleValue
3492
+ if fractional payments occur. REST clients must coerce (#11).
3144
3493
  description: (Read-only) Sum of totalAmount for bookings where PAYMENT_PAID_AT
3145
3494
  is today. Resets at midnight.
3146
3495
  todayRevenue:
@@ -3186,6 +3535,8 @@ components:
3186
3535
  averageRating:
3187
3536
  type: number
3188
3537
  readOnly: true
3538
+ x-note: Observed as integerValue "0" on all tenants (no reviews yet). Will
3539
+ be doubleValue once real ratings exist. REST clients must coerce (#11).
3189
3540
  description: (Read-only) Average rating from the last 200 reviews (rolling).
3190
3541
  Always full recalc.
3191
3542
  lowStockItemsCount:
@@ -3252,17 +3603,34 @@ components:
3252
3603
  maximum: 9007199254740991
3253
3604
  paymentsByMethod:
3254
3605
  readOnly: true
3255
- x-note: Always {} until the first real payment is recorded. Present on all
3256
- tenants once rewritten by current aggregator (#8).
3257
- description: (Read-only, Optional) Map of PaymentMethod total amount for
3258
- the current day. Empty map {} until first payment.
3606
+ x-note: Map keys use short codes in practice (OM, CASH, WAVE) OM is a
3607
+ non-canonical alias for ORANGE_MONEY emitted by the metrics writer (#21).
3608
+ Value shape { total, count } differs from MetricsCurrent.paymentsByMethod
3609
+ (bare number).
3610
+ description: '(Read-only, Optional) Payment breakdown by method for the
3611
+ day. Keys are PaymentMethod values (or legacy short code OM). Value is
3612
+ { total: XOF amount, count: number of payments }.'
3259
3613
  type:
3260
3614
  - object
3261
3615
  - 'null'
3262
3616
  propertyNames:
3263
3617
  type: string
3264
3618
  additionalProperties:
3265
- type: number
3619
+ type: object
3620
+ properties:
3621
+ total:
3622
+ type: number
3623
+ description: Sum of payment amounts for this method on the given day
3624
+ (XOF).
3625
+ count:
3626
+ type: integer
3627
+ minimum: -9007199254740991
3628
+ maximum: 9007199254740991
3629
+ description: Number of payments using this method on the given day.
3630
+ required:
3631
+ - total
3632
+ - count
3633
+ additionalProperties: false
3266
3634
  computedForDay:
3267
3635
  type: string
3268
3636
  readOnly: true
@@ -3305,9 +3673,17 @@ components:
3305
3673
  - generatedAt
3306
3674
  - date
3307
3675
  additionalProperties: false
3676
+ x-note: 'Zero-activity behavior (#9): the computeDailyCompanyMetrics cron (02:00
3677
+ UTC) does NOT write a snapshot for days with no activity. A missing document
3678
+ means either zero activity or the cron did not run — consumers cannot distinguish
3679
+ the two from the absence alone. To detect cron gaps, compare the latest document
3680
+ date with today; a gap larger than 1 day on an otherwise active tenant indicates
3681
+ a cron failure. Observed: 3 of 4 zahoui tenants had gaps; bingerville (active)
3682
+ had a document every day.'
3308
3683
  description: 'Daily metrics snapshot. Collection: companies/{companyId}/metrics_daily/{YYYY-MM-DD}.
3309
3684
  Same fields as MetricsCurrent plus `date`. Written by computeDailyCompanyMetrics
3310
- cron (02:00 UTC) and inline after full recalcs.'
3685
+ cron (02:00 UTC) when the company had activity that day; zero-activity days
3686
+ may produce no document (#9).'
3311
3687
  MetricsDailyCreate:
3312
3688
  allOf:
3313
3689
  - $ref: '#/components/schemas/MetricsDaily'
@@ -3339,6 +3715,9 @@ components:
3339
3715
  orderCompletionRate30d:
3340
3716
  type: number
3341
3717
  readOnly: true
3718
+ x-note: Firestore REST API v1 writes integerValue when the stored value
3719
+ is whole (e.g. 0), doubleValue when fractional (e.g. 82.17). Admin SDK
3720
+ smooths this to a JS number; REST clients must coerce the union (#11).
3342
3721
  description: (Read-only) Percentage of orders completed or delivered in
3343
3722
  the last 30 days. Always full recalc.
3344
3723
  todayPurchasesCount:
@@ -3355,11 +3734,16 @@ components:
3355
3734
  averagePurchaseAmount:
3356
3735
  type: number
3357
3736
  readOnly: true
3737
+ x-note: Firestore REST API v1 writes integerValue when whole (e.g. 10070),
3738
+ doubleValue when fractional (e.g. 10006.5). Admin SDK smooths this; REST
3739
+ clients must coerce (#11).
3358
3740
  description: (Read-only) Average purchase value across last 200 purchases
3359
3741
  (rolling, not time-windowed).
3360
3742
  monthlyRevenue:
3361
3743
  type: number
3362
3744
  readOnly: true
3745
+ x-note: Observed as integerValue on all sampled tenants (whole XOF amounts).
3746
+ Will be doubleValue if fractional. REST clients must coerce (#11).
3363
3747
  description: (Read-only) Total purchase value in the current calendar month
3364
3748
  (UTC).
3365
3749
  monthlyPurchasesCount:
@@ -3396,6 +3780,8 @@ components:
3396
3780
  todayCollectedAmount:
3397
3781
  type: number
3398
3782
  readOnly: true
3783
+ x-note: Observed as integerValue "0" on all sampled tenants. Will be doubleValue
3784
+ if fractional payments occur. REST clients must coerce (#11).
3399
3785
  description: (Read-only) Sum of totalAmount for bookings where PAYMENT_PAID_AT
3400
3786
  is today. Resets at midnight.
3401
3787
  todayRevenue:
@@ -3441,6 +3827,8 @@ components:
3441
3827
  averageRating:
3442
3828
  type: number
3443
3829
  readOnly: true
3830
+ x-note: Observed as integerValue "0" on all tenants (no reviews yet). Will
3831
+ be doubleValue once real ratings exist. REST clients must coerce (#11).
3444
3832
  description: (Read-only) Average rating from the last 200 reviews (rolling).
3445
3833
  Always full recalc.
3446
3834
  lowStockItemsCount:
@@ -3781,6 +4169,17 @@ components:
3781
4169
  type: string
3782
4170
  readOnly: true
3783
4171
  description: (Read-only) Server-generated order number.
4172
+ followUpUrl:
4173
+ x-note: Standardized casing is followUpUrl (not followUpURL). Mobile legacy
4174
+ code reads both casings for backward compat but writes followUpUrl going
4175
+ forward (#6).
4176
+ x-when: 'Used by mobile to generate QR codes on printed receipts. If absent,
4177
+ the mobile app derives a deterministic fallback: https://zahoui.web.app/reviews?orderNumber={orderNumber}.'
4178
+ description: 'Optional URL for receipt QR code / post-order follow-up. Canonical
4179
+ casing: followUpUrl (#6).'
4180
+ type:
4181
+ - string
4182
+ - 'null'
3784
4183
  status:
3785
4184
  $ref: '#/components/schemas/OrderStatus'
3786
4185
  description: Core lifecycle status (D34, MIG-11). See OrderStatus enum for
@@ -3879,7 +4278,10 @@ components:
3879
4278
  anyOf:
3880
4279
  - $ref: '#/components/schemas/PaymentMethod'
3881
4280
  - type: 'null'
3882
- description: Unified payment method set with African + global methods (D02).
4281
+ description: 'Unified payment method set with African + global methods (D02).
4282
+ Note: the metrics writer historically emits "OM" instead of "ORANGE_MONEY"
4283
+ as a paymentsByMethod map key — treat OM as deprecated; canonical value
4284
+ is ORANGE_MONEY (#21).'
3883
4285
  invoiceId:
3884
4286
  description: FK → Invoice document ID.
3885
4287
  type:
@@ -4315,6 +4717,376 @@ components:
4315
4717
  description: Write payload for partial update (PATCH) of a OrderItem document.
4316
4718
  All fields optional. Fields marked `readOnly` or `x-immutable` must not be
4317
4719
  sent.
4720
+ OutboundPayment:
4721
+ type: object
4722
+ properties:
4723
+ id:
4724
+ readOnly: true
4725
+ description: (Read-only) Firestore document ID, auto-generated.
4726
+ type:
4727
+ - string
4728
+ - 'null'
4729
+ companyId:
4730
+ type: string
4731
+ x-immutable: true
4732
+ description: (Immutable) FK → Company document ID.
4733
+ payeeId:
4734
+ type: string
4735
+ x-immutable: true
4736
+ description: '(Immutable) FK → Payee document ID. Full Payee model tracked
4737
+ in #20.'
4738
+ contractId:
4739
+ x-immutable: true
4740
+ description: (Immutable, Optional) FK → Contract document ID. Absent for
4741
+ ad-hoc payments outside a formal contract.
4742
+ type:
4743
+ - string
4744
+ - 'null'
4745
+ amount:
4746
+ type: number
4747
+ description: Amount paid (XOF).
4748
+ currency:
4749
+ type: string
4750
+ const: XOF
4751
+ description: Currency code. Locked to XOF — consistent with Contract and
4752
+ CustomerPayment.
4753
+ method:
4754
+ $ref: '#/components/schemas/PaymentMethod'
4755
+ description: Payment method used (e.g. BANK_TRANSFER, MOBILE_MONEY, CHECK).
4756
+ reference:
4757
+ description: Optional payment reference — bank transaction number, check
4758
+ number, Wave ref, etc.
4759
+ type:
4760
+ - string
4761
+ - 'null'
4762
+ paidAt:
4763
+ $ref: '#/components/schemas/FirestoreTimestamp'
4764
+ description: When the payment was made.
4765
+ notes:
4766
+ description: Optional internal notes.
4767
+ type:
4768
+ - string
4769
+ - 'null'
4770
+ createdBy:
4771
+ type: string
4772
+ x-immutable: true
4773
+ description: (Immutable) FK → User/staff UID who recorded the payment.
4774
+ createdAt:
4775
+ anyOf:
4776
+ - $ref: '#/components/schemas/FirestoreTimestamp'
4777
+ - type: 'null'
4778
+ readOnly: true
4779
+ description: (Read-only) Server-generated creation timestamp.
4780
+ updatedAt:
4781
+ anyOf:
4782
+ - $ref: '#/components/schemas/FirestoreTimestamp'
4783
+ - type: 'null'
4784
+ readOnly: true
4785
+ description: (Read-only) Server-generated update timestamp.
4786
+ required:
4787
+ - companyId
4788
+ - payeeId
4789
+ - amount
4790
+ - currency
4791
+ - method
4792
+ - paidAt
4793
+ - createdBy
4794
+ additionalProperties: false
4795
+ description: 'OutboundPayment (GH#15). Collection: companies/{companyId}/outboundPayments/{paymentId}.
4796
+ Single outbound transfer to a Payee. Canonical name chosen over SupplierPayment/VendorPayment
4797
+ — direction-based, agnostic of payee type. Currency locked to XOF.'
4798
+ OutboundPaymentCreate:
4799
+ allOf:
4800
+ - $ref: '#/components/schemas/OutboundPayment'
4801
+ description: Write payload for creating a new OutboundPayment document. Fields
4802
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
4803
+ may be set once at creation.
4804
+ required:
4805
+ - companyId
4806
+ - payeeId
4807
+ - amount
4808
+ - currency
4809
+ - method
4810
+ - paidAt
4811
+ - createdBy
4812
+ OutboundPaymentUpdate:
4813
+ allOf:
4814
+ - $ref: '#/components/schemas/OutboundPayment'
4815
+ description: Write payload for partial update (PATCH) of a OutboundPayment document.
4816
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
4817
+ sent.
4818
+ OutboundPaymentAllocation:
4819
+ type: object
4820
+ properties:
4821
+ id:
4822
+ readOnly: true
4823
+ description: (Read-only) Firestore document ID, auto-generated.
4824
+ type:
4825
+ - string
4826
+ - 'null'
4827
+ outboundPaymentId:
4828
+ type: string
4829
+ x-immutable: true
4830
+ description: (Immutable) FK → OutboundPayment document ID (parent).
4831
+ expenseId:
4832
+ type: string
4833
+ x-immutable: true
4834
+ description: '(Immutable) FK → Expense document ID. Full Expense model tracked
4835
+ in #20.'
4836
+ companyId:
4837
+ type: string
4838
+ x-immutable: true
4839
+ description: (Immutable) FK → Company document ID. Denormalized for collection-group
4840
+ queries.
4841
+ amount:
4842
+ type: number
4843
+ description: Amount of this payment allocated to the referenced expense
4844
+ (XOF).
4845
+ notes:
4846
+ description: Optional notes about this allocation.
4847
+ type:
4848
+ - string
4849
+ - 'null'
4850
+ allocatedAt:
4851
+ anyOf:
4852
+ - $ref: '#/components/schemas/FirestoreTimestamp'
4853
+ - type: 'null'
4854
+ readOnly: true
4855
+ description: (Read-only) When this allocation was recorded.
4856
+ required:
4857
+ - outboundPaymentId
4858
+ - expenseId
4859
+ - companyId
4860
+ - amount
4861
+ additionalProperties: false
4862
+ description: 'OutboundPaymentAllocation (GH#15). Subcollection: companies/{companyId}/outboundPayments/{paymentId}/allocations/{allocationId}.
4863
+ Links an OutboundPayment to an Expense. Supports partial and multi-expense
4864
+ allocations.'
4865
+ OutboundPaymentAllocationCreate:
4866
+ allOf:
4867
+ - $ref: '#/components/schemas/OutboundPaymentAllocation'
4868
+ description: Write payload for creating a new OutboundPaymentAllocation document.
4869
+ Fields marked `readOnly` are server-set and must not be included. Fields marked
4870
+ `x-immutable` may be set once at creation.
4871
+ required:
4872
+ - outboundPaymentId
4873
+ - expenseId
4874
+ - companyId
4875
+ - amount
4876
+ OutboundPaymentAllocationUpdate:
4877
+ allOf:
4878
+ - $ref: '#/components/schemas/OutboundPaymentAllocation'
4879
+ description: Write payload for partial update (PATCH) of a OutboundPaymentAllocation
4880
+ document. All fields optional. Fields marked `readOnly` or `x-immutable` must
4881
+ not be sent.
4882
+ PaymentWebhookDelivery:
4883
+ type: object
4884
+ properties:
4885
+ id:
4886
+ readOnly: true
4887
+ description: (Read-only) Firestore document ID, auto-generated.
4888
+ type:
4889
+ - string
4890
+ - 'null'
4891
+ company:
4892
+ type: string
4893
+ x-immutable: true
4894
+ description: (Immutable) Company document ID (e.g. "gerko_studios"). Resolved
4895
+ from the matched endpoint at delivery time.
4896
+ provider:
4897
+ type: string
4898
+ x-note: 'Observed value: "jeko". The inner payload.paymentMethod may differ
4899
+ (e.g. "wave") — provider here is the webhook sender, not the payment rail
4900
+ (#42).'
4901
+ description: Payment provider that sent this webhook (e.g. "jeko", "wave").
4902
+ eventType:
4903
+ type: string
4904
+ x-note: 'Observed value: "transaction.payment". Jeko docs say "transaction.completed"
4905
+ — full set per provider unconfirmed (#42). Use z.string() to avoid rejecting
4906
+ unseen values.'
4907
+ description: 'Provider-defined event type. Observed: "transaction.payment".
4908
+ Full set per provider TBD (#42).'
4909
+ status:
4910
+ type: string
4911
+ x-note: 'Observed values: "success", "error". Indicates whether the webhook
4912
+ was processed without errors.'
4913
+ description: 'Processing outcome. Observed values: "success" | "error".'
4914
+ verified:
4915
+ type: string
4916
+ x-note: 'Observed value: "verified". Indicates HMAC-SHA256 signature verification
4917
+ result. Full set of failure values unconfirmed (#42).'
4918
+ description: 'Signature verification result. Observed: "verified". Other
4919
+ values (e.g. "unverified", "error") may exist but are unconfirmed.'
4920
+ endpointStatus:
4921
+ type: string
4922
+ x-note: 'Observed value: "matched". Indicates whether the token resolved
4923
+ to a known PaymentWebhookEndpoint. Other values (e.g. "not_found", "inactive")
4924
+ unconfirmed (#42).'
4925
+ description: 'Endpoint resolution result. Observed: "matched". Other values
4926
+ unconfirmed (#42).'
4927
+ processed:
4928
+ type: boolean
4929
+ x-note: Set to true once the delivery has been reconciled to the AppPayment
4930
+ ledger (companies/{cid}/apps/{appId}/payments). Back-reference FK not
4931
+ stored on this doc — reconciliation is tracked on the AppPayment side.
4932
+ description: Whether this delivery has been reconciled to the AppPayment
4933
+ ledger.
4934
+ reference:
4935
+ type: string
4936
+ x-note: Equals payload.id (provider transaction ID). Used as dedup key —
4937
+ the backend checks this before processing (#42 ask 3).
4938
+ description: Provider transaction ID (= payload.id). Canonical dedup key.
4939
+ amount:
4940
+ type: number
4941
+ description: Payment amount, denormalized from payload.amount.amount (XOF).
4942
+ currency:
4943
+ type: string
4944
+ description: Currency code, denormalized from payload.amount.currency (e.g.
4945
+ "XOF").
4946
+ payload:
4947
+ type: object
4948
+ propertyNames:
4949
+ type: string
4950
+ additionalProperties: {}
4951
+ x-note: 'Parsed provider request body. Contains PII: counterpartIdentifier
4952
+ and counterpartLabel hold customer phone numbers. Retention/TTL policy
4953
+ TBD (#42 ask 5).'
4954
+ description: Full parsed provider payload. Shape varies by provider. Contains
4955
+ PII (customer phone). See x-note for retention policy status.
4956
+ rawBody:
4957
+ type: string
4958
+ x-note: Verbatim request body string used for HMAC-SHA256 signature verification.
4959
+ Contains same PII as payload. Retention/TTL policy TBD (#42 ask 5).
4960
+ description: Verbatim raw request body — used for HMAC-SHA256 verification.
4961
+ Contains PII.
4962
+ headers:
4963
+ x-note: Request headers including provider signature (e.g. "jeko-signature")
4964
+ and tracing headers. Sensitive — do not expose to clients.
4965
+ description: Incoming HTTP headers. Includes provider signature header and
4966
+ Sentry trace. Sensitive.
4967
+ type:
4968
+ - object
4969
+ - 'null'
4970
+ propertyNames:
4971
+ type: string
4972
+ additionalProperties:
4973
+ type: string
4974
+ receivedAt:
4975
+ $ref: '#/components/schemas/FirestoreTimestamp'
4976
+ description: When the webhook delivery was received by the backend.
4977
+ required:
4978
+ - company
4979
+ - provider
4980
+ - eventType
4981
+ - status
4982
+ - verified
4983
+ - endpointStatus
4984
+ - processed
4985
+ - reference
4986
+ - amount
4987
+ - currency
4988
+ - payload
4989
+ - rawBody
4990
+ - receivedAt
4991
+ additionalProperties: false
4992
+ x-internal: true
4993
+ description: 'PaymentWebhookDelivery (GH#42). Collection: payment_webhooks/{deliveryId}
4994
+ (top-level). One document per received provider callback. Contains raw payload
4995
+ + verification outcome. x-internal: written/read only by backend; dashboard
4996
+ reads via admin UI. PII in payload/rawBody — retention policy TBD.'
4997
+ PaymentWebhookDeliveryCreate:
4998
+ allOf:
4999
+ - $ref: '#/components/schemas/PaymentWebhookDelivery'
5000
+ description: Write payload for creating a new PaymentWebhookDelivery document.
5001
+ Fields marked `readOnly` are server-set and must not be included. Fields marked
5002
+ `x-immutable` may be set once at creation.
5003
+ required:
5004
+ - company
5005
+ - provider
5006
+ - eventType
5007
+ - status
5008
+ - verified
5009
+ - endpointStatus
5010
+ - processed
5011
+ - reference
5012
+ - amount
5013
+ - currency
5014
+ - payload
5015
+ - rawBody
5016
+ - receivedAt
5017
+ PaymentWebhookDeliveryUpdate:
5018
+ allOf:
5019
+ - $ref: '#/components/schemas/PaymentWebhookDelivery'
5020
+ description: Write payload for partial update (PATCH) of a PaymentWebhookDelivery
5021
+ document. All fields optional. Fields marked `readOnly` or `x-immutable` must
5022
+ not be sent.
5023
+ PaymentWebhookEndpoint:
5024
+ type: object
5025
+ properties:
5026
+ token:
5027
+ type: string
5028
+ x-immutable: true
5029
+ description: (Immutable) base64url-encoded 24-byte random token. Also the
5030
+ Firestore document ID and the URL path segment.
5031
+ company:
5032
+ type: string
5033
+ x-immutable: true
5034
+ description: (Immutable) Company slug (e.g. gerko_studios). Identifies the
5035
+ tenant that owns this endpoint.
5036
+ provider:
5037
+ type: string
5038
+ enum:
5039
+ - wave
5040
+ - jeko
5041
+ x-immutable: true
5042
+ description: (Immutable) Payment provider whose webhooks this endpoint accepts.
5043
+ secret:
5044
+ type: string
5045
+ x-note: Provider-specific webhook signing secret. Used to verify HMAC-SHA256
5046
+ signatures on inbound payloads. Never expose to clients.
5047
+ readOnly: true
5048
+ description: Webhook signing secret for HMAC verification. Backend-only
5049
+ — never returned to dashboard or mobile clients.
5050
+ active:
5051
+ type: boolean
5052
+ description: Whether this endpoint is currently active. Inactive endpoints
5053
+ reject incoming webhooks.
5054
+ createdAt:
5055
+ anyOf:
5056
+ - $ref: '#/components/schemas/FirestoreTimestamp'
5057
+ - type: 'null'
5058
+ readOnly: true
5059
+ description: (Read-only) When this endpoint was provisioned.
5060
+ required:
5061
+ - token
5062
+ - company
5063
+ - provider
5064
+ - secret
5065
+ - active
5066
+ additionalProperties: false
5067
+ x-internal: true
5068
+ description: 'PaymentWebhookEndpoint (GH#41). Collection: payment_webhook_endpoints/{token}.
5069
+ Top-level, not tenant-scoped. Provisioned by superadmin; used by the backend
5070
+ to verify inbound payment webhooks from Wave and Jeko. The delivery log collection
5071
+ (raw webhook payloads + verification outcomes) is tracked in GH#41 — shape
5072
+ TBD.'
5073
+ PaymentWebhookEndpointCreate:
5074
+ allOf:
5075
+ - $ref: '#/components/schemas/PaymentWebhookEndpoint'
5076
+ description: Write payload for creating a new PaymentWebhookEndpoint document.
5077
+ Fields marked `readOnly` are server-set and must not be included. Fields marked
5078
+ `x-immutable` may be set once at creation.
5079
+ required:
5080
+ - token
5081
+ - company
5082
+ - provider
5083
+ - active
5084
+ PaymentWebhookEndpointUpdate:
5085
+ allOf:
5086
+ - $ref: '#/components/schemas/PaymentWebhookEndpoint'
5087
+ description: Write payload for partial update (PATCH) of a PaymentWebhookEndpoint
5088
+ document. All fields optional. Fields marked `readOnly` or `x-immutable` must
5089
+ not be sent.
4318
5090
  Sale:
4319
5091
  type: object
4320
5092
  properties:
@@ -5213,6 +5985,12 @@ components:
5213
5985
  received the message.
5214
5986
  waba:
5215
5987
  $ref: '#/components/schemas/WabaLabel'
5988
+ x-note: 'cmz = "IngenX - Chez Miss Zahoui", phone number ID 446424085225188,
5989
+ used for chez_miss_zahoui_* companies. val = "IngenX - Valets", phone
5990
+ number ID 425582173979125, used for all other companies. Templates are
5991
+ approved at WABA level — if cmz and val share a WABA ID, all templates
5992
+ are available to both. Routing logic: functions/src/orders/order.ts#isSender2Company.
5993
+ See messaging/whatsapp-platform-constraints (#50).'
5216
5994
  description: (Immutable) Human-readable WABA label (cmz | val). See WabaLabel
5217
5995
  enum.
5218
5996
  x-immutable: true
@@ -5415,10 +6193,26 @@ components:
5415
6193
  to:
5416
6194
  type: string
5417
6195
  x-immutable: true
5418
- description: (Immutable) Recipient phone number in E.164 format. Must match
5419
- the inbound `from` for the same conversation.
6196
+ x-note: 'This is the Meta wa_id (digits only, no leading +), NOT a literal
6197
+ E.164 string. Meta normalizes phone numbers in non-obvious ways (e.g.
6198
+ +2250777471485 → wa_id 22577471485, dropping the trunk zero). The only
6199
+ reliable source is Meta''s contacts[].wa_id from the send response — do
6200
+ not derive from E.164 by stripping + alone. The dashboard conversational
6201
+ sends and the whatsapp-server OTP path both use the wa_id form. Confirmed
6202
+ in prod: ING-368.'
6203
+ x-when: Set at enqueue time to the recipient's Meta wa_id. Use inbound WhatsappInboundMessage.from
6204
+ (also wa_id) as the join key for the conversation thread.
6205
+ description: (Immutable) Recipient Meta wa_id — digits only, no leading
6206
+ + (e.g. 22577471485). Equal to the inbound `from` for the same conversation.
6207
+ NOT literal E.164; see x-note (#54).
5420
6208
  waba:
5421
6209
  $ref: '#/components/schemas/WabaLabel'
6210
+ x-note: 'cmz = "IngenX - Chez Miss Zahoui", phone number ID 446424085225188,
6211
+ used for chez_miss_zahoui_* companies. val = "IngenX - Valets", phone
6212
+ number ID 425582173979125, used for all other companies. Templates are
6213
+ approved at WABA level — if cmz and val share a WABA ID, all templates
6214
+ are available to both. Routing logic: functions/src/orders/order.ts#isSender2Company.
6215
+ See messaging/whatsapp-platform-constraints (#50).'
5422
6216
  description: (Immutable) WABA that will send the message. Mirrors inbound
5423
6217
  `waba`.
5424
6218
  x-immutable: true
@@ -5504,6 +6298,9 @@ components:
5504
6298
  after (sent → delivered → read, or failed).
5505
6299
  wamid:
5506
6300
  readOnly: true
6301
+ x-note: Useful for audit and dedup. Does NOT enable reply-threading for
6302
+ template messages — Meta silently ignores context.message_id on templates.
6303
+ Threading only works for kind=text within the 24-hour service window (#50).
5507
6304
  description: (Read-only) WhatsApp message ID returned by Meta once the backend
5508
6305
  sends. Absent while queued or on failure.
5509
6306
  type:
@@ -5583,6 +6380,12 @@ components:
5583
6380
  - 'null'
5584
6381
  waba:
5585
6382
  $ref: '#/components/schemas/WabaLabel'
6383
+ x-note: 'cmz = "IngenX - Chez Miss Zahoui", phone number ID 446424085225188,
6384
+ used for chez_miss_zahoui_* companies. val = "IngenX - Valets", phone
6385
+ number ID 425582173979125, used for all other companies. Templates are
6386
+ approved at WABA level — if cmz and val share a WABA ID, all templates
6387
+ are available to both. Routing logic: functions/src/orders/order.ts#isSender2Company.
6388
+ See messaging/whatsapp-platform-constraints (#50).'
5586
6389
  description: (Read-only) WABA label this template is approved on. Templates
5587
6390
  are WABA-scoped. Mirrors WhatsappOutboundMessage.waba.
5588
6391
  readOnly: true