Skip to main content
A Subscription is the record that links a tenant to a plan. It tracks billing periods, payment status, trial dates, and seat counts. Every status change is recorded to an immutable event log.

Subscription Statuses

StatusDescription
pendingCreated but not yet activated (awaiting payment confirmation)
trialingIn a free trial period
activePaid and active
past_duePayment failed — grace period
canceledCanceled by tenant or admin
expiredTrial or subscription ended without renewal

Status Transitions

Transitions are triggered by Stripe webhook events:
EventTransition
checkout.session.completedpendingactive or trialing
customer.subscription.updatedtrialingactive
invoice.payment_failedactivepast_due
customer.subscription.deletedany → canceled

Subscription Fields

{
  "id": "sub_crovver_abc123",
  "status": "active",
  "tenantId": "ten_xyz",
  "planId": "plan_pro",
  "trialEndsAt": null,
  "currentPeriodStart": "2025-01-01T00:00:00Z",
  "currentPeriodEnd": "2025-02-01T00:00:00Z",
  "canceledAt": null,
  "capacityUnits": 15,
  "usedCapacity": 12,
  "providerSubscriptionId": "sub_stripe_...",
  "providerCustomerId": "cus_stripe_..."
}

Event Sourcing

Every subscription status change is written to subscription_events. This gives you a complete audit trail:
{
  "event_type": "subscription.activated",
  "previous_status": "trialing",
  "new_status": "active",
  "occurred_at": "2025-02-01T00:00:00Z",
  "metadata": {
    "stripe_event_id": "evt_...",
    "invoice_id": "in_..."
  }
}

Checking Subscription Status

Use the React SDK hook on the frontend:
import { useSubscription } from 'crovver-react';

function Dashboard() {
  const { isActive, plan, subscription } = useSubscription();

  if (!isActive) return <UpgradePrompt />;

  return <App trialEndsAt={subscription?.trialEndsAt} />;
}
Or query the API from your backend:
GET /api/public/subscriptions/status?publicKey=pk_live_...&tenantId=workspace_123