@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
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: "WhatsappOutboundLifecycleEvent"
3
3
  sidebar_label: "WhatsappOutboundLifecycleEvent"
4
- sidebar_position: 33
4
+ sidebar_position: 39
5
5
  ---
6
6
 
7
7
  # WhatsappOutboundLifecycleEvent
@@ -303,7 +303,7 @@ Do not include in write requests. This field is set exclusively by the server (F
303
303
  **Description:** (Read-only) Raw Meta error object(s).
304
304
 
305
305
  ----------------------------------------------------------------------------------------------------------------------------
306
- Generated using [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans) on 2026-05-30 at 12:42:51 +0000
306
+ Generated using [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans) on 2026-05-30 at 13:57:29 +0000
307
307
 
308
308
  :::warning Server-set
309
309
  Do not include in write requests. This field is set exclusively by the server (Firestore trigger or Admin SDK). Clients that send it will have the value silently ignored or may receive a validation error.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: "WhatsappOutboundMessage"
3
3
  sidebar_label: "WhatsappOutboundMessage"
4
- sidebar_position: 34
4
+ sidebar_position: 40
5
5
  ---
6
6
 
7
7
  # WhatsappOutboundMessage
@@ -83,26 +83,26 @@ sidebar_position: 34
83
83
 
84
84
  **Description:** WhatsappOutboundMessage — Firestore-as-the-bus outbox doc (GH#43). Collection: whatsapp_outbound_messages/\{docId\}. Top-level scope; mirrors WhatsappInboundMessage. Dashboard enqueues `queued`; backend sends and owns the lifecycle.
85
85
 
86
- | Property | Pattern | Type | Deprecated | Definition | Title/Description |
87
- | ------------------------------ | ------- | ---------------- | ---------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
88
- | - [id](#id ) | No | string or null | No | - | (Read-only) Firestore document ID — auto-generated at enqueue time. |
89
- | + [to](#to ) | No | string | No | - | (Immutable) Recipient phone number in E.164 format. Must match the inbound \`from\` for the same conversation. |
90
- | + [waba](#waba ) | No | enum (of string) | No | In #/definitions/waba-label | (Immutable) WABA that will send the message. Mirrors inbound \`waba\`. |
91
- | + [wabaId](#wabaId ) | No | string | No | - | (Immutable) Meta WABA ID. Mirrors inbound \`wabaId\`. |
92
- | + [kind](#kind ) | No | enum (of string) | No | In #/definitions/outbound-message-format | (Immutable) Message format: \`text\` or \`template\`. Set \`text\` xor \`template\` to match. |
93
- | - [text](#text ) | No | string or null | No | - | Free-form message body. Present only when kind=text. |
94
- | - [template](#template ) | No | object or null | No | - | Template send payload. Present only when kind=template. |
95
- | - [purpose](#purpose ) | No | Combination | No | - | Business purpose (otp \| review_request \| adhoc \| conversational). Optional. |
96
- | - [orderUuid](#orderUuid ) | No | string or null | No | - | Order reference. Present on review_request messages. |
97
- | + [sentBy](#sentBy ) | No | object | No | - | (Immutable) Dashboard actor who enqueued the message. |
98
- | + [createdAt](#createdAt ) | No | object | No | In #/definitions/firestore-timestamp | (Immutable) Enqueue timestamp, set by the dashboard at create. |
99
- | + [status](#status ) | No | enum (of string) | No | In #/definitions/outbound-message-status | Delivery status. Created as \`queued\` by the dashboard; updated in place by the backend. |
100
- | - [wamid](#wamid ) | No | string or null | No | - | (Read-only) WhatsApp message ID returned by Meta once the backend sends. Absent while queued or on failure. |
101
- | - [error](#error ) | No | string or null | No | - | (Read-only) Human-readable failure reason. Present on status=failed. Raw Meta error detail lives in the lifecycle_events subcollection. |
102
- | - [sentAt](#sentAt ) | No | Combination | No | - | (Read-only) When the backend sent the message to Meta. |
103
- | - [deliveredAt](#deliveredAt ) | No | Combination | No | - | (Read-only) When Meta reported delivery. |
104
- | - [readAt](#readAt ) | No | Combination | No | - | (Read-only) When Meta reported the recipient read the message. |
105
- | - [failedAt](#failedAt ) | No | Combination | No | - | (Read-only) When the send failed. |
86
+ | Property | Pattern | Type | Deprecated | Definition | Title/Description |
87
+ | ------------------------------ | ------- | ---------------- | ---------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
88
+ | - [id](#id ) | No | string or null | No | - | (Read-only) Firestore document ID — auto-generated at enqueue time. |
89
+ | + [to](#to ) | No | string | No | - | (Immutable) Recipient Meta wa_id digits only, no leading + (e.g. 22577471485). Equal to the inbound \`from\` for the same conversation. NOT literal E.164; see x-note (#54). |
90
+ | + [waba](#waba ) | No | enum (of string) | No | In #/definitions/waba-label | (Immutable) WABA that will send the message. Mirrors inbound \`waba\`. |
91
+ | + [wabaId](#wabaId ) | No | string | No | - | (Immutable) Meta WABA ID. Mirrors inbound \`wabaId\`. |
92
+ | + [kind](#kind ) | No | enum (of string) | No | In #/definitions/outbound-message-format | (Immutable) Message format: \`text\` or \`template\`. Set \`text\` xor \`template\` to match. |
93
+ | - [text](#text ) | No | string or null | No | - | Free-form message body. Present only when kind=text. |
94
+ | - [template](#template ) | No | object or null | No | - | Template send payload. Present only when kind=template. |
95
+ | - [purpose](#purpose ) | No | Combination | No | - | Business purpose (otp \| review_request \| adhoc \| conversational). Optional. |
96
+ | - [orderUuid](#orderUuid ) | No | string or null | No | - | Order reference. Present on review_request messages. |
97
+ | + [sentBy](#sentBy ) | No | object | No | - | (Immutable) Dashboard actor who enqueued the message. |
98
+ | + [createdAt](#createdAt ) | No | object | No | In #/definitions/firestore-timestamp | (Immutable) Enqueue timestamp, set by the dashboard at create. |
99
+ | + [status](#status ) | No | enum (of string) | No | In #/definitions/outbound-message-status | Delivery status. Created as \`queued\` by the dashboard; updated in place by the backend. |
100
+ | - [wamid](#wamid ) | No | string or null | No | - | (Read-only) WhatsApp message ID returned by Meta once the backend sends. Absent while queued or on failure. |
101
+ | - [error](#error ) | No | string or null | No | - | (Read-only) Human-readable failure reason. Present on status=failed. Raw Meta error detail lives in the lifecycle_events subcollection. |
102
+ | - [sentAt](#sentAt ) | No | Combination | No | - | (Read-only) When the backend sent the message to Meta. |
103
+ | - [deliveredAt](#deliveredAt ) | No | Combination | No | - | (Read-only) When Meta reported delivery. |
104
+ | - [readAt](#readAt ) | No | Combination | No | - | (Read-only) When Meta reported the recipient read the message. |
105
+ | - [failedAt](#failedAt ) | No | Combination | No | - | (Read-only) When the send failed. |
106
106
 
107
107
  ## <a name="id"></a>1. Property `id`
108
108
 
@@ -124,12 +124,20 @@ Do not include in write requests. This field is set exclusively by the server (F
124
124
  | **Type** | `string` |
125
125
  | **Required** | Yes |
126
126
 
127
- **Description:** (Immutable) Recipient phone number in E.164 format. Must match the inbound `from` for the same conversation.
127
+ **Description:** (Immutable) Recipient Meta wa_id digits only, no leading + (e.g. 22577471485). Equal to the inbound `from` for the same conversation. NOT literal E.164; see x-note (#54).
128
128
 
129
129
  :::info Immutable
130
130
  Set at creation only. This field cannot be modified after the document is created. Include it in CREATE payloads; omit it (or leave unchanged) in UPDATE payloads.
131
131
  :::
132
132
 
133
+ :::note
134
+ 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 → wa_id 22577471485, dropping the trunk zero). The only reliable source is Meta's contacts[].wa_id from the send response — 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.
135
+ :::
136
+
137
+ :::tip When to set
138
+ 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.
139
+ :::
140
+
133
141
  ## <a name="waba"></a>3. Property `waba`
134
142
 
135
143
  | | |
@@ -148,6 +156,10 @@ Must be one of:
148
156
  Set at creation only. This field cannot be modified after the document is created. Include it in CREATE payloads; omit it (or leave unchanged) in UPDATE payloads.
149
157
  :::
150
158
 
159
+ :::note
160
+ 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 — 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).
161
+ :::
162
+
151
163
  ## <a name="wabaId"></a>4. Property `wabaId`
152
164
 
153
165
  | | |
@@ -424,6 +436,10 @@ Dashboard must create with `queued`. The backend owns every transition after (se
424
436
  Do not include in write requests. This field is set exclusively by the server (Firestore trigger or Admin SDK). Clients that send it will have the value silently ignored or may receive a validation error.
425
437
  :::
426
438
 
439
+ :::note
440
+ Useful for audit and dedup. Does NOT enable reply-threading for template messages — Meta silently ignores context.message_id on templates. Threading only works for kind=text within the 24-hour service window (#50).
441
+ :::
442
+
427
443
  ## <a name="error"></a>14. Property `error`
428
444
 
429
445
  | | |
@@ -582,7 +598,7 @@ Do not include in write requests. This field is set exclusively by the server (F
582
598
  | **Required** | No |
583
599
 
584
600
  ----------------------------------------------------------------------------------------------------------------------------
585
- Generated using [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans) on 2026-05-30 at 12:42:51 +0000
601
+ Generated using [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans) on 2026-05-30 at 13:57:29 +0000
586
602
 
587
603
  :::warning Server-set
588
604
  Do not include in write requests. This field is set exclusively by the server (Firestore trigger or Admin SDK). Clients that send it will have the value silently ignored or may receive a validation error.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: "WhatsappTemplate"
3
3
  sidebar_label: "WhatsappTemplate"
4
- sidebar_position: 35
4
+ sidebar_position: 41
5
5
  ---
6
6
 
7
7
  # WhatsappTemplate
@@ -99,6 +99,10 @@ Must be one of:
99
99
  Do not include in write requests. This field is set exclusively by the server (Firestore trigger or Admin SDK). Clients that send it will have the value silently ignored or may receive a validation error.
100
100
  :::
101
101
 
102
+ :::note
103
+ 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 — 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).
104
+ :::
105
+
102
106
  ## <a name="wabaId"></a>3. Property `wabaId`
103
107
 
104
108
  | | |
@@ -287,7 +291,7 @@ Do not include in write requests. This field is set exclusively by the server (F
287
291
  **Description:** (Read-only) Rendered preview text for display in the dashboard picker.
288
292
 
289
293
  ----------------------------------------------------------------------------------------------------------------------------
290
- Generated using [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans) on 2026-05-30 at 12:42:51 +0000
294
+ Generated using [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans) on 2026-05-30 at 13:57:29 +0000
291
295
 
292
296
  :::warning Server-set
293
297
  Do not include in write requests. This field is set exclusively by the server (Firestore trigger or Admin SDK). Clients that send it will have the value silently ignored or may receive a validation error.
@@ -22,6 +22,10 @@ Values: ACTIVE, STALE, ON_HOLD, ESCALATED
22
22
  Booking lifecycle status. COMPLETED_MIXED = some sessions completed, others cancelled/no-show.
23
23
  Values: PENDING, CONFIRMED, COMPLETED, CANCELLED, CANCELLATION_REQUESTED, COMPLETED_MIXED
24
24
 
25
+ ### ContractStatus
26
+ 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.
27
+ Values: DRAFT, ACTIVE, COMPLETED, TERMINATED
28
+
25
29
  ### CustomerPaymentStatus
26
30
  Customer payment lifecycle status (D22). Tracks allocation progress of received payments.
27
31
  Values: PENDING, CONFIRMED, PARTIALLY_APPLIED, FULLY_APPLIED, REFUNDED, CANCELLED
@@ -42,6 +46,10 @@ Values: web, mobile, pwa, app-store, play-store, other
42
46
  Ticketed event lifecycle (D32). Mobile-only today; Dashboard in Wave 4.
43
47
  Values: DRAFT, ACTIVE, CANCELLED, COMPLETED
44
48
 
49
+ ### ExpensePaymentStatus
50
+ Payment status of an Expense. Stored enum — OVERDUE is not stored, it is derived at read time from dueDate (#13).
51
+ Values: PENDING, PARTIALLY_PAID, PAID, FAILED
52
+
45
53
  ### FulfillmentStatus
46
54
  Delivery/fulfillment lifecycle (D34). Optional — null for in-person orders.
47
55
  Values: PREPARING, PARTIALLY_SHIPPED, SHIPPED, IN_TRANSIT, DELIVERED, PICKED_UP
@@ -50,6 +58,10 @@ Values: PREPARING, PARTIALLY_SHIPPED, SHIPPED, IN_TRANSIT, DELIVERED, PICKED_UP
50
58
  Loyalty point transaction type (D07). SCREAMING_SNAKE past tense.
51
59
  Values: EARNED, REDEEMED, ADJUSTED, EXPIRED, BONUS, REFUND
52
60
 
61
+ ### MilestoneStatus
62
+ Status of a ContractMilestone (#17). PENDING = created, not yet invoiced. INVOICED = invoice issued, awaiting payment. PAID = payment received.
63
+ Values: PENDING, INVOICED, PAID
64
+
53
65
  ### NotificationChannel
54
66
  Delivery channel of a NotificationRecord (GH#48).
55
67
  Values: email, whatsapp, sms, push
@@ -79,8 +91,8 @@ Outbound WhatsApp message delivery status. Lifecycle: queued → sent → delive
79
91
  Values: queued, sent, delivered, read, failed
80
92
 
81
93
  ### PaymentMethod
82
- Unified payment method set with African + global methods (D02).
83
- Values: CASH, CREDIT_CARD, ORANGE_MONEY, WAVE, MTN_MONEY, MOOV_MONEY, BANK_TRANSFER, PAYPAL, STRIPE, OTHER
94
+ Unified payment method set with African + global methods (D02). Note: the metrics writer historically emits "OM" instead of "ORANGE_MONEY" as a paymentsByMethod map key — treat OM as deprecated; canonical value is ORANGE_MONEY (#21).
95
+ Values: CASH, CREDIT_CARD, ORANGE_MONEY, WAVE, MTN_MONEY, MOOV_MONEY, BANK_TRANSFER, PAYPAL, STRIPE, OTHER, OM
84
96
 
85
97
  ### PaymentProofStatus
86
98
  Payment proof review status. Used by Order and Booking payment proof workflows.
@@ -123,7 +135,7 @@ Event ticket status (D32). VALID = active and unused.
123
135
  Values: VALID, USED, CANCELLED
124
136
 
125
137
  ### WabaLabel
126
- Human-readable WABA label identifying which Meta business number received the message (GH#36).
138
+ 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).
127
139
  Values: cmz, val
128
140
 
129
141
  ### WhatsappButtonSubType
@@ -361,7 +373,7 @@ Example:
361
373
  ```
362
374
 
363
375
  ### App
364
- Fields: 14 (6 required)
376
+ Fields: 14 (5 required)
365
377
 
366
378
  | Field | Type | Required | Description |
367
379
  |-------|------|----------|-------------|
@@ -369,7 +381,7 @@ Fields: 14 (6 required)
369
381
  | companyId | string | yes | (Immutable) FK → Company document ID. Scopes all app sub-collections. |
370
382
  | name | string | yes | Human-readable app name shown in dashboards. |
371
383
  | description | ['string', 'null'] | no | Optional freeform description. |
372
- | status | AppStatus | yes | Lifecycle status (D41/D44). Clients filter by ACTIVE. |
384
+ | status | any | no | Lifecycle status (D41/D44). Optional pending backfill of pre-existing records (#26) — treat absent as ACTIVE. Clients filter by ACTIVE. |
373
385
  | deploymentLinks | array<object> | yes | Ordered list of deployment URLs (web, mobile, PWA, store links). |
374
386
  | deploymentLinks[].label | string | yes | Human-readable label shown in dashboards (e.g. "Production", "Staging"). |
375
387
  | deploymentLinks[].url | string | yes | Absolute URL or store link. |
@@ -642,6 +654,48 @@ Example:
642
654
  }
643
655
  ```
644
656
 
657
+ ### Contract
658
+ Fields: 15 (8 required)
659
+
660
+ | Field | Type | Required | Description |
661
+ |-------|------|----------|-------------|
662
+ | id | ['string', 'null'] | no | (Read-only) Firestore document ID, auto-generated. |
663
+ | companyId | string | yes | (Immutable) FK → Company document ID. |
664
+ | payeeId | string | yes | (Immutable) FK → Payee document ID. Full Payee model tracked in #20. |
665
+ | title | string | yes | Contract title or reference name shown in dashboards. |
666
+ | description | ['string', 'null'] | no | Optional freeform description of the contract scope. |
667
+ | status | ContractStatus | yes | Contract lifecycle status (#14). Query by ACTIVE — do not use an isActive boolean. |
668
+ | currency | string | yes | Currency code. Locked to XOF (#18). Multi-currency support requires a deliberate schema version bump. |
669
+ | totalAmount | number | yes | Total contract value (XOF). |
670
+ | startDate | string | yes | Contract start date (ISO 8601 YYYY-MM-DD). |
671
+ | endDate | ['string', 'null'] | no | Contract end date (ISO 8601 YYYY-MM-DD). Absent for open-ended contracts. |
672
+ | milestones | ['array', 'null'] | no | Ordered list of payment milestones. Embedded in the contract document (#17). |
673
+ | notes | ['string', 'null'] | no | Optional internal notes. |
674
+ | createdBy | string | yes | (Immutable) FK → User/staff UID who created the contract. |
675
+ | createdAt | any | no | (Read-only) Server-generated creation timestamp. |
676
+ | updatedAt | any | no | (Read-only) Server-generated update timestamp. |
677
+
678
+ Example:
679
+ ```json
680
+ {
681
+ "id": null,
682
+ "companyId": "comp_xyz789",
683
+ "payeeId": "pay_ref123",
684
+ "title": "title",
685
+ "description": null,
686
+ "status": "status",
687
+ "currency": "XOF",
688
+ "totalAmount": 45000,
689
+ "startDate": "2026-02-15",
690
+ "endDate": null,
691
+ "milestones": null,
692
+ "notes": null,
693
+ "createdBy": "staff_k0f1",
694
+ "createdAt": "createdAt",
695
+ "updatedAt": "updatedAt"
696
+ }
697
+ ```
698
+
645
699
  ### Customer
646
700
  Fields: 17 (4 required)
647
701
 
@@ -700,7 +754,7 @@ Fields: 17 (12 required)
700
754
  | amount | number | yes | |
701
755
  | currency | string | yes | Currency code. Locked to XOF (West African CFA franc) for now. |
702
756
  | paymentDate | FirestoreTimestamp | yes | Firestore Timestamp — Admin SDK form: { _seconds, _nanoseconds }. See types/firestore.ts for REST API v1 and client SDK serialization notes (#10). |
703
- | paymentMethod | PaymentMethod | yes | Unified payment method set with African + global methods (D02). |
757
+ | paymentMethod | PaymentMethod | yes | Unified payment method set with African + global methods (D02). Note: the metrics writer historically emits "OM" instead of "ORANGE_MONEY" as a paymentsByMethod map key — treat OM as deprecated; canonical value is ORANGE_MONEY (#21). |
704
758
  | referenceNumber | string | yes | Unique payment reference (receipt number, transaction ID, etc.). |
705
759
  | allocatedAmount | number | yes | (Read-only) Total amount allocated to bookings/orders/purchases via allocations. Server-calculated. |
706
760
  | unappliedAmount | number | yes | (Read-only) Remaining unallocated amount (amount - allocatedAmount). Server-calculated. |
@@ -816,6 +870,52 @@ Example:
816
870
  }
817
871
  ```
818
872
 
873
+ ### Expense
874
+ Fields: 17 (6 required)
875
+
876
+ | Field | Type | Required | Description |
877
+ |-------|------|----------|-------------|
878
+ | id | ['string', 'null'] | no | (Read-only) Firestore document ID, auto-generated. |
879
+ | companyId | string | yes | (Immutable) FK → Company document ID. |
880
+ | title | string | yes | Human-readable expense title (e.g. "Loyer mars 2026", "Facture EAU"). |
881
+ | description | ['string', 'null'] | no | Optional longer description or notes. |
882
+ | amount | number | yes | Total expense amount (XOF). |
883
+ | currency | string | yes | Currency code. Locked to XOF. |
884
+ | dueDate | any | no | When the expense is due. Used to compute OVERDUE state at read time (not stored in paymentStatus). |
885
+ | paymentStatus | ExpensePaymentStatus | yes | Current payment status. OVERDUE is never stored — derive from dueDate at read time (#13). |
886
+ | amountDue | ['number', 'null'] | no | Amount still owed. Authoritative when no OutboundPaymentAllocation records exist for this expense. |
887
+ | amountPaid | ['number', 'null'] | no | Amount paid to date (simple tracking, no allocations). |
888
+ | allocatedAmount | ['number', 'null'] | no | (Read-only) Total amount allocated via OutboundPaymentAllocation records. Authoritative when allocations exist. |
889
+ | balance | ['number', 'null'] | no | (Read-only) Remaining balance: amount minus allocatedAmount. Authoritative when OutboundPaymentAllocation records exist. |
890
+ | paidDate | any | no | When the expense was fully settled (paymentStatus = PAID). Null otherwise. |
891
+ | paymentReference | ['string', 'null'] | no | Most recent payment transaction reference. Overwritten on each payment; not a full history. |
892
+ | createdBy | string | yes | (Immutable) FK → User/staff UID who created this expense record. |
893
+ | createdAt | any | no | (Read-only) Server-generated creation timestamp. |
894
+ | updatedAt | any | no | (Read-only) Server-generated last-update timestamp. |
895
+
896
+ Example:
897
+ ```json
898
+ {
899
+ "id": null,
900
+ "companyId": "comp_xyz789",
901
+ "title": "title",
902
+ "description": null,
903
+ "amount": 45000,
904
+ "currency": "XOF",
905
+ "dueDate": "dueDate",
906
+ "paymentStatus": "paymentStatus",
907
+ "amountDue": null,
908
+ "amountPaid": null,
909
+ "allocatedAmount": null,
910
+ "balance": null,
911
+ "paidDate": "pai_ref123",
912
+ "paymentReference": null,
913
+ "createdBy": "staff_k0f1",
914
+ "createdAt": "createdAt",
915
+ "updatedAt": "updatedAt"
916
+ }
917
+ ```
918
+
819
919
  ### LoyaltyConfig
820
920
  Fields: 11 (2 required)
821
921
 
@@ -1104,7 +1204,7 @@ Fields: 31 (26 required)
1104
1204
  | staleOrdersCount | ['integer', 'null'] | no | (Read-only, Optional) Orders that have been in an incomplete status beyond the stale threshold. |
1105
1205
  | staleBookingsCount | ['integer', 'null'] | no | (Read-only, Optional) Bookings that have been in an incomplete status beyond the stale threshold. |
1106
1206
  | onHoldOrdersCount | ['integer', 'null'] | no | (Read-only, Optional) Orders currently in an on-hold state. |
1107
- | paymentsByMethod | ['object', 'null'] | no | (Read-only, Optional) Map of PaymentMethod total amount for the current day. Empty map {} until first payment. |
1207
+ | paymentsByMethod | ['object', 'null'] | no | (Read-only, Optional) Payment breakdown by method for the day. Keys are PaymentMethod values (or legacy short code OM). Value is { total: XOF amount, count: number of payments }. |
1108
1208
  | computedForDay | string | yes | (Read-only) YYYY-MM-DD string indicating which day these metrics reflect. Used to detect day boundaries and trigger midnight resets. |
1109
1209
  | generatedAt | FirestoreTimestamp | yes | (Read-only) Server timestamp of the last metrics write. |
1110
1210
  | date | string | yes | (Read-only) YYYY-MM-DD document ID repeated as a field. Identifies the day this snapshot covers. |
@@ -1281,7 +1381,7 @@ Example:
1281
1381
  ```
1282
1382
 
1283
1383
  ### Order
1284
- Fields: 48 (8 required)
1384
+ Fields: 49 (8 required)
1285
1385
 
1286
1386
  | Field | Type | Required | Description |
1287
1387
  |-------|------|----------|-------------|
@@ -1290,12 +1390,13 @@ Fields: 48 (8 required)
1290
1390
  | companyId | string | yes | (Immutable) FK → Company document ID. Scopes all queries. |
1291
1391
  | appId | ['string', 'null'] | no | (Immutable, Optional) FK → App document ID (D43 / ADR-003, renamed from siteId per D44). null = company-wide. |
1292
1392
  | orderNumber | string | yes | (Read-only) Server-generated order number. |
1393
+ | followUpUrl | ['string', 'null'] | no | Optional URL for receipt QR code / post-order follow-up. Canonical casing: followUpUrl (#6). |
1293
1394
  | status | OrderStatus | yes | Core lifecycle status (D34, MIG-11). See OrderStatus enum for the legacy value migration mapping. |
1294
1395
  | paymentStatus | any | no | Payment lifecycle (D34). Null until payment is initiated. |
1295
1396
  | fulfillmentStatus | any | no | Delivery/fulfillment lifecycle (D34). |
1296
1397
  | returnStatus | any | no | Return/exchange lifecycle (D34). Null until a return or exchange is initiated. |
1297
1398
  | deliveryType | any | no | Fulfillment channel for this order (ON_SITE, PICK_UP, DELIVERY). |
1298
- | paymentMethod | any | no | Unified payment method set with African + global methods (D02). |
1399
+ | paymentMethod | any | no | Unified payment method set with African + global methods (D02). Note: the metrics writer historically emits "OM" instead of "ORANGE_MONEY" as a paymentsByMethod map key — treat OM as deprecated; canonical value is ORANGE_MONEY (#21). |
1299
1400
  | invoiceId | ['string', 'null'] | no | FK → Invoice document ID. |
1300
1401
  | customerId | ['string', 'null'] | no | FK → Customer.id (Firestore doc ID). Used to resolve customer details. |
1301
1402
  | customerName | ['string', 'null'] | no | (Denormalized) From Customer.name at write time. |
@@ -1342,6 +1443,7 @@ Example:
1342
1443
  "companyId": "comp_xyz789",
1343
1444
  "appId": null,
1344
1445
  "orderNumber": "ORD-2026-0042",
1446
+ "followUpUrl": null,
1345
1447
  "status": "status",
1346
1448
  "paymentStatus": "paymentStatus",
1347
1449
  "fulfillmentStatus": "fulfillmentStatus",
@@ -1424,6 +1526,136 @@ Example:
1424
1526
  }
1425
1527
  ```
1426
1528
 
1529
+ ### OutboundPayment
1530
+ Fields: 13 (7 required)
1531
+
1532
+ | Field | Type | Required | Description |
1533
+ |-------|------|----------|-------------|
1534
+ | id | ['string', 'null'] | no | (Read-only) Firestore document ID, auto-generated. |
1535
+ | companyId | string | yes | (Immutable) FK → Company document ID. |
1536
+ | payeeId | string | yes | (Immutable) FK → Payee document ID. Full Payee model tracked in #20. |
1537
+ | contractId | ['string', 'null'] | no | (Immutable, Optional) FK → Contract document ID. Absent for ad-hoc payments outside a formal contract. |
1538
+ | amount | number | yes | Amount paid (XOF). |
1539
+ | currency | string | yes | Currency code. Locked to XOF — consistent with Contract and CustomerPayment. |
1540
+ | method | PaymentMethod | yes | Payment method used (e.g. BANK_TRANSFER, MOBILE_MONEY, CHECK). |
1541
+ | reference | ['string', 'null'] | no | Optional payment reference — bank transaction number, check number, Wave ref, etc. |
1542
+ | paidAt | FirestoreTimestamp | yes | When the payment was made. |
1543
+ | notes | ['string', 'null'] | no | Optional internal notes. |
1544
+ | createdBy | string | yes | (Immutable) FK → User/staff UID who recorded the payment. |
1545
+ | createdAt | any | no | (Read-only) Server-generated creation timestamp. |
1546
+ | updatedAt | any | no | (Read-only) Server-generated update timestamp. |
1547
+
1548
+ Example:
1549
+ ```json
1550
+ {
1551
+ "id": null,
1552
+ "companyId": "comp_xyz789",
1553
+ "payeeId": "pay_ref123",
1554
+ "contractId": null,
1555
+ "amount": 45000,
1556
+ "currency": "XOF",
1557
+ "method": "method",
1558
+ "reference": null,
1559
+ "paidAt": "pai_ref123",
1560
+ "notes": null,
1561
+ "createdBy": "staff_k0f1",
1562
+ "createdAt": "createdAt",
1563
+ "updatedAt": "updatedAt"
1564
+ }
1565
+ ```
1566
+
1567
+ ### OutboundPaymentAllocation
1568
+ Fields: 7 (4 required)
1569
+
1570
+ | Field | Type | Required | Description |
1571
+ |-------|------|----------|-------------|
1572
+ | id | ['string', 'null'] | no | (Read-only) Firestore document ID, auto-generated. |
1573
+ | outboundPaymentId | string | yes | (Immutable) FK → OutboundPayment document ID (parent). |
1574
+ | expenseId | string | yes | (Immutable) FK → Expense document ID. Full Expense model tracked in #20. |
1575
+ | companyId | string | yes | (Immutable) FK → Company document ID. Denormalized for collection-group queries. |
1576
+ | amount | number | yes | Amount of this payment allocated to the referenced expense (XOF). |
1577
+ | notes | ['string', 'null'] | no | Optional notes about this allocation. |
1578
+ | allocatedAt | any | no | (Read-only) When this allocation was recorded. |
1579
+
1580
+ Example:
1581
+ ```json
1582
+ {
1583
+ "id": null,
1584
+ "outboundPaymentId": "out_ref123",
1585
+ "expenseId": "exp_ref123",
1586
+ "companyId": "comp_xyz789",
1587
+ "amount": 45000,
1588
+ "notes": null,
1589
+ "allocatedAt": "allocatedAt"
1590
+ }
1591
+ ```
1592
+
1593
+ ### PaymentWebhookDelivery
1594
+ Fields: 15 (13 required)
1595
+
1596
+ | Field | Type | Required | Description |
1597
+ |-------|------|----------|-------------|
1598
+ | id | ['string', 'null'] | no | (Read-only) Firestore document ID, auto-generated. |
1599
+ | company | string | yes | (Immutable) Company document ID (e.g. "gerko_studios"). Resolved from the matched endpoint at delivery time. |
1600
+ | provider | string | yes | Payment provider that sent this webhook (e.g. "jeko", "wave"). |
1601
+ | eventType | string | yes | Provider-defined event type. Observed: "transaction.payment". Full set per provider TBD (#42). |
1602
+ | status | string | yes | Processing outcome. Observed values: "success" | "error". |
1603
+ | verified | string | yes | Signature verification result. Observed: "verified". Other values (e.g. "unverified", "error") may exist but are unconfirmed. |
1604
+ | endpointStatus | string | yes | Endpoint resolution result. Observed: "matched". Other values unconfirmed (#42). |
1605
+ | processed | boolean | yes | Whether this delivery has been reconciled to the AppPayment ledger. |
1606
+ | reference | string | yes | Provider transaction ID (= payload.id). Canonical dedup key. |
1607
+ | amount | number | yes | Payment amount, denormalized from payload.amount.amount (XOF). |
1608
+ | currency | string | yes | Currency code, denormalized from payload.amount.currency (e.g. "XOF"). |
1609
+ | payload | object | yes | Full parsed provider payload. Shape varies by provider. Contains PII (customer phone). See x-note for retention policy status. |
1610
+ | rawBody | string | yes | Verbatim raw request body — used for HMAC-SHA256 verification. Contains PII. |
1611
+ | headers | ['object', 'null'] | no | Incoming HTTP headers. Includes provider signature header and Sentry trace. Sensitive. |
1612
+ | receivedAt | FirestoreTimestamp | yes | When the webhook delivery was received by the backend. |
1613
+
1614
+ Example:
1615
+ ```json
1616
+ {
1617
+ "id": null,
1618
+ "company": "company",
1619
+ "provider": "pro_ref123",
1620
+ "eventType": "eventType",
1621
+ "status": "status",
1622
+ "verified": "verified",
1623
+ "endpointStatus": "endpointStatus",
1624
+ "processed": true,
1625
+ "reference": "reference",
1626
+ "amount": 45000,
1627
+ "currency": "XOF",
1628
+ "payload": {},
1629
+ "rawBody": "rawBody",
1630
+ "headers": null,
1631
+ "receivedAt": "receivedAt"
1632
+ }
1633
+ ```
1634
+
1635
+ ### PaymentWebhookEndpoint
1636
+ Fields: 6 (5 required)
1637
+
1638
+ | Field | Type | Required | Description |
1639
+ |-------|------|----------|-------------|
1640
+ | token | string | yes | (Immutable) base64url-encoded 24-byte random token. Also the Firestore document ID and the URL path segment. |
1641
+ | company | string | yes | (Immutable) Company slug (e.g. gerko_studios). Identifies the tenant that owns this endpoint. |
1642
+ | provider | enum(2) | yes | (Immutable) Payment provider whose webhooks this endpoint accepts. |
1643
+ | secret | string | yes | Webhook signing secret for HMAC verification. Backend-only — never returned to dashboard or mobile clients. |
1644
+ | active | boolean | yes | Whether this endpoint is currently active. Inactive endpoints reject incoming webhooks. |
1645
+ | createdAt | any | no | (Read-only) When this endpoint was provisioned. |
1646
+
1647
+ Example:
1648
+ ```json
1649
+ {
1650
+ "token": "token",
1651
+ "company": "company",
1652
+ "provider": "wave",
1653
+ "secret": "secret",
1654
+ "active": true,
1655
+ "createdAt": "createdAt"
1656
+ }
1657
+ ```
1658
+
1427
1659
  ### Sale
1428
1660
  Fields: 12 (1 required)
1429
1661
 
@@ -1792,7 +2024,7 @@ Fields: 18 (7 required)
1792
2024
  | Field | Type | Required | Description |
1793
2025
  |-------|------|----------|-------------|
1794
2026
  | id | ['string', 'null'] | no | (Read-only) Firestore document ID — auto-generated at enqueue time. |
1795
- | to | string | yes | (Immutable) Recipient phone number in E.164 format. Must match the inbound `from` for the same conversation. |
2027
+ | to | string | yes | (Immutable) Recipient Meta wa_id digits only, no leading + (e.g. 22577471485). Equal to the inbound `from` for the same conversation. NOT literal E.164; see x-note (#54). |
1796
2028
  | waba | WabaLabel | yes | (Immutable) WABA that will send the message. Mirrors inbound `waba`. |
1797
2029
  | wabaId | string | yes | (Immutable) Meta WABA ID. Mirrors inbound `wabaId`. |
1798
2030
  | kind | OutboundMessageFormat | yes | (Immutable) Message format: `text` or `template`. Set `text` xor `template` to match. |