Skip to main content
DocsReferenceSubscription Plans
Back to docs

Subscription Plans

Free, Pro, and Premium plan features and limits

reference
subscription-plans

Subscription Plans Reference

Complete reference for Creatiq subscription plans, feature flags, rate limiting, usage tracking, and Stripe integration.

Plan Comparison

FreeProPremium
Price$0/month$4/month$8/month
Contents per month330Unlimited
AI generations per month5100Unlimited
Storage100 MB5 GB50 GB

Feature Flags

Each feature flag is gated by plan tier. The requireFeature middleware (server/middleware/feature-gate.ts) checks the user's current plan against the feature access map before allowing the request to proceed.

Feature Access by Plan

Feature FlagFreeProPremiumDescription
pdf-to-h5p--YesYesGenerate H5P content from PDF documents
video-to-h5p----YesGenerate H5P content from video files
image-hotspot--YesYesCreate image hotspot activities
url-to-h5p--YesYesGenerate H5P content from a URL
blooms-critique--YesYesBloom's Taxonomy critique for content
differentiation--YesYesContent differentiation by learner level
bulk-generation--YesYesGenerate multiple content items at once
lesson-bundle----YesBundle multiple activities into a lesson
branching-scenario----YesCreate branching scenario content
analytics-dashboard--YesYesAccess the analytics dashboard
ai-recommendation----YesAI-powered content recommendations
content-remixer--YesYesRemix existing content into new formats
lti-integration----YesLTI 1.3 platform integration
lti-ags----YesLTI Assignment and Grade Services
lti-nrps----YesLTI Names and Role Provisioning Services
cmi5-launch--YesYescmi5 content launch
whitelabel----YesWhite-label branding customization
scorm-export--YesYesExport content as SCORM packages

Summary by Plan

  • Free: No gated features. Basic H5P creation and playback only.
  • Pro (11 features): pdf-to-h5p, image-hotspot, url-to-h5p, blooms-critique, differentiation, bulk-generation, analytics-dashboard, content-remixer, cmi5-launch, scorm-export
  • Premium (all 17 features): Everything in Pro, plus video-to-h5p, lesson-bundle, branching-scenario, ai-recommendation, lti-integration, lti-ags, lti-nrps, whitelabel

Rate Limiting

Rate limits are defined in server/config/security.ts and applied via express-rate-limit. All tiers use a 15-minute sliding window.

TierMax Requests (per 15 min)Applied To
AUTH10/login, /refresh
AI50AI generation endpoints
API100General authenticated API endpoints
PUBLIC200Public/unauthenticated endpoints

When E2E_TEST_CODE is set, all tiers are raised to 10,000 requests per window.

Rate limit responses use standard headers (RateLimit-*) and return:

{
  "success": false,
  "error": "Too many requests. Please try again later."
}

Usage Tracking

Usage counters are tracked per user in the creatiq_users table. Counters reset automatically on a monthly basis (checked on each user lookup based on month_reset_date).

Tracked Metrics

MetricColumnReset Period
Contents createdcontents_created_this_monthMonthly
AI generationsai_generations_this_monthMonthly
Storage usedstorage_used (bytes)Cumulative (no reset)

Limit Enforcement

Before performing a gated action, the subscription service checks usage via checkLimit(userId, action):

  • Content creation: Blocked when contentsCreatedThisMonth >= plan.contentsPerMonth (unless limit is -1 for unlimited).
  • AI generation: Uses atomic check-and-increment (UPDATE ... WHERE ai_generations_this_month < limit) to prevent race conditions under concurrent requests.
  • Storage: Tracked cumulatively. The limit is defined per plan but enforcement is at the application level.

A limit of -1 means unlimited (Premium plan for both contents and AI generations).

Usage Stats API

GET /auth/me returns the current user's usage alongside plan details:

{
  "success": true,
  "data": {
    "user": { "id": "...", "email": "...", "name": "...", "plan": "pro" },
    "usage": {
      "plan": "pro",
      "contentsUsed": 12,
      "contentsLimit": 30,
      "aiUsed": 45,
      "aiLimit": 100,
      "storageUsed": 52428800,
      "storageLimit": 5368709120
    },
    "planDetails": {
      "id": "pro",
      "name": "Pro",
      "price": 4,
      "limits": {
        "contentsPerMonth": 30,
        "aiGenerationsPerMonth": 100,
        "storageBytes": 5368709120
      }
    }
  }
}

Stripe Integration

Stripe handles subscription billing. It is initialized only when STRIPE_SECRET_KEY is set; otherwise payment features are disabled and a warning is logged.

Required Environment Variables

VariablePurpose
STRIPE_SECRET_KEYStripe API authentication
STRIPE_WEBHOOK_SECRETWebhook signature verification
STRIPE_PRO_PRICE_IDStripe Price ID for Pro plan ($4/month)
STRIPE_PREMIUM_PRICE_IDStripe Price ID for Premium plan ($8/month)

API Endpoints

MethodPathAuthDescription
GET/auth/plansNoList all plans with limits and pricing
POST/auth/checkoutYesCreate a Stripe Checkout session for plan upgrade
POST/auth/billing-portalYesCreate a Stripe Billing Portal session for subscription management
POST/auth/webhookStripe signatureHandle Stripe webhook events

Checkout Flow

  1. User calls POST /auth/checkout with { "planId": "pro" } or { "planId": "premium" }.
  2. The service creates (or reuses) a Stripe Customer tied to the user.
  3. A Stripe Checkout Session is created in subscription mode with the plan's Price ID.
  4. The checkout session URL is returned. The user is redirected to Stripe.
  5. On success, Stripe redirects to NEXT_PUBLIC_APP_URL/workspace?subscription=success.
  6. On cancel, Stripe redirects to NEXT_PUBLIC_APP_URL/workspace?subscription=cancelled.

Webhook Events Handled

EventAction
checkout.session.completedUpdates user's plan and stores stripe_subscription_id
customer.subscription.deletedDowngrades user to free plan and clears subscription ID
invoice.payment_failedLogs a warning (no automatic downgrade)

Webhook idempotency is enforced via Redis: each event ID is stored with a 24-hour TTL (webhook:<event_id>). Duplicate events receive a 200 response with { "duplicate": true }.

Stripe API Version

The Stripe client is initialized with API version 2025-12-15.clover.


JIT Plan Assignment on Login

When a user authenticates via Keycloak, their Creatiq plan is determined by their Keycloak role:

Keycloak RoleAssigned Plan
adminpremium
teacherpro
All othersfree

This assignment only applies when the user record is first created (JIT provisioning). Subsequent Stripe upgrades take precedence.


Database Schema

The creatiq_users table stores subscription and usage data:

ColumnTypeDefaultDescription
idUUIDgen_random_uuid()Primary key
emailVARCHAR(255)--Unique, lowercased
nameVARCHAR(255)--Display name
planVARCHAR(20)'free'Current plan (free, pro, premium)
stripe_customer_idVARCHAR(255)NULLStripe Customer ID
stripe_subscription_idVARCHAR(255)NULLActive Stripe Subscription ID
contents_created_this_monthINTEGER0Monthly content counter
ai_generations_this_monthINTEGER0Monthly AI generation counter
storage_usedBIGINT0Total storage in bytes
month_reset_dateDATECURRENT_DATEDate of last monthly counter reset
external_user_idVARCHAR(255)NULLKeycloak sub claim
tenant_idUUIDNULLForeign key to tenants(id)
created_atTIMESTAMPCURRENT_TIMESTAMPRecord creation time
updated_atTIMESTAMPCURRENT_TIMESTAMPLast update time

Indexes

IndexColumnsCondition
idx_creatiq_users_emailemail--
idx_creatiq_users_stripe_customerstripe_customer_id--
idx_creatiq_users_tenant_external (unique)tenant_id, external_user_idWHERE tenant_id IS NOT NULL AND external_user_id IS NOT NULL
idx_creatiq_users_external_user_idexternal_user_idWHERE external_user_id IS NOT NULL
Back to docsdocs/product/reference/subscription-plans.md