Add Scale plan ($29.99/mo, 25k SMS/mo, 15 devices) between Pro and
Custom in the subscription priority chain. Update dashboard upgrade
prompts to surface Scale for Pro users approaching their monthly limit,
and expose Scale upgrade links in subscription-info and account-settings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add deviceLimit to plans (default -1 = unlimited) with per-subscription
customDeviceLimit override, resolved in getEffectiveLimits and exposed
via the usage object. Gateway blocks device creation and disabled to
enabled transitions with 429 once the enabled-device count reaches the
limit; already-enabled devices are never affected and the check fails
open on lookup errors. Send a throttled device_limit_reached email
notification and show approaching/reached banners with an upgrade CTA
in the dashboard device list.
Also replace the isYearly checkout field with billingInterval
('monthly' | 'yearly') across DTO, service, and checkout page (legacy
?billing= param still accepted until the marketing site redeploys).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Order the polar products array from the isYearly flag so the chosen
interval is preselected at checkout, forward the ?billing= param from
the checkout page as isYearly, and only reuse cached checkout sessions
that match the requested plan and billing interval and are neither
completed nor abandoned.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pnpm v10 is now the npm "latest" tag but generates a different lockfile
format than v9.0 used in this repo, causing --frozen-lockfile to fail.
Replace corepack pnpm@latest with npm install -g pnpm@9 in api and web
Dockerfiles. Also bump docker/* actions to v3/v6 to resolve Node.js 20
deprecation warnings ahead of the June 2026 forced migration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add fourth_reminder to AbandonedEmailType and schema enum
- Replace expiry-based timing with createdAt-based timing so the
schedule is independent of Stripe session expiry windows
- Register all 6 emails in emailSchedule with correct delays:
10 min, 1 hr, 24 hr, 3 d, 7 d, 14 d after session creation
- Add isCompleted filter to query so paid users are never emailed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Complete rewrite of all 6 abandoned checkout templates for better
conversion and deliverability:
- Remove social media icons from all templates (promotions tab signal)
- Remove fake discounts, fabricated testimonials, and unverifiable
claims (money-back guarantee, Calendly link)
- Give each email a distinct purpose: recovery nudge (10min), feature
comparison table (1hr), cost objection handling (24hr), personal
founder message (3d), honest comparison + low pressure (7d),
graceful farewell + feedback ask (14d)
- Add opt-out notice to every email footer (required for marketing emails)
- Fix spam trigger subject '⏰ Your textbee pro upgrade is waiting!'
to 'Your TextBee checkout is still open'
- Standardise year to 2026 and brand to on-brand orange throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added `lastEnabledAt` property to track when a user re-enables a previously disabled webhook, preventing immediate auto-disable due to historical failures.
- Updated `WebhookService` to set `lastEnabledAt` when a webhook is re-enabled and adjusted the auto-disable logic to respect a grace period based on this new property.
- Updated device query in GatewayService to cast the filter object, addressing a type collision with the reserved `model` field in Mongoose 9.6. This change maintains the runtime behavior while ensuring type compatibility.
Mongoose 9 updateOne expects an ObjectId for _id; webhookSubscriptionId
was inferred as ObjectId | WebhookSubscription in CI, causing TS2769.
Use webhookSubscription._id after findById instead.
Made-with: Cursor
- Added a new method in AuthService to find active API keys using a masked match and fallback to regex.
- Updated OptionalAuthGuard and AuthGuard to utilize the new method for improved API key validation.
- Introduced an index on the apiKey field in the ApiKey schema for optimized query performance.
- Added a query parameter to filter API keys by status (active, revoked, all) in the getApiKey endpoint.
- Updated the AuthService to handle status filtering logic for API key retrieval.
- Modified the frontend to support status-based API key listing and added a button to view revoked keys.
- Implemented a check for the existence of the insertMany method in the SMS model to enhance flexibility.
- Added a fallback mechanism for models that do not support insertMany, allowing for individual document creation.
- Improved SMS document insertion process by maintaining performance while ensuring compatibility with various model types.
- Introduced batching for SMS document insertion to improve performance.
- Added metadata tracking for SMS to FCM message mapping.
- Implemented error handling for mismatched SMS records and queue payloads.
- Updated SMS queue service to support dynamic batch sizes and immediate queue delays.
- Refactored SMS status updates in SmsQueueProcessor to batch updates for failed and dispatched SMS records.
- Improved error handling by collecting failed SMS details and updating their status in a single operation.
- Updated the SMS queue registration to use asynchronous configuration with dynamic limits from the ConfigService.
- Updated getFcmErrorCode function to remove 'messaging/' prefix from error codes.
- Introduced getFcmErrorMessage function to provide actionable feedback for invalid device tokens.
- Enhanced error messages in SMS processing to utilize the new getFcmErrorMessage function for better clarity.
- Added `dispatchedAt` property to SMS schema and updated status options to include 'dispatched'.
- Implemented logic in SmsQueueProcessor to mark SMS as 'dispatched' upon successful FCM push.
- Enhanced error handling for SMS failures, including specific error codes for FCM delivery issues.
- Updated SmsStatusUpdateTask to handle both 'pending' and 'dispatched' statuses for timeout updates.