feat(subscriptions): bring subscriptions to the free tier#59580
Closed
vdekrijger wants to merge 5 commits into
Closed
feat(subscriptions): bring subscriptions to the free tier#59580vdekrijger wants to merge 5 commits into
vdekrijger wants to merge 5 commits into
Conversation
47287b3 to
0c25637
Compare
…synced to alerts) Free orgs get 5 subscriptions (synced live to the alert free-tier limit), then the existing paywall on the next create; viewing/editing/deleting existing subscriptions is never gated; paid orgs stay unlimited. - Subscription.check_subscription_limit mirrors AlertConfiguration.check_alert_limit - POST-only enforcement in SubscriptionSerializer.validate - remove PremiumFeaturePermission from both subscription viewsets - frontend: subscriptionCountLogic + FreeTierCreateGate in EditSubscription - remove wholesale PayGateMini wrappers; unhide insight menu item
…uto-login + count/feature mocks)
…te gate The free-tier create gate rendered the generic PayGateMini 'Upgrade to use this feature', implying subscriptions are paid-only. They are freemium with a 5-subscription cap, so swap in UsageLimitPaywall with 'Subscription limit reached' framing showing the limit and current usage. Update the e2e to anchor on the new title instead of the PayGateMini learn-more testid.
AI summary generation called the LLM with no budget check. Adopt the chat assistant's pattern (ee/api/conversation.py): is_team_limited(AI_CREDITS) via the billing quota-limiting cache. Enable-time gate in the serializer raises QuotaLimitExceeded when toggling a summary on while over budget; generation-time gate in the snapshot activity skips the summary and delivers the rest (graceful degradation) so an org over budget stops consuming credits mid-month.
0c25637 to
835003b
Compare
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Insight & dashboard subscriptions were a hard paid feature — free-tier orgs were blocked entirely (a
PremiumFeaturePermissiongate on the viewsets and a full-pagePayGateMinion the UI). That's a missed adoption/stickiness opportunity: free users can't experience the feature before deciding to upgrade.Changes
Make subscriptions freemium, mirroring how alerts already work: free orgs get a small allowance, then hit the existing paywall.
AlertConfiguration.ALERTS_ALLOWED_ON_FREE_TIER) so the two never drift.SUBSCRIPTIONSentitlement carries no numeric limit → unlimited), unchanged.Backend:
Subscription.check_subscription_limit()— a near-exact mirror ofAlertConfiguration.check_alert_limit(reads the billing entitlement limit, falls back to the free-tier constant). Countsdeleted=Falserows.SubscriptionSerializer.validate()onPOSTonly (edits via PATCH are never blocked).PremiumFeaturePermissionfromSubscriptionViewSetandSubscriptionDeliveryViewSetso free orgs can list/read.Frontend:
subscriptionCountLogic— lazily loads the team-wide count via the generated API (limit=1, readscount).EditSubscriptioncreate mode (id === 'new'). AFreeTierCreateGateshows the form while under the limit and the existingPayGateMiniupsell at/over it (isFreeTierCreateAtLimit). Fail-open while the count is loading — the backend is the hard guarantee.PayGateMiniwrappers (list scene, single scene, subscribe modal) and unhid the insight side-panel "Subscription" menu item for free users.No DB migration, no schema/serializer-field changes (so no generated-type churn).
Screenshots (Storybook, real components)
(Editing an existing subscription and managing the list render normally for free orgs — no wholesale paywall.)
How did you test this code?
This PR was authored by an agent (see Agent context). No manual click-testing is claimed — below are the automated tests actually run, all green:
ee/api/test/test_subscription.py,posthog/models/test/test_subscription_model.py) — real requests through the full Django stack against Postgres: free org creates 5 → 6th returns400under thesubscriptionkey; PATCH at the limit returns200; soft-delete frees a slot; paid org unlimited; free orgs can list/read;check_subscription_limitparameterized over free/paid/numeric-limit/zero/soft-deleted. 152 passed.jest,src/lib/components/Subscriptions,src/scenes/subscriptions) —isFreeTierCreateAtLimitboundary cases, count-loader (countfield +limit=1invariant + fallback), gate wiring. 63 passed.playwright/e2e/subscriptions-freemium.spec.ts) — drove the real frontend against a running stack:The e2e mocks the org entitlement + team subscription count (the gate is a pure frontend decision:
!hasSubscriptionsFeature && count >= FREE_LIMIT); the hard backend limit is covered by the backend tests.Publish to changelog?
Yes — "Subscriptions are now available on the free tier (up to 5, then upgrade for unlimited)."
Docs update
Subscriptions docs may want a note that the free tier includes up to 5 subscriptions.
🤖 Agent context
Authored with Claude Code via a proof-driven-dev workflow (spec → testable criteria matrix → plan → TDD task-by-task with per-task spec + code-quality review → verification report + rerunnable
scripts/verify-subscriptions-free-tier.sh). Hardened with a local multi-reviewer pass; the one substantive finding (a tautological frontend constant test) was fixed by extracting the gate decision into the testedisFreeTierCreateAtLimitpredicate.Key decisions:
EditSubscriptioncreate mode only — which also covers every create entry point in one place.POSTcheck is the authoritative limit.Agent-authored; requires human review. Verified via the automated tests listed above (including a live browser e2e run against a local stack).