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)
trialIn a free trial period
activePaid and active
past_duePayment failed — grace period
pending_cancelCancellation requested; access continues until currentPeriodEnd
canceledCanceled. For manual billing subscriptions, this is set immediately on cancellation; for recurring subscriptions, it is set at period end.
expiredTrial or subscription ended without renewal

Status Transitions

Transitions are triggered by Stripe webhook events or explicit API calls:
Event / ActionTransition
checkout.session.completedpendingactive or trial
customer.subscription.updatedtrialactive
invoice.payment_failedactivepast_due
customer.subscription.deletedany → canceled
POST /subscriptions/{id}/cancel (recurring)active / trial / past_duepending_cancel
POST /subscriptions/{id}/cancel (manual billing)active / trial / past_duecanceled

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": "trial",
  "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