feat: Paid privacy opt-out, email thread entitlements, and settings cleanup#78
Conversation
…leanup Backend: - Add paid opt-out of training data (schema, enforcement in conversationLogs, Polar webhooks) - Add email thread metering: 10 free/month, auto-paywall email on 11th via preflightOutboundEmail - Integrate preflight checks into emailReply.ts and notes.ts - Add Polar webhook handlers for subscription lifecycle (create/update/cancel) - Auto-populate email from mentraUserId on user creation - Add backfillEmailsFromMentraId internal mutation for existing users - New email templates: OptOutCheckout, EmailThreadPaywall Frontend: - Settings: add Training Data card (sends opt-out email, no in-app checkout) - Remove all payment/billing/usage language from web app - Remove Email Threads card entirely (entitlement handled via email) - Type safety fixes across ChatPage, FollowupsPage, MemoryPage, QueuePage Screenshots: - Add iPhone 15 mobile screenshots for Home, Memory, Follow-ups, Queue, Settings Amp-Thread-ID: https://ampcode.com/threads/T-019c9d33-f47d-73fa-b739-2873a25a4d6f Co-authored-by: Amp <amp@ampcode.com>
|
@ajay-bhargava must be a member of the With Context team on Vercel to deploy. Learn more about collaboration on Vercel and other options here. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: edddb87f83
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| args: { mentraUserId: v.string() }, | ||
| handler: async (ctx, args) => { | ||
| const user = await ctx.runQuery(api.users.getByMentraId, { | ||
| mentraUserId: args.mentraUserId, |
There was a problem hiding this comment.
Bind opt-out checkout requests to the authenticated user
requestOptOutCheckoutEmail trusts a caller-provided mentraUserId and never checks ctx.auth or that the ID belongs to the caller, so any authenticated client can trigger paid opt-out checkout emails for another account if they know/guess that Mentra ID. This creates an account-level authorization gap (unsolicited billing emails and checkout links for other users) and should be restricted to the current authenticated user identity.
Useful? React with 👍 / 👎.
| if (used < FREE_EMAIL_THREAD_LIMIT) { | ||
| return { allowed: true as const, trackUsage: true }; |
There was a problem hiding this comment.
Enforce email quota atomically before allowing outbound sends
The entitlement gate returns allowed: true based on a read of current usage, but usage is only incremented later in separate mutations (notes.ts and emailReply.ts), so concurrent sends for the same user/period can both pass this check and exceed the 10-email free limit before paywalling. This race weakens billing enforcement and should be handled with an atomic check-and-increment path.
Useful? React with 👍 / 👎.
Summary
Backend
conversationLogs(skips logging when opted out), Polar webhook handlers for subscription lifecyclepreflightOutboundEmail— no in-app payment UIemailReply.tsandnotes.tsmentraUserIdon user creation +backfillEmailsFromMentraIdinternal mutation for existing usersOptOutCheckout,EmailThreadPaywallFrontend
Screenshots
Key Design Decision
All payment flows happen via email, not in the web app. Users never see pricing, usage counts, or checkout pages in the UI. When they exceed limits, the system sends an email with a checkout link automatically.