platform-types 1.10.762 → 1.10.763

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/dist/ai.d.ts CHANGED
@@ -621,184 +621,12 @@ declare global {
621
621
  /** Email body: trimmed, non-empty, within the send path's body bound. */
622
622
  body: string;
623
623
  }
624
- interface WhatsAppOrderDraftRequest {
625
- /** Meta's message id of an INBOUND message this store received. The server re-reads its text. */
626
- messageId: string;
627
- /** `CUST` + 6–10 digits. Prices the draft at the list a cart prices this customer at (a missing, 0 or deleted list is PUBLICO 1); absent → the store default. */
628
- customerId?: string;
629
- }
630
- /**
631
- * One product a parsed item could be. Every field but `score` is read off the
632
- * store's own product row and priced by the server — never model output.
633
- */
634
- interface WhatsAppOrderDraftMatch {
635
- productId: string;
636
- sku: string;
637
- name: string;
638
- /** Unit price at the customer's list, in `currency`. Indicative: the order create path re-prices. */
639
- amount: number;
640
- currency: string;
641
- /** Token-overlap similarity of the item phrase and the product name, 0–1. Best first. */
642
- score: number;
643
- }
644
- interface WhatsAppOrderDraftItem {
645
- /** The product as the model read it from the message, at most 120 characters. Display it, never parse it. */
646
- text: string;
647
- /** A whole number of units, 1–999, or `null` when the model's reading was not one — never guessed. */
648
- quantity: number | null;
649
- /** The model's own confidence in this item, 0–1. */
650
- confidence: number;
651
- /** At most three, best first. Empty when nothing sellable matched. */
652
- matches: WhatsAppOrderDraftMatch[];
653
- /** The first match is close enough to stand as the line without a person choosing. */
654
- matched: boolean;
655
- }
656
- /**
657
- * Why a person must take the conversation over, in this order:
658
- * - `requested` — the customer asked to talk to someone.
659
- * - `not_an_order` — the message asks for no products.
660
- * - `no_items` — it reads as an order, but no product could be picked out.
661
- * - `low_confidence` — the model is below 0.7 on at least one item.
662
- * - `invalid_quantity` — at least one item's quantity is `null`.
663
- * - `no_match` — nothing sellable matched at least one item.
664
- * - `weak_match` — at least one item has matches, none close enough to stand alone.
665
- */
666
- type WhatsAppOrderDraftHandoffReason = 'requested' | 'not_an_order' | 'no_items' | 'low_confidence' | 'invalid_quantity' | 'no_match' | 'weak_match';
667
- /**
668
- * `data` payload of the success envelope. ⚠️ A DRAFT: nothing was placed,
669
- * reserved or sent. Every draft still needs a staff member to place the
670
- * order; `handoff.needed` says one must also talk to the customer.
671
- */
672
- interface WhatsAppOrderDraftResponse {
673
- messageId: string;
674
- intent: 'order' | 'human' | 'other';
675
- items: WhatsAppOrderDraftItem[];
676
- handoff: {
677
- needed: boolean;
678
- reasons: WhatsAppOrderDraftHandoffReason[];
679
- };
680
- /** More than 10 items were read; the rest were dropped. */
681
- truncated: boolean;
682
- /** The model that read the message. */
683
- model: string;
684
- /** Echoed when sent. */
685
- customerId?: string;
686
- }
687
- /**
688
- * The `error` codes this endpoint answers, beyond `GatingErrorCode`
689
- * (`PLAN_UPGRADE_REQUIRED` for `aiFeatures` or `whatsappCommerce`,
690
- * `IMPERSONATION_READONLY`).
691
- *
692
- * FREE — no model call was made:
693
- * - `VALIDATION_FAILED` · 400.
694
- * - `AI_OPT_OUT` · 403 — AI is off for this store, or its state could not be read.
695
- * - `WHATSAPP_MESSAGE_NOT_FOUND` · 404 — no INBOUND message of this store has that id.
696
- * - `WHATSAPP_MESSAGE_NOT_TEXT` · 422 — the message has no text (a photo, a sticker).
697
- * - `WHATSAPP_ORDER_DRAFT_MESSAGE_TOO_LONG` · 422 — over 1000 characters.
698
- * - `CUSTOMER_NOT_FOUND` · 404 — `customerId` names no active customer of this store.
699
- * - `COST_CEILING_EXCEEDED` · 403 or 503 — see `AiCostCeilingRefusal`.
700
- * - `AI_WHATSAPP_ORDER_DRAFT_DISABLED` · 503 — the platform kill-switch for
701
- * this feature is off. Distinct from a ceiling: nothing was spent and no
702
- * budget is exhausted. Like `AI_OPT_OUT`, it is checked AFTER the stored
703
- * draft is read, so a message that already has a `ready` draft is still
704
- * answered with it (200, no spend) while the switch is off. The automatic
705
- * worker checks it before it claims the message and skips it — no draft
706
- * row, no frame — so the message stays undrafted until a staff `POST`.
707
- * - `AI_UNAVAILABLE` · 503 — the platform could not make the model call at
708
- * all (a server-side fault, the same code on every AI endpoint). Nothing
709
- * was sent or spent; the message's claim is settled `failed`, so a later
710
- * `POST` drafts it. The automatic worker writes no draft row for it at all
711
- * and retries the message itself.
712
- * - `WHATSAPP_ORDER_DRAFT_PENDING` · 409 — the message is being drafted right
713
- * now (by the automatic worker or another request); carries
714
- * `status: 'pending'`. Wait for the `whatsapp-order-draft` frame or retry.
715
- * A `pending` claim older than 60 s is dead (its invocation timed out
716
- * before settling) and the next `POST` takes it over instead.
717
- *
718
- * A `POST` for a message that already has a `ready` stored draft answers
719
- * that draft (200) and spends nothing; only an absent, `failed` or stale one is
720
- * drafted again. A `failed` draft whose call was METERED (below) is spent on
721
- * a second time when a `POST` retries it — the worker never does.
722
- *
723
- * METERED — the call ran and its output was refused:
724
- * - `WHATSAPP_ORDER_DRAFT_BLOCKED` · 422 — the guardrail refused; carries `reason`
725
- * (filter type names only).
726
- * - `WHATSAPP_ORDER_DRAFT_GENERATION_FAILED` · 502 — the output did not parse.
727
- */
728
- type WhatsAppOrderDraftErrorCode = 'VALIDATION_FAILED' | 'AI_OPT_OUT' | 'WHATSAPP_MESSAGE_NOT_FOUND' | 'WHATSAPP_MESSAGE_NOT_TEXT' | 'WHATSAPP_ORDER_DRAFT_MESSAGE_TOO_LONG' | 'CUSTOMER_NOT_FOUND' | 'COST_CEILING_EXCEEDED' | 'AI_WHATSAPP_ORDER_DRAFT_DISABLED' | 'AI_UNAVAILABLE' | 'WHATSAPP_ORDER_DRAFT_PENDING' | 'WHATSAPP_ORDER_DRAFT_BLOCKED' | 'WHATSAPP_ORDER_DRAFT_GENERATION_FAILED';
729
- /**
730
- * The one stored draft of a message — built by the automatic worker when
731
- * the message arrived (stores with `config.whatsappOrderAutoDraft` on) or
732
- * by a staff `POST /whatsapp/order-draft`. Read at `GET
733
- * /whatsapp/order-draft?messageId=`. The worker pushes it as the
734
- * `whatsapp-order-draft` frame when it settles, through `wsPostStore` with
735
- * `audience: 'operator'` — the store's operator sockets, never the
736
- * customer's. Kept as long as the message it reads (90 days).
737
- *
738
- * - `pending` — being drafted; no `draft`, no `error`.
739
- * - `ready` — `draft` is set. Still a DRAFT: staff place the order.
740
- * - `failed` — `error` is set, one of `WhatsAppOrderDraftErrorCode` or
741
- * `WHATSAPP_ORDER_DRAFT_FAILED` for an unexpected fault. The worker never
742
- * retries it; a staff `POST` does.
743
- */
744
- interface WhatsAppStoredOrderDraft {
745
- messageId: string;
746
- status: 'pending' | 'ready' | 'failed';
747
- draft?: WhatsAppOrderDraftResponse;
748
- error?: string;
749
- /** Epoch ms. */
750
- createdAt: number;
751
- /** Epoch ms. */
752
- updatedAt: number;
753
- }
754
- /** Staff sends a priced quote to the sender of one stored inbound WhatsApp message. No AI spend. */
755
- interface WhatsAppOrderConfirmationRequest {
756
- messageId: string;
757
- presupuestoId: string;
758
- paymentMethod?: number;
759
- deliveryMethod?: number;
760
- }
761
- type WhatsAppOrderConfirmationStatus = 'sending' | 'awaiting' | 'confirming' | 'confirmed' | 'cancelled' | 'changes_requested' | 'expired' | 'superseded' | 'failed';
762
- /**
763
- * Public confirmation returned by POST /whatsapp/order-draft/confirmation and
764
- * emitted to operator sockets. The private nonce and sender number never reach
765
- * this shape. An accepted staff-authored quote is converted through the existing
766
- * atomic order service; an AI parse cannot create an order.
767
- */
768
- interface WhatsAppOrderConfirmation {
769
- messageId: string;
770
- presupuestoId: string;
771
- status: WhatsAppOrderConfirmationStatus;
772
- customerId: string;
773
- paymentMethod?: number;
774
- deliveryMethod?: number;
775
- sentBy: {
776
- userId: string;
777
- fullName: string;
778
- };
779
- /** Epoch ms; the earlier of the quote expiry and 24 hours after sending. */
780
- expiresAt: number;
781
- sentMessageId?: string;
782
- orderId?: string;
783
- /** A stable refusal code, never a raw provider message or customer text. */
784
- error?: string;
785
- createdAt: number;
786
- updatedAt: number;
787
- }
788
- /**
789
- * Route refusals in addition to the existing entitlement/impersonation codes.
790
- * `…_DISABLED` (503) is the `whatsappOrderConfirmationEnabled` platform switch;
791
- * `…_RECIPIENT_MISMATCH` (409) is a quote whose customer phone is not the
792
- * source message's sender; `…_SETTLED` (409) carries the final confirmation
793
- * in `data` (confirmed, cancelled, superseded, or failed after the tap).
794
- */
795
- type WhatsAppOrderConfirmationErrorCode = 'VALIDATION_FAILED' | 'ADMIN_REQUIRED' | 'WHATSAPP_MESSAGE_NOT_FOUND' | 'PRESUPUESTO_NOT_FOUND' | 'PRESUPUESTO_NOT_MUTABLE' | 'CUSTOMER_NOT_FOUND' | 'PAYMENT_METHOD_NOT_FOUND' | 'DELIVERY_METHOD_NOT_FOUND' | 'WHATSAPP_WINDOW_CLOSED' | 'WHATSAPP_ORDER_CONFIRMATION_DISABLED' | 'WHATSAPP_ORDER_CONFIRMATION_RECIPIENT_MISMATCH' | 'WHATSAPP_ORDER_CONFIRMATION_PENDING' | 'WHATSAPP_ORDER_CONFIRMATION_SETTLED' | 'PLATFORM_INTEGRATION_DISABLED' | 'WHATSAPP_NOT_CONNECTED' | 'WHATSAPP_RECIPIENT_NOT_ALLOWED' | 'WHATSAPP_SEND_UNCONFIRMED' | 'WHATSAPP_SEND_FAILED' | 'WHATSAPP_TEMPLATE_NOT_APPROVED' | 'WHATSAPP_RECIPIENT_OPTED_OUT' | 'WHATSAPP_RATE_LIMITED' | 'WHATSAPP_ACCOUNT_SUSPENDED' | 'WHATSAPP_TOKEN_REJECTED';
796
624
  /**
797
625
  * The ONE spent-budget refusal every AI spend route answers —
798
626
  * `POST /products/enrich`, `/reports/narrative`, `/marketing/copy`,
799
627
  * `/reminders/draft`, `/suppliers/doc-extract`,
800
- * `/suppliers/price-list-extract`, `/presupuestos/draft` and `/whatsapp/order-draft`, and any AI
801
- * route added later. There is no second spelling and no 429 for it.
628
+ * `/suppliers/price-list-extract` and `/presupuestos/draft`, and any AI route
629
+ * added later. There is no second spelling and no 429 for it.
802
630
  *
803
631
  * - 403 — the TENANT's own ceiling. Carries `feature`, `upsell` and `period`.
804
632
  * `upsell` is true only for `monthly`: a daily tenant ceiling is one
package/dist/audit.d.ts CHANGED
@@ -58,7 +58,8 @@ declare global {
58
58
  *
59
59
  * - `operator` — a store user acting through the App. Actor is that user.
60
60
  * - `platform` — a MANAGER acting through `/platform/*`. Actor is that user.
61
- * - `customer` — a web, WhatsApp or storefront customer. Actor is the
61
+ * - `customer` — a web or storefront customer (older rows also carry ones who
62
+ * ordered through the since-removed WhatsApp checkout). Actor is the
62
63
  * `CUST…` id with `fullName: ''` ALWAYS: an `AUDIT#` row has no TTL and is
63
64
  * not reached by erasure, so a stored customer name could never be erased.
64
65
  * Render the name from the live customer row.
package/dist/cjs/index.js CHANGED
@@ -122,7 +122,6 @@ __exportStar(require("./userActivity.js"), exports);
122
122
  __exportStar(require("./valuation.js"), exports);
123
123
  __exportStar(require("./webhook.js"), exports);
124
124
  __exportStar(require("./wire.js"), exports);
125
- __exportStar(require("./whatsapp.js"), exports);
126
125
  __exportStar(require("./workItem.js"), exports);
127
126
  __exportStar(require("./zone.js"), exports);
128
127
  __exportStar(require("./provinces.js"), exports);
@@ -28,8 +28,7 @@ const product_ts_1 = require("./product.js");
28
28
  *
29
29
  * - `counter` — the operator basket and `POST /orders`, which the app's POS and
30
30
  * the mobile app share;
31
- * - `storefront` — the shopper cart and the web checkout. Its WhatsApp catalogue
32
- * and order drafts still leave kits out.
31
+ * - `storefront` — the shopper cart and the web checkout.
33
32
  */
34
33
  exports.KIT_SALE_CHANNELS = ['counter', 'storefront'];
35
34
  /**
package/dist/cjs/order.js CHANGED
@@ -364,14 +364,14 @@ exports.orderFulfilmentProgress = orderFulfilmentProgress;
364
364
  * `data.shiftId`. Logged; reconcile the queued sale by hand.
365
365
  * - `ORDER_CURRENCY_NOT_SUBSCRIBED` · 400 — the sale's currency has no
366
366
  * subscription in the store's `currencies`. Logged.
367
- * - `PAYMENT_METHODS_NOT_CONFIGURED` · 409 — `tenders` sent, or storefront/
368
- * WhatsApp checkout attempted, while the store has no payment methods at
367
+ * - `PAYMENT_METHODS_NOT_CONFIGURED` · 409 — `tenders` sent, or storefront
368
+ * checkout attempted, while the store has no payment methods at
369
369
  * all. Storefront checkout carries `data: {}` and refuses before the cart read.
370
370
  * - `PAYMENT_METHOD_NOT_FOUND` · 400 — a tender leg's `method`, or storefront
371
371
  * checkout's `paymentMethod`, names none of the store's payment methods.
372
372
  * Storefront checkout carries `data: {}` and refuses before the cart read.
373
373
  *
374
- * Storefront and WhatsApp checkout business refusals. None allocates an order
374
+ * Storefront checkout business refusals. None allocates an order
375
375
  * id, redeems a coupon or writes an order; none is logged as an error:
376
376
  * - `DELIVERY_METHOD_NOT_FOUND` · 400 — `deliveryMethod` is not in the store's
377
377
  * delivery catalogue, including an absent/empty catalogue. `data: {}`;
@@ -501,13 +501,6 @@ exports.SOCKET_ACTIONS = [
501
501
  // Notifications & messaging.
502
502
  'notifications',
503
503
  'support-message',
504
- /**
505
- * An automatic WhatsApp order draft settled (`ready` or `failed`). The
506
- * operator sockets of the store only; `data` is a `WhatsAppStoredOrderDraft`.
507
- */
508
- 'whatsapp-order-draft',
509
- /** Staff quote confirmation state; operator audience only, data: WhatsAppOrderConfirmation. */
510
- 'whatsapp-order-confirmation',
511
504
  // Payments & providers.
512
505
  'payment_received',
513
506
  // Operator-only observation invalidation; never a receipt or customer frame.
@@ -25,7 +25,7 @@
25
25
  * - Feature keys now match the BE wire format directly (renamed afip→afipInvoicing,
26
26
  * cash→cashManagement, reportsAdvanced→advancedReports as of 2026-04-26). The interim
27
27
  * paymentIntegrations key was split into domesticPayments + stripePayments.
28
- * New keys whatsappCommerce/aiFeatures/mobileApp/customDomain are
28
+ * New keys aiFeatures/mobileApp/customDomain are
29
29
  * declared here even when their epics haven't shipped — the matrix can set
30
30
  * enabled:false until they do.
31
31
  */
@@ -113,7 +113,6 @@ exports.FEATURE_KEYS = [
113
113
  'importExport',
114
114
  'domesticPayments',
115
115
  'stripePayments',
116
- 'whatsappCommerce',
117
116
  'aiFeatures',
118
117
  'mobileApp',
119
118
  'customBranding',
@@ -133,5 +132,4 @@ exports.FEATURE_KEYS = [
133
132
  'webhooks',
134
133
  'maxOrdersMonth',
135
134
  'maxInvoicesMonth',
136
- 'whatsappMessages',
137
135
  ];
package/dist/cjs/wire.js CHANGED
@@ -18,7 +18,7 @@
18
18
  * describes the result in the same edit.
19
19
  */
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.ROW_TTL_KEYS = exports.USER_TOTP_INTERNAL_KEYS = exports.STORE_TIENDANUBE_SECRET_KEYS = exports.STORE_WHATSAPP_SECRET_KEYS = exports.STORE_MERCADOLIBRE_SECRET_KEYS = exports.STORE_GMAIL_SECRET_KEYS = exports.STORE_MP_SECRET_KEYS = exports.STORE_AFIP_SECRET_KEYS = exports.RESPONSE_STRIPPED_KEYS = void 0;
21
+ exports.ROW_TTL_KEYS = exports.USER_TOTP_INTERNAL_KEYS = exports.STORE_TIENDANUBE_SECRET_KEYS = exports.STORE_MERCADOLIBRE_SECRET_KEYS = exports.STORE_GMAIL_SECRET_KEYS = exports.STORE_MP_SECRET_KEYS = exports.STORE_AFIP_SECRET_KEYS = exports.RESPONSE_STRIPPED_KEYS = void 0;
22
22
  /**
23
23
  * The seven keys `response()` deletes from `body.data`.
24
24
  *
@@ -67,8 +67,6 @@ exports.STORE_MERCADOLIBRE_SECRET_KEYS = [
67
67
  'refreshTokenEncrypted',
68
68
  'refreshAttemptAt',
69
69
  ];
70
- /** WhatsApp's only secret field on `Store.integrations.whatsapp`. */
71
- exports.STORE_WHATSAPP_SECRET_KEYS = ['accessToken'];
72
70
  /**
73
71
  * KMS-ciphertext TiendaNube OAuth credential on `Store.integrations.tiendanube`.
74
72
  * A single `accessTokenEncrypted` — TiendaNube issues no refresh token, so there
@@ -420,8 +420,8 @@ declare global {
420
420
  * already makes on this row.
421
421
  *
422
422
  * ⚠️ Fed ONLY by a `ReminderRecord` with `kind: 'reminder'` — a delivered
423
- * collections chase on ANY channel (the email leg or the scheduled
424
- * WhatsApp leg), so both legs share one cooldown. It is absent on a
423
+ * collections chase on ANY channel (the email leg, or on older rows the
424
+ * since-removed WhatsApp leg), so every leg shared one cooldown. It is absent on a
425
425
  * customer who was never chased, and that is correct rather than missing:
426
426
  * populating it from transactional mail would read as "already chased" to
427
427
  * a collections operator who would then skip a debtor.
package/dist/index.d.ts CHANGED
@@ -98,7 +98,6 @@ export * from './userActivity.ts';
98
98
  export * from './valuation.ts';
99
99
  export * from './webhook.ts';
100
100
  export * from './wire.ts';
101
- export * from './whatsapp.ts';
102
101
  export * from './workItem.ts';
103
102
  export * from './zone.ts';
104
103
  export * from './provinces.ts';
package/dist/index.js CHANGED
@@ -106,7 +106,6 @@ export * from "./userActivity.js";
106
106
  export * from "./valuation.js";
107
107
  export * from "./webhook.js";
108
108
  export * from "./wire.js";
109
- export * from "./whatsapp.js";
110
109
  export * from "./workItem.js";
111
110
  export * from "./zone.js";
112
111
  export * from "./provinces.js";
@@ -24,8 +24,7 @@
24
24
  *
25
25
  * - `counter` — the operator basket and `POST /orders`, which the app's POS and
26
26
  * the mobile app share;
27
- * - `storefront` — the shopper cart and the web checkout. Its WhatsApp catalogue
28
- * and order drafts still leave kits out.
27
+ * - `storefront` — the shopper cart and the web checkout.
29
28
  */
30
29
  export declare const KIT_SALE_CHANNELS: readonly ["counter", "storefront"];
31
30
  /**
package/dist/kitRecipe.js CHANGED
@@ -25,8 +25,7 @@ import { MAX_BASE_QUANTITY } from "./product.js";
25
25
  *
26
26
  * - `counter` — the operator basket and `POST /orders`, which the app's POS and
27
27
  * the mobile app share;
28
- * - `storefront` — the shopper cart and the web checkout. Its WhatsApp catalogue
29
- * and order drafts still leave kits out.
28
+ * - `storefront` — the shopper cart and the web checkout.
30
29
  */
31
30
  export const KIT_SALE_CHANNELS = ['counter', 'storefront'];
32
31
  /**
@@ -18,15 +18,10 @@ declare global {
18
18
  /**
19
19
  * A channel a maintenance broadcast can actually leave the platform on.
20
20
  *
21
- * ⚠️ `'whatsapp'` is deliberately ABSENT, and its absence is the contract
22
- * rather than an oversight. The platform's WhatsApp send path
23
- * (`POST /tenants/whatsapp`, `mode: 'template'`) is a synchronous HTTP
24
- * handler, not a queue producer, and Meta requires a pre-approved template
25
- * per message: a fleet-wide fan-out through it would be N inline Graph API
26
- * calls inside one request, against a template that does not exist yet.
27
- * Declaring the channel here before either is true would let a caller ask
28
- * for a send this service cannot make and would have to refuse at runtime.
29
- * Adding the member later is purely additive for every consumer.
21
+ * ⚠️ `'whatsapp'` is deliberately ABSENT: the platform has no WhatsApp send
22
+ * path, so declaring it would let a caller ask for a send this service cannot
23
+ * make and would have to refuse at runtime. Adding the member later is purely
24
+ * additive for every consumer.
30
25
  */
31
26
  type MaintenanceBroadcastChannel = 'email' | 'sms';
32
27
  /**
@@ -341,13 +341,11 @@ declare global {
341
341
  * What the outbound seam (`notify()`) knew about one call the moment its
342
342
  * transport returned. It is never a delivery receipt: `enqueued` and
343
343
  * `handed-off` say a queue or a sender took the message, and `sent` says the
344
- * provider accepted it. Later SES events arrive through `EMAILEVENT#`;
345
- * WhatsApp status callbacks update the established `WHATSAPP#` history.
344
+ * provider accepted it. Later SES events arrive through `EMAILEVENT#`.
346
345
  */
347
346
  type NotifyDeliveryOutcome = 'sent' | 'suppressed' | 'not-sent' | 'enqueued' | 'handed-off' | 'failed';
348
347
  /** Why a channel did not deliver, including send-time preference refusals. */
349
348
  type NotifyNotSentReason = 'skipped-by-preference' | 'demo' | 'suppressed' | 'refused';
350
- type NotifyWhatsAppSuppressionReason = 'allowlist-missing' | 'recipient-malformed' | 'recipient-not-allowlisted';
351
349
  /**
352
350
  * A recorded transport outcome or send-time preference refusal — the
353
351
  * wire half of the `NOTIFY#{storeId}` delivery row. The stored row,
@@ -362,14 +360,14 @@ declare global {
362
360
  * `transport`, plus a count.
363
361
  */
364
362
  interface NotifyDelivery {
365
- transport: 'email' | 'queue' | 'ses' | 'ses-marketing' | 'push' | 'whatsapp';
363
+ transport: 'email' | 'queue' | 'ses' | 'ses-marketing' | 'push';
366
364
  /** The closed audience enum `notify()` accepts. */
367
365
  audience: 'store-customer' | 'account-holder' | 'store-operator' | 'platform-staff' | 'marketing-recipient';
368
366
  /** The producer's event name. Never recipient data. */
369
367
  logTag: string;
370
368
  outcome: NotifyDeliveryOutcome;
371
369
  /** Only when `outcome` is `'not-sent'` or `'suppressed'`. */
372
- reason?: NotifyNotSentReason | NotifyWhatsAppSuppressionReason;
370
+ reason?: NotifyNotSentReason;
373
371
  /** Provider channel for sent email; semantic channel for preference skips. */
374
372
  channel?: 'gmail' | 'ses' | NotificationPreferenceChannel;
375
373
  /** Only when SES returned one. The join key to `EMAILEVENT#`. */
package/dist/order.d.ts CHANGED
@@ -2722,14 +2722,14 @@ export declare const orderFulfilmentProgress: (order: OrderFulfilmentProgressInp
2722
2722
  * `data.shiftId`. Logged; reconcile the queued sale by hand.
2723
2723
  * - `ORDER_CURRENCY_NOT_SUBSCRIBED` · 400 — the sale's currency has no
2724
2724
  * subscription in the store's `currencies`. Logged.
2725
- * - `PAYMENT_METHODS_NOT_CONFIGURED` · 409 — `tenders` sent, or storefront/
2726
- * WhatsApp checkout attempted, while the store has no payment methods at
2725
+ * - `PAYMENT_METHODS_NOT_CONFIGURED` · 409 — `tenders` sent, or storefront
2726
+ * checkout attempted, while the store has no payment methods at
2727
2727
  * all. Storefront checkout carries `data: {}` and refuses before the cart read.
2728
2728
  * - `PAYMENT_METHOD_NOT_FOUND` · 400 — a tender leg's `method`, or storefront
2729
2729
  * checkout's `paymentMethod`, names none of the store's payment methods.
2730
2730
  * Storefront checkout carries `data: {}` and refuses before the cart read.
2731
2731
  *
2732
- * Storefront and WhatsApp checkout business refusals. None allocates an order
2732
+ * Storefront checkout business refusals. None allocates an order
2733
2733
  * id, redeems a coupon or writes an order; none is logged as an error:
2734
2734
  * - `DELIVERY_METHOD_NOT_FOUND` · 400 — `deliveryMethod` is not in the store's
2735
2735
  * delivery catalogue, including an absent/empty catalogue. `data: {}`;
package/dist/order.js CHANGED
@@ -357,14 +357,14 @@ export const orderFulfilmentProgress = (order) => {
357
357
  * `data.shiftId`. Logged; reconcile the queued sale by hand.
358
358
  * - `ORDER_CURRENCY_NOT_SUBSCRIBED` · 400 — the sale's currency has no
359
359
  * subscription in the store's `currencies`. Logged.
360
- * - `PAYMENT_METHODS_NOT_CONFIGURED` · 409 — `tenders` sent, or storefront/
361
- * WhatsApp checkout attempted, while the store has no payment methods at
360
+ * - `PAYMENT_METHODS_NOT_CONFIGURED` · 409 — `tenders` sent, or storefront
361
+ * checkout attempted, while the store has no payment methods at
362
362
  * all. Storefront checkout carries `data: {}` and refuses before the cart read.
363
363
  * - `PAYMENT_METHOD_NOT_FOUND` · 400 — a tender leg's `method`, or storefront
364
364
  * checkout's `paymentMethod`, names none of the store's payment methods.
365
365
  * Storefront checkout carries `data: {}` and refuses before the cart read.
366
366
  *
367
- * Storefront and WhatsApp checkout business refusals. None allocates an order
367
+ * Storefront checkout business refusals. None allocates an order
368
368
  * id, redeems a coupon or writes an order; none is logged as an error:
369
369
  * - `DELIVERY_METHOD_NOT_FOUND` · 400 — `deliveryMethod` is not in the store's
370
370
  * delivery catalogue, including an absent/empty catalogue. `data: {}`;
@@ -88,8 +88,8 @@ export interface ReseedConstraintsOpBody extends PlatformOperatorReason {
88
88
  */
89
89
  export interface RotateWebhookKeyOpBody extends PlatformOperatorReason {
90
90
  mode: 'rotate-webhook-key';
91
- provider: 'mercadopago' | 'stripe' | 'whatsapp';
92
- /** Required for `stripe` and `whatsapp` (provider-issued); optional override for `mercadopago`. Never echoed or audited. */
91
+ provider: 'mercadopago' | 'stripe';
92
+ /** Required for `stripe` (provider-issued); optional override for `mercadopago`. Never echoed or audited. */
93
93
  newSecret?: string;
94
94
  /** Must equal `provider`. Required — absent or wrong is `400 CONFIRMATION_MISMATCH`. */
95
95
  confirm: string;
@@ -460,9 +460,6 @@ export interface TenantHealthEnvelope {
460
460
  connected: boolean;
461
461
  oauthExpiresAt: number | null;
462
462
  };
463
- whatsapp: {
464
- enabled: boolean;
465
- };
466
463
  sms: {
467
464
  enabled: boolean;
468
465
  };
package/dist/product.d.ts CHANGED
@@ -774,7 +774,7 @@ declare global {
774
774
  /**
775
775
  * Most units of this product ONE sale may carry, summed across every line of
776
776
  * the operation. Enforced server-side on every local sale entry point
777
- * (order create, storefront and WhatsApp checkout, presupuesto conversion,
777
+ * (order create, storefront checkout, presupuesto conversion,
778
778
  * service delivery, and quantity-increasing edits), which refuse
779
779
  * `409 PRODUCT_SALE_LIMIT_EXCEEDED` — see {@link ProductSaleLimitRefusal}.
780
780
  *
@@ -2,10 +2,11 @@ declare global {
2
2
  /**
3
3
  * Which dispatcher actually delivered. Only the send helper knows this.
4
4
  *
5
- * `'whatsapp'` is the scheduled payment reminder's WhatsApp leg
6
- * (`Store.config.paymentReminders.channels`): a template message sent from the
7
- * store's own connected number. On such a row `to` is the phone number the
8
- * message went to, digits only, not an email address.
5
+ * ⚠️ `'whatsapp'` is HISTORY only. Nothing sends or records it any more, but the
6
+ * automatic payment reminder once had a WhatsApp leg, and the rows it wrote are
7
+ * durable and still read back in a customer's reminder history. On such a row
8
+ * `to` is the phone number the message went to, digits only, not an email
9
+ * address. Handle the member; never expect a new row to carry it.
9
10
  */
10
11
  type ReminderChannel = 'gmail' | 'ses' | 'whatsapp';
11
12
  /**
package/dist/socket.d.ts CHANGED
@@ -26,7 +26,7 @@
26
26
  * A client must ignore actions it does not own rather than assume it
27
27
  * receives all of them.
28
28
  */
29
- export declare const SOCKET_ACTIONS: readonly ["account", "appointments", "appointmentWaitlist", "attendance", "attributes", "automation-rules", "baskets", "cart", "brands", "cash", "catalogOperation", "categories", "campaigns", "customers", "customerNote", "customerAddress", "customerContact", "deliveries", "globals", "inventory", "invoices", "literals", "loyalty", "orders", "presupuestos", "products", "promotions", "segments", "returns", "rma", "templates", "services", "service-templates", "shifts", "stores", "suppliers", "supplier-invoice", "support", "surveys", "users", "warehouses", "zones", "favorites", "webhooks", "account-delete", "baskets-delete", "cart-delete", "log-delete", "notifications", "support-message", "whatsapp-order-draft", "whatsapp-order-confirmation", "payment_received", "mp_payment_status_changed", "payment_linked", "payment_unlinked", "mercadopago", "mercadopago_dynamic_qr_created", "mercadopago_static_qr_created", "mp_hook_log_appended", "mp_ipn_log_appended", "orderPaymentAttempt", "paymentAttemptChanged", "stripe", "subscription", "caea", "print", "print-order", "print-invoice", "print-product", "print-tag", "print_job_failed", "request_printers", "printers_changed", "agent_status_changed", "print_rules_changed", "printers_active", "agent_command", "maintenance", "currency_auto_updated", "drain_progress", "integration_event_appended", "userActivityRecorded", "mercadolibre_settlement_period", "customDomain", "purchaseOrders", "cycleCounts", "stockTransfers", "workItems"];
29
+ export declare const SOCKET_ACTIONS: readonly ["account", "appointments", "appointmentWaitlist", "attendance", "attributes", "automation-rules", "baskets", "cart", "brands", "cash", "catalogOperation", "categories", "campaigns", "customers", "customerNote", "customerAddress", "customerContact", "deliveries", "globals", "inventory", "invoices", "literals", "loyalty", "orders", "presupuestos", "products", "promotions", "segments", "returns", "rma", "templates", "services", "service-templates", "shifts", "stores", "suppliers", "supplier-invoice", "support", "surveys", "users", "warehouses", "zones", "favorites", "webhooks", "account-delete", "baskets-delete", "cart-delete", "log-delete", "notifications", "support-message", "payment_received", "mp_payment_status_changed", "payment_linked", "payment_unlinked", "mercadopago", "mercadopago_dynamic_qr_created", "mercadopago_static_qr_created", "mp_hook_log_appended", "mp_ipn_log_appended", "orderPaymentAttempt", "paymentAttemptChanged", "stripe", "subscription", "caea", "print", "print-order", "print-invoice", "print-product", "print-tag", "print_job_failed", "request_printers", "printers_changed", "agent_status_changed", "print_rules_changed", "printers_active", "agent_command", "maintenance", "currency_auto_updated", "drain_progress", "integration_event_appended", "userActivityRecorded", "mercadolibre_settlement_period", "customDomain", "purchaseOrders", "cycleCounts", "stockTransfers", "workItems"];
30
30
  /** Union of every server→client data-frame action. */
31
31
  export type SocketAction = (typeof SOCKET_ACTIONS)[number];
32
32
  /**
package/dist/socket.js CHANGED
@@ -498,13 +498,6 @@ export const SOCKET_ACTIONS = [
498
498
  // Notifications & messaging.
499
499
  'notifications',
500
500
  'support-message',
501
- /**
502
- * An automatic WhatsApp order draft settled (`ready` or `failed`). The
503
- * operator sockets of the store only; `data` is a `WhatsAppStoredOrderDraft`.
504
- */
505
- 'whatsapp-order-draft',
506
- /** Staff quote confirmation state; operator audience only, data: WhatsAppOrderConfirmation. */
507
- 'whatsapp-order-confirmation',
508
501
  // Payments & providers.
509
502
  'payment_received',
510
503
  // Operator-only observation invalidation; never a receipt or customer frame.
package/dist/store.d.ts CHANGED
@@ -270,16 +270,10 @@ declare global {
270
270
  * at send time and failing CLOSED.
271
271
  *
272
272
  * - `channels` — which legs the chase runs on. ABSENT means `['email']`, so a
273
- * store that never set it behaves exactly as before. `'whatsapp'` adds a
274
- * template message from the store's OWN connected WhatsApp number, carrying
275
- * a MercadoPago payment link for the balance, and needs more than the email
276
- * leg: the `whatsappCommerce` entitlement, headroom on the monthly
277
- * `whatsappMessages` meter, a connected MercadoPago account, a ledger in
278
- * ARS, and per customer a `marketing.consent.whatsapp` stamp with
279
- * `marketing.whatsapp === true` and a phone. Both legs share one cooldown
280
- * (`Customer.lastReminderAt`). On a store with both channels the email leg
281
- * wins the period: a customer it can chase gets the email only, and the
282
- * WhatsApp leg covers the customers email cannot reach (no address).
273
+ * store that never set it behaves exactly as before; `'email'` is the only
274
+ * leg. ⚠️ A store saved while a `'whatsapp'` leg still existed can carry that
275
+ * member until its next save: the chase ignores it, and `PATCH /store` accepts
276
+ * it and drops it (a list naming ONLY it saves as `frequency: 'none'`).
283
277
  */
284
278
  interface PaymentRemindersConfig {
285
279
  frequency?: 'none' | 'weekly' | 'monthly';
@@ -288,7 +282,7 @@ declare global {
288
282
  channels?: PaymentReminderChannel[];
289
283
  }
290
284
  /** A leg of the automatic payment reminder — see `PaymentRemindersConfig.channels`. */
291
- type PaymentReminderChannel = 'email' | 'whatsapp';
285
+ type PaymentReminderChannel = 'email';
292
286
  /**
293
287
  * Cut-offs and window for the periodic ABC (Pareto) classification of the
294
288
  * catalogue. Every member is optional and absence falls back to the api's
@@ -879,13 +873,6 @@ declare global {
879
873
  * storefront.
880
874
  */
881
875
  publicContact?: boolean;
882
- /**
883
- * Draft an order automatically from every inbound WhatsApp text message.
884
- * Off unless an admin turns it on. Still needs the `aiFeatures` and
885
- * `whatsappCommerce` entitlements, and `aiOptOut` still wins. Drafts are
886
- * never placed or sent: staff confirm each one.
887
- */
888
- whatsappOrderAutoDraft?: boolean;
889
876
  /**
890
877
  * Per-category × per-channel notification/feedback defaults; absent ⇒
891
878
  * FE falls back to all-on. FE-read only — `PATCH /store` validates shape only.
@@ -1251,7 +1238,6 @@ declare global {
1251
1238
  interface StoreIntegrations {
1252
1239
  afip?: Afip;
1253
1240
  mercadopago?: Mercadopago;
1254
- whatsapp?: WhatsAppConfig;
1255
1241
  sms?: SmsIntegration;
1256
1242
  gmail?: Gmail;
1257
1243
  mercadolibre?: Mercadolibre;
@@ -24,7 +24,7 @@
24
24
  * - Feature keys now match the BE wire format directly (renamed afip→afipInvoicing,
25
25
  * cash→cashManagement, reportsAdvanced→advancedReports as of 2026-04-26). The interim
26
26
  * paymentIntegrations key was split into domesticPayments + stripePayments.
27
- * New keys whatsappCommerce/aiFeatures/mobileApp/customDomain are
27
+ * New keys aiFeatures/mobileApp/customDomain are
28
28
  * declared here even when their epics haven't shipped — the matrix can set
29
29
  * enabled:false until they do.
30
30
  */
@@ -100,7 +100,7 @@ declare global {
100
100
  * exhausts this union (a `switch`, a `Record<FeatureKey, …>`) still has to
101
101
  * handle the new member in the same change.
102
102
  */
103
- type FeatureKey = 'maxOrdersMonth' | 'maxInvoicesMonth' | 'whatsappMessages' | 'maxProducts' | 'maxUsers' | 'maxCustomers' | 'maxWarehouses' | 'priceListsMax' | 'webhooks' | 'afipInvoicing' | 'suppliers' | 'domesticPayments' | 'stripePayments' | 'cashManagement' | 'importExport' | 'advancedReports' | 'customBranding' | 'prioritySupport' | 'whatsappCommerce' | 'aiFeatures' | 'mobileApp' | 'customDomain' | 'advancedPricing' | 'marketplaceChannels' | 'storefront' | 'loyalty' | 'marketing' | 'mandatory2fa';
103
+ type FeatureKey = 'maxOrdersMonth' | 'maxInvoicesMonth' | 'maxProducts' | 'maxUsers' | 'maxCustomers' | 'maxWarehouses' | 'priceListsMax' | 'webhooks' | 'afipInvoicing' | 'suppliers' | 'domesticPayments' | 'stripePayments' | 'cashManagement' | 'importExport' | 'advancedReports' | 'customBranding' | 'prioritySupport' | 'aiFeatures' | 'mobileApp' | 'customDomain' | 'advancedPricing' | 'marketplaceChannels' | 'storefront' | 'loyalty' | 'marketing' | 'mandatory2fa';
104
104
  /** Resolved entitlements for a specific tenant (matrix + overrides applied). */
105
105
  type ResolvedEntitlements = Record<FeatureKey, Entitlement>;
106
106
  /**
@@ -1236,7 +1236,7 @@ export declare const isPaidPlanTier: (value: unknown) => value is PaidPlanTier;
1236
1236
  * metered / lifetime cap PASSES THROUGH — a new cap silently unbounded.
1237
1237
  * Checklist: `docs/PLANS_AND_ENTITLEMENTS.md` in the api repo.
1238
1238
  */
1239
- export declare const FEATURE_KEYS: readonly ["afipInvoicing", "suppliers", "advancedReports", "cashManagement", "importExport", "domesticPayments", "stripePayments", "whatsappCommerce", "aiFeatures", "mobileApp", "customBranding", "customDomain", "prioritySupport", "advancedPricing", "marketplaceChannels", "storefront", "loyalty", "marketing", "mandatory2fa", "maxUsers", "maxProducts", "maxCustomers", "maxWarehouses", "priceListsMax", "webhooks", "maxOrdersMonth", "maxInvoicesMonth", "whatsappMessages"];
1239
+ export declare const FEATURE_KEYS: readonly ["afipInvoicing", "suppliers", "advancedReports", "cashManagement", "importExport", "domesticPayments", "stripePayments", "aiFeatures", "mobileApp", "customBranding", "customDomain", "prioritySupport", "advancedPricing", "marketplaceChannels", "storefront", "loyalty", "marketing", "mandatory2fa", "maxUsers", "maxProducts", "maxCustomers", "maxWarehouses", "priceListsMax", "webhooks", "maxOrdersMonth", "maxInvoicesMonth"];
1240
1240
  type AssertNoUncoveredFeatureKey<T extends never> = T;
1241
1241
  export type UncoveredFeatureKeys = AssertNoUncoveredFeatureKey<Exclude<FeatureKey, (typeof FEATURE_KEYS)[number]>>;
1242
1242
  export {};
@@ -24,7 +24,7 @@
24
24
  * - Feature keys now match the BE wire format directly (renamed afip→afipInvoicing,
25
25
  * cash→cashManagement, reportsAdvanced→advancedReports as of 2026-04-26). The interim
26
26
  * paymentIntegrations key was split into domesticPayments + stripePayments.
27
- * New keys whatsappCommerce/aiFeatures/mobileApp/customDomain are
27
+ * New keys aiFeatures/mobileApp/customDomain are
28
28
  * declared here even when their epics haven't shipped — the matrix can set
29
29
  * enabled:false until they do.
30
30
  */
@@ -109,7 +109,6 @@ export const FEATURE_KEYS = [
109
109
  'importExport',
110
110
  'domesticPayments',
111
111
  'stripePayments',
112
- 'whatsappCommerce',
113
112
  'aiFeatures',
114
113
  'mobileApp',
115
114
  'customBranding',
@@ -129,5 +128,4 @@ export const FEATURE_KEYS = [
129
128
  'webhooks',
130
129
  'maxOrdersMonth',
131
130
  'maxInvoicesMonth',
132
- 'whatsappMessages',
133
131
  ];
package/dist/wire.d.ts CHANGED
@@ -43,8 +43,6 @@ export declare const STORE_MP_SECRET_KEYS: readonly ["accessToken", "code", "ref
43
43
  export declare const STORE_GMAIL_SECRET_KEYS: readonly ["refreshTokenEncrypted", "accessTokenEncrypted"];
44
44
  /** KMS-ciphertext Mercadolibre OAuth credentials + the single-flight refresh marker. */
45
45
  export declare const STORE_MERCADOLIBRE_SECRET_KEYS: readonly ["accessTokenEncrypted", "refreshTokenEncrypted", "refreshAttemptAt"];
46
- /** WhatsApp's only secret field on `Store.integrations.whatsapp`. */
47
- export declare const STORE_WHATSAPP_SECRET_KEYS: readonly ["accessToken"];
48
46
  /**
49
47
  * KMS-ciphertext TiendaNube OAuth credential on `Store.integrations.tiendanube`.
50
48
  * A single `accessTokenEncrypted` — TiendaNube issues no refresh token, so there
@@ -94,8 +92,6 @@ declare global {
94
92
  * because the type has to describe what actually arrives.
95
93
  */
96
94
  type StoreMercadolibreSecretKey = (typeof STORE_MERCADOLIBRE_SECRET_KEYS)[number];
97
- /** The one credential key stripped from `Store.integrations.whatsapp` before it crosses a wire. */
98
- type StoreWhatsappSecretKey = (typeof STORE_WHATSAPP_SECRET_KEYS)[number];
99
95
  /** The one KMS-ciphertext key stripped from `Store.integrations.tiendanube` before it crosses a wire. */
100
96
  type StoreTiendanubeSecretKey = (typeof STORE_TIENDANUBE_SECRET_KEYS)[number];
101
97
  /** A TOTP sub-key stripped from `User.totp` before a USER row crosses a wire. */
package/dist/wire.js CHANGED
@@ -64,8 +64,6 @@ export const STORE_MERCADOLIBRE_SECRET_KEYS = [
64
64
  'refreshTokenEncrypted',
65
65
  'refreshAttemptAt',
66
66
  ];
67
- /** WhatsApp's only secret field on `Store.integrations.whatsapp`. */
68
- export const STORE_WHATSAPP_SECRET_KEYS = ['accessToken'];
69
67
  /**
70
68
  * KMS-ciphertext TiendaNube OAuth credential on `Store.integrations.tiendanube`.
71
69
  * A single `accessTokenEncrypted` — TiendaNube issues no refresh token, so there
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "platform-types",
3
- "version": "1.10.762",
3
+ "version": "1.10.763",
4
4
  "description": "Shared wire and entity contracts for the platform.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,70 +0,0 @@
1
- "use strict";
2
- /**
3
- * Published WhatsApp surface — the tenant-facing connection config and the
4
- * tenant's own template send (`POST /whatsapp/send`).
5
- *
6
- * ⚠️ **Meta's inbound webhook payload is NOT here and must not come back.**
7
- * The twelve shapes Meta POSTs to `/whatsapp` — the entry/change/value
8
- * envelope, the contact, message, document, audio, image, status, error,
9
- * conversation and pricing leaves — live in
10
- * `./internal/whatsappWebhook.ts`, prefixed `WhatsAppWebhook*` and excluded
11
- * from the published build. They are inbound (the provider answering us), so
12
- * they fail `./internal/README.md`'s own test for what may be published, and
13
- * their bare names MERGED with globals in every consumer: a published
14
- * `interface Document` made `document.filename` compile and type as `string`
15
- * against `lib.dom`'s `Document`, returning `undefined` at runtime.
16
- */
17
- Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.WHATSAPP_SEND_ERROR_CODES = void 0;
19
- /**
20
- * `POST /whatsapp/send` refusals, beyond the cross-cutting `GATING_ERROR_CODES`
21
- * (notably `PLAN_LIMIT_EXCEEDED` for the `whatsappMessages` monthly cap).
22
- *
23
- * - `VALIDATION_FAILED` · 400 — including a body naming both or neither of
24
- * `to` / `customerId`.
25
- * - `IDEMPOTENCY_KEY_REQUIRED` · 400 — no `Idempotency-Key` header. The
26
- * idempotency wrapper's own codes (`INVALID_IDEMPOTENCY_KEY`,
27
- * `IDEMPOTENCY_KEY_REUSE`, `IDEMPOTENCY_IN_PROGRESS`, `IDEMPOTENCY_KEY_FAILED`,
28
- * `IDEMPOTENCY_LOCK_CONTENTION`) apply too. `IDEMPOTENCY_KEY_FAILED` (409)
29
- * means an earlier attempt under this key may have reached Meta: check the
30
- * recipient's chat before sending again under a NEW key.
31
- * - `PLATFORM_INTEGRATION_DISABLED` · 503 — WhatsApp is switched off
32
- * platform-wide by SINFACTURA.
33
- * - `WHATSAPP_NOT_CONNECTED` · 409 — the calling store has no connected
34
- * WhatsApp, or a connection with no stored token after a disconnect. Disable
35
- * the send control. ⚠️ Never falls back to the platform number.
36
- * - `WHATSAPP_TOKEN_REJECTED` · 409 — connected, but Meta refused the stored
37
- * token. Reconnect.
38
- * - `CUSTOMER_NOT_FOUND` · 404 / `WHATSAPP_RECIPIENT_PHONE_MISSING` · 400 — a bad
39
- * id versus a real customer with no usable number.
40
- * - `WHATSAPP_RECIPIENT_NOT_ALLOWED` · 403 — non-production stages only: the
41
- * number is not on that stage's test allowlist. Nothing was sent.
42
- * - `WHATSAPP_TEMPLATE_NOT_APPROVED` · 422 — not approved, paused, disabled,
43
- * wrong language or wrong parameter count (Meta does not tell these apart).
44
- * - `WHATSAPP_RECIPIENT_OPTED_OUT` · 409 — the recipient cannot receive it.
45
- * - `WHATSAPP_RATE_LIMITED` · 429 — throughput cap; retry later.
46
- * - `WHATSAPP_ACCOUNT_SUSPENDED` · 403 — the WhatsApp Business account or the
47
- * number itself is restricted.
48
- * - `WHATSAPP_SEND_FAILED` · 502 — any other Meta 4xx refusal; carries `metaCode`.
49
- * - `WHATSAPP_SEND_UNCONFIRMED` · 502 — the request reached Meta and its outcome
50
- * is unknown: no answer came back, Meta answered 5xx, or Meta accepted it
51
- * without a message id.
52
- * The message may have been delivered, so the key is sealed:
53
- * a retry under it answers `IDEMPOTENCY_KEY_FAILED` rather than re-sending.
54
- */
55
- exports.WHATSAPP_SEND_ERROR_CODES = [
56
- 'VALIDATION_FAILED',
57
- 'IDEMPOTENCY_KEY_REQUIRED',
58
- 'PLATFORM_INTEGRATION_DISABLED',
59
- 'WHATSAPP_NOT_CONNECTED',
60
- 'WHATSAPP_TOKEN_REJECTED',
61
- 'CUSTOMER_NOT_FOUND',
62
- 'WHATSAPP_RECIPIENT_PHONE_MISSING',
63
- 'WHATSAPP_RECIPIENT_NOT_ALLOWED',
64
- 'WHATSAPP_TEMPLATE_NOT_APPROVED',
65
- 'WHATSAPP_RECIPIENT_OPTED_OUT',
66
- 'WHATSAPP_RATE_LIMITED',
67
- 'WHATSAPP_ACCOUNT_SUSPENDED',
68
- 'WHATSAPP_SEND_FAILED',
69
- 'WHATSAPP_SEND_UNCONFIRMED',
70
- ];
@@ -1,148 +0,0 @@
1
- /**
2
- * Published WhatsApp surface — the tenant-facing connection config and the
3
- * tenant's own template send (`POST /whatsapp/send`).
4
- *
5
- * ⚠️ **Meta's inbound webhook payload is NOT here and must not come back.**
6
- * The twelve shapes Meta POSTs to `/whatsapp` — the entry/change/value
7
- * envelope, the contact, message, document, audio, image, status, error,
8
- * conversation and pricing leaves — live in
9
- * `./internal/whatsappWebhook.ts`, prefixed `WhatsAppWebhook*` and excluded
10
- * from the published build. They are inbound (the provider answering us), so
11
- * they fail `./internal/README.md`'s own test for what may be published, and
12
- * their bare names MERGED with globals in every consumer: a published
13
- * `interface Document` made `document.filename` compile and type as `string`
14
- * against `lib.dom`'s `Document`, returning `undefined` at runtime.
15
- */
16
- declare global {
17
- /**
18
- * Per-tenant WhatsApp Business connection + plan tier.
19
- *
20
- * ⚠️ **Treat every member as potentially absent on a real row.** Rows
21
- * predate the connect flow that populates this block, and writes here are
22
- * forward-only — an older tenant keeps its original shape rather than being
23
- * backfilled. This is a property of the stored data, not a statement about
24
- * which release shipped the writer, so it does not go stale.
25
- *
26
- * ⚠️ The Meta access token is deliberately absent. It is written by the
27
- * per-tenant OAuth callback and stripped by `sanitizeStoreRow` at every
28
- * STORE-row wire boundary, so a consumer never receives it;
29
- * `WhatsAppConfigRow` (api-internal, unpublished) is what the api's own
30
- * senders read. Declaring it here described a field that never arrives.
31
- */
32
- interface WhatsAppConfig {
33
- wabaId: string;
34
- phoneNumberId: string;
35
- verifiedName: string;
36
- /**
37
- * Meta's quality signal for the number.
38
- *
39
- * ⚠️ `'UNKNOWN'` is a real Meta value, not a placeholder — it is what a
40
- * number with no traffic yet reports, so every freshly connected tenant
41
- * starts here. Render it as "no data", never fold it into `'GREEN'`:
42
- * coercing it makes a brand-new number indistinguishable from a
43
- * healthy one, and the coercion is invisible at the read site.
44
- */
45
- qualityRating: 'GREEN' | 'YELLOW' | 'RED' | 'UNKNOWN';
46
- status: 'connected' | 'disconnected' | 'suspended';
47
- tier: 'free' | 'pro' | 'enterprise';
48
- /** ISO timestamp. */
49
- connectedAt: string;
50
- /** ISO timestamp. */
51
- disconnectedAt?: string;
52
- }
53
- /**
54
- * Request body for `POST /whatsapp/send` — the TENANT's own template send,
55
- * under `adminToken`, from the calling store's own connected number.
56
- *
57
- * ⚠️ The request must carry an `Idempotency-Key` header — without one it is
58
- * refused `400 IDEMPOTENCY_KEY_REQUIRED`. Meta is called before anything is
59
- * written, so a retried request with no key would send the message twice.
60
- *
61
- * ⚠️ There is no `storeId` member and there must never be one. The sending
62
- * store is the authorizer's; a body-supplied one is the shape that would let
63
- * a tenant address another tenant's number.
64
- *
65
- * ⚠️ Template only. Outside Meta's 24-hour customer-service window a free-form
66
- * message is refused, and every case this serves (invoice reminder, payment
67
- * link, order confirmation) is outside it — so there is no free-text member.
68
- *
69
- * Exactly one of `to` / `customerId` is required.
70
- */
71
- interface WhatsAppSendRequest {
72
- /** Meta-approved template NAME, as registered against the store's own WhatsApp Business account. */
73
- template: string;
74
- /**
75
- * Language code of the approved translation. Defaults to `'es'`.
76
- *
77
- * ⚠️ Meta matches a template by (name, language) PAIR — an approved template
78
- * requested in the wrong language is refused exactly like one that was never
79
- * approved (`WHATSAPP_TEMPLATE_NOT_APPROVED`).
80
- */
81
- languageCode?: string;
82
- /**
83
- * BODY placeholders, in `{{1}}`, `{{2}}`, … order.
84
- *
85
- * ⚠️ ORDER IS THE BINDING — Meta positions them, so an amount where a due
86
- * date belongs is accepted, delivered and wrong.
87
- */
88
- parameters?: string[];
89
- /** Recipient, digits with country code (`5491150123456`). Mutually exclusive with `customerId`. */
90
- to?: string;
91
- /** Recipient by id, resolved against the CALLING store's own customers. */
92
- customerId?: string;
93
- /** Display name for the message-log row only; never sent to Meta. */
94
- name?: string;
95
- }
96
- /**
97
- * `data` of a successful `POST /whatsapp/send`.
98
- *
99
- * (Named for the template send, not `WhatsAppSendResult`: that name belongs
100
- * to the api-internal send seam and has a different shape.)
101
- */
102
- interface WhatsAppTemplateSendResult {
103
- /** Meta's message id — the handle every later delivery-status webhook carries. */
104
- messageId: string;
105
- template: string;
106
- /** ms epoch of the accepted send. */
107
- sentAt: number;
108
- }
109
- }
110
- /**
111
- * `POST /whatsapp/send` refusals, beyond the cross-cutting `GATING_ERROR_CODES`
112
- * (notably `PLAN_LIMIT_EXCEEDED` for the `whatsappMessages` monthly cap).
113
- *
114
- * - `VALIDATION_FAILED` · 400 — including a body naming both or neither of
115
- * `to` / `customerId`.
116
- * - `IDEMPOTENCY_KEY_REQUIRED` · 400 — no `Idempotency-Key` header. The
117
- * idempotency wrapper's own codes (`INVALID_IDEMPOTENCY_KEY`,
118
- * `IDEMPOTENCY_KEY_REUSE`, `IDEMPOTENCY_IN_PROGRESS`, `IDEMPOTENCY_KEY_FAILED`,
119
- * `IDEMPOTENCY_LOCK_CONTENTION`) apply too. `IDEMPOTENCY_KEY_FAILED` (409)
120
- * means an earlier attempt under this key may have reached Meta: check the
121
- * recipient's chat before sending again under a NEW key.
122
- * - `PLATFORM_INTEGRATION_DISABLED` · 503 — WhatsApp is switched off
123
- * platform-wide by SINFACTURA.
124
- * - `WHATSAPP_NOT_CONNECTED` · 409 — the calling store has no connected
125
- * WhatsApp, or a connection with no stored token after a disconnect. Disable
126
- * the send control. ⚠️ Never falls back to the platform number.
127
- * - `WHATSAPP_TOKEN_REJECTED` · 409 — connected, but Meta refused the stored
128
- * token. Reconnect.
129
- * - `CUSTOMER_NOT_FOUND` · 404 / `WHATSAPP_RECIPIENT_PHONE_MISSING` · 400 — a bad
130
- * id versus a real customer with no usable number.
131
- * - `WHATSAPP_RECIPIENT_NOT_ALLOWED` · 403 — non-production stages only: the
132
- * number is not on that stage's test allowlist. Nothing was sent.
133
- * - `WHATSAPP_TEMPLATE_NOT_APPROVED` · 422 — not approved, paused, disabled,
134
- * wrong language or wrong parameter count (Meta does not tell these apart).
135
- * - `WHATSAPP_RECIPIENT_OPTED_OUT` · 409 — the recipient cannot receive it.
136
- * - `WHATSAPP_RATE_LIMITED` · 429 — throughput cap; retry later.
137
- * - `WHATSAPP_ACCOUNT_SUSPENDED` · 403 — the WhatsApp Business account or the
138
- * number itself is restricted.
139
- * - `WHATSAPP_SEND_FAILED` · 502 — any other Meta 4xx refusal; carries `metaCode`.
140
- * - `WHATSAPP_SEND_UNCONFIRMED` · 502 — the request reached Meta and its outcome
141
- * is unknown: no answer came back, Meta answered 5xx, or Meta accepted it
142
- * without a message id.
143
- * The message may have been delivered, so the key is sealed:
144
- * a retry under it answers `IDEMPOTENCY_KEY_FAILED` rather than re-sending.
145
- */
146
- export declare const WHATSAPP_SEND_ERROR_CODES: readonly ["VALIDATION_FAILED", "IDEMPOTENCY_KEY_REQUIRED", "PLATFORM_INTEGRATION_DISABLED", "WHATSAPP_NOT_CONNECTED", "WHATSAPP_TOKEN_REJECTED", "CUSTOMER_NOT_FOUND", "WHATSAPP_RECIPIENT_PHONE_MISSING", "WHATSAPP_RECIPIENT_NOT_ALLOWED", "WHATSAPP_TEMPLATE_NOT_APPROVED", "WHATSAPP_RECIPIENT_OPTED_OUT", "WHATSAPP_RATE_LIMITED", "WHATSAPP_ACCOUNT_SUSPENDED", "WHATSAPP_SEND_FAILED", "WHATSAPP_SEND_UNCONFIRMED"];
147
- export type WhatsAppSendErrorCode = (typeof WHATSAPP_SEND_ERROR_CODES)[number];
148
- export {};
package/dist/whatsapp.js DELETED
@@ -1,67 +0,0 @@
1
- /**
2
- * Published WhatsApp surface — the tenant-facing connection config and the
3
- * tenant's own template send (`POST /whatsapp/send`).
4
- *
5
- * ⚠️ **Meta's inbound webhook payload is NOT here and must not come back.**
6
- * The twelve shapes Meta POSTs to `/whatsapp` — the entry/change/value
7
- * envelope, the contact, message, document, audio, image, status, error,
8
- * conversation and pricing leaves — live in
9
- * `./internal/whatsappWebhook.ts`, prefixed `WhatsAppWebhook*` and excluded
10
- * from the published build. They are inbound (the provider answering us), so
11
- * they fail `./internal/README.md`'s own test for what may be published, and
12
- * their bare names MERGED with globals in every consumer: a published
13
- * `interface Document` made `document.filename` compile and type as `string`
14
- * against `lib.dom`'s `Document`, returning `undefined` at runtime.
15
- */
16
- /**
17
- * `POST /whatsapp/send` refusals, beyond the cross-cutting `GATING_ERROR_CODES`
18
- * (notably `PLAN_LIMIT_EXCEEDED` for the `whatsappMessages` monthly cap).
19
- *
20
- * - `VALIDATION_FAILED` · 400 — including a body naming both or neither of
21
- * `to` / `customerId`.
22
- * - `IDEMPOTENCY_KEY_REQUIRED` · 400 — no `Idempotency-Key` header. The
23
- * idempotency wrapper's own codes (`INVALID_IDEMPOTENCY_KEY`,
24
- * `IDEMPOTENCY_KEY_REUSE`, `IDEMPOTENCY_IN_PROGRESS`, `IDEMPOTENCY_KEY_FAILED`,
25
- * `IDEMPOTENCY_LOCK_CONTENTION`) apply too. `IDEMPOTENCY_KEY_FAILED` (409)
26
- * means an earlier attempt under this key may have reached Meta: check the
27
- * recipient's chat before sending again under a NEW key.
28
- * - `PLATFORM_INTEGRATION_DISABLED` · 503 — WhatsApp is switched off
29
- * platform-wide by SINFACTURA.
30
- * - `WHATSAPP_NOT_CONNECTED` · 409 — the calling store has no connected
31
- * WhatsApp, or a connection with no stored token after a disconnect. Disable
32
- * the send control. ⚠️ Never falls back to the platform number.
33
- * - `WHATSAPP_TOKEN_REJECTED` · 409 — connected, but Meta refused the stored
34
- * token. Reconnect.
35
- * - `CUSTOMER_NOT_FOUND` · 404 / `WHATSAPP_RECIPIENT_PHONE_MISSING` · 400 — a bad
36
- * id versus a real customer with no usable number.
37
- * - `WHATSAPP_RECIPIENT_NOT_ALLOWED` · 403 — non-production stages only: the
38
- * number is not on that stage's test allowlist. Nothing was sent.
39
- * - `WHATSAPP_TEMPLATE_NOT_APPROVED` · 422 — not approved, paused, disabled,
40
- * wrong language or wrong parameter count (Meta does not tell these apart).
41
- * - `WHATSAPP_RECIPIENT_OPTED_OUT` · 409 — the recipient cannot receive it.
42
- * - `WHATSAPP_RATE_LIMITED` · 429 — throughput cap; retry later.
43
- * - `WHATSAPP_ACCOUNT_SUSPENDED` · 403 — the WhatsApp Business account or the
44
- * number itself is restricted.
45
- * - `WHATSAPP_SEND_FAILED` · 502 — any other Meta 4xx refusal; carries `metaCode`.
46
- * - `WHATSAPP_SEND_UNCONFIRMED` · 502 — the request reached Meta and its outcome
47
- * is unknown: no answer came back, Meta answered 5xx, or Meta accepted it
48
- * without a message id.
49
- * The message may have been delivered, so the key is sealed:
50
- * a retry under it answers `IDEMPOTENCY_KEY_FAILED` rather than re-sending.
51
- */
52
- export const WHATSAPP_SEND_ERROR_CODES = [
53
- 'VALIDATION_FAILED',
54
- 'IDEMPOTENCY_KEY_REQUIRED',
55
- 'PLATFORM_INTEGRATION_DISABLED',
56
- 'WHATSAPP_NOT_CONNECTED',
57
- 'WHATSAPP_TOKEN_REJECTED',
58
- 'CUSTOMER_NOT_FOUND',
59
- 'WHATSAPP_RECIPIENT_PHONE_MISSING',
60
- 'WHATSAPP_RECIPIENT_NOT_ALLOWED',
61
- 'WHATSAPP_TEMPLATE_NOT_APPROVED',
62
- 'WHATSAPP_RECIPIENT_OPTED_OUT',
63
- 'WHATSAPP_RATE_LIMITED',
64
- 'WHATSAPP_ACCOUNT_SUSPENDED',
65
- 'WHATSAPP_SEND_FAILED',
66
- 'WHATSAPP_SEND_UNCONFIRMED',
67
- ];