> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/calcom/cal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Billing & Licensing

> Manage organization billing, seats, and enterprise licensing

Cal.com Enterprise billing provides flexible subscription management for organizations, including seat-based pricing, usage tracking, and payment processing through Stripe.

## Overview

Enterprise billing features:

* **Seat-based Pricing**: Pay per active user in your organization
* **Flexible Billing Periods**: Monthly or annual subscriptions
* **Trial Periods**: Optional trial before payment
* **Usage Tracking**: Monitor booking usage and seat utilization
* **Self-Service**: Manage seats and billing independently
* **Custom Pricing**: Enterprise pricing for large deployments

## License Requirements

### Enterprise License Key

To enable Enterprise features, obtain a license key:

```bash .env theme={null}
# Required: Cal.com Enterprise license key
CALCOM_LICENSE_KEY=your_license_key

# Required: Signature token for license API
CAL_SIGNATURE_TOKEN=your_signature_token

# Optional: License API route (default shown)
CALCOM_PRIVATE_API_ROUTE=https://goblin.cal.com
```

Source: `.env.example:21-26`

**To obtain a license:**

1. Visit [cal.com/sales](https://cal.com/sales)
2. Complete the Enterprise inquiry form
3. Receive your `CALCOM_LICENSE_KEY` and `CAL_SIGNATURE_TOKEN`
4. Add to your `.env` file

### License Setup

For new deployments without a license:

1. Navigate to `/auth/setup` as admin
2. Select your license type
3. Enter license key and signature token
4. Complete setup wizard

Source: `.env.example:19`

## Billing Configuration

### Environment Variables

Configure Stripe billing integration:

```bash .env theme={null}
# Stripe API Keys
STRIPE_PRIVATE_KEY=sk_test_xxx
STRIPE_CLIENT_ID=ca_xxx

# Organization Products
STRIPE_ORG_PRODUCT_ID=prod_xxx
STRIPE_ORG_MONTHLY_PRICE_ID=price_xxx_monthly
STRIPE_ORG_ANNUAL_PRICE_ID=price_xxx_annual

# Optional: Trial period in days
STRIPE_ORG_TRIAL_DAYS=14

# Optional: Minimum seats for self-serve (default: 30)
NEXT_PUBLIC_ORGANIZATIONS_MIN_SELF_SERVE_SEATS=30

# Optional: Price per seat (default: $37)
NEXT_PUBLIC_ORGANIZATIONS_SELF_SERVE_PRICE_NEW=37

# Monthly credits allocation for organizations
ORG_MONTHLY_CREDITS=1000

# Webhook secrets for payment events
STRIPE_WEBHOOK_SECRET=whsec_xxx
STRIPE_WEBHOOK_SECRET_BILLING=whsec_xxx_billing
```

Source: `.env.example:206-221`, `.env.example:319-321`, `.env.example:418`

## Organization Billing Flow

### 1. Organization Creation

When creating an organization, billing details are collected:

```typescript theme={null}
const onboardingInput = {
  name: "Acme Corp",
  slug: "acme",
  orgOwnerEmail: "owner@acme.com",
  billingPeriod: "MONTHLY", // or "ANNUALLY"
  seats: 50,
  pricePerSeat: 37, // USD
};

const organizationPaymentService = new OrganizationPaymentService(user);
const onboarding = await organizationPaymentService
  .createOrganizationOnboarding(onboardingInput);
```

Source: `packages/features/ee/organizations/lib/OrganizationPaymentService.ts:174-206`

### 2. Stripe Customer Creation

A Stripe customer is created or retrieved:

```typescript theme={null}
// Check for existing Stripe customer
const existingCustomer = await prisma.user.findUnique({
  where: { email: orgOwnerEmail },
  select: { metadata: true },
});

const stripeCustomerId = existingCustomer?.metadata?.stripeCustomerId
  ? existingCustomer.metadata.stripeCustomerId
  : await billingService.createCustomer({
      email: orgOwnerEmail,
      metadata: { email: orgOwnerEmail },
    });
```

Source: `packages/features/ee/organizations/lib/OrganizationPaymentService.ts:88-123`

### 3. Subscription Checkout

A Stripe Checkout session is created:

```typescript theme={null}
const subscription = await billingService.createSubscriptionCheckout({
  customerId: stripeCustomerId,
  priceId: stripePriceId,
  quantity: seats,
  successUrl: `${WEBAPP_URL}/api/organizations/payment-redirect?session_id={CHECKOUT_SESSION_ID}&paymentStatus=success`,
  cancelUrl: `${WEBAPP_URL}/api/organizations/payment-redirect?session_id={CHECKOUT_SESSION_ID}&paymentStatus=failed`,
  metadata: {
    organizationOnboardingId: onboarding.id,
    seats,
    pricePerSeat,
    billingPeriod,
  },
  subscriptionData: {
    trial_period_days: ORG_TRIAL_DAYS, // Optional
    metadata: { source: "onboarding" },
  },
});

// Redirect user to: subscription.checkoutUrl
```

Source: `packages/features/ee/organizations/lib/OrganizationPaymentService.ts:270-308`

### 4. Payment Confirmation

After successful payment:

1. Stripe webhook confirms payment
2. Organization is activated
3. Users are provisioned
4. Subscription is active

## Pricing Models

### Standard Pricing

**Monthly:**

* \$37 per seat per month
* Minimum 30 seats (configurable)
* Billed monthly

**Annual:**

* \$37 per seat per month (billed annually)
* Minimum 30 seats (configurable)
* 12-month commitment

Source: `.env.example:320-321`

### Custom Pricing

Admins can configure custom pricing:

```typescript theme={null}
const customOnboarding = {
  ...onboardingInput,
  pricePerSeat: 29, // Custom price
  seats: 100,
  billingPeriod: "ANNUALLY",
};

// Requires admin permission
if (user.role !== UserPermissionRole.ADMIN) {
  // Permission check fails
  throw new ErrorWithCode(
    ErrorCode.Unauthorized,
    "You do not have permission to modify the default payment settings"
  );
}
```

Source: `packages/features/ee/organizations/lib/OrganizationPaymentService.ts:175-183`

### Custom Stripe Prices

For non-standard pricing, custom Stripe prices are created:

```typescript theme={null}
const customPrice = await billingService.createPrice({
  amount: pricePerSeat * 100 * occurrence, // cents * months
  productId: process.env.STRIPE_ORG_PRODUCT_ID,
  currency: "usd",
  interval: billingPeriod === "MONTHLY" ? "month" : "year",
  nickname: `Custom Organization Price - $${pricePerSeat} per seat`,
  metadata: {
    organizationOnboardingId: onboarding.id,
    pricePerSeat,
    billingPeriod,
    createdAt: new Date().toISOString(),
  },
});
```

Source: `packages/features/ee/organizations/lib/OrganizationPaymentService.ts:244-268`

## Seat Management

### Automatic Seat Calculation

Seats are automatically calculated based on:

1. **Existing Team Members**: Unique members across migrated teams
2. **Invited Members**: New users being added during onboarding
3. **Minimum Requirements**: Configured minimum seats

```typescript theme={null}
// Get unique members from teams
const memberships = await prisma.membership.findMany({
  where: { teamId: { in: teamIds } },
  select: { user: { select: { email: true } } },
  distinct: ["userId"],
});

const emailsSet = new Set(memberships.map(m => m.user.email));

// Add invited members
invitedMembers.forEach(member => emailsSet.add(member.email));

const uniqueMembersCount = emailsSet.size;

// Ensure meets minimum
const seats = Math.max(configuredSeats, uniqueMembersCount);
```

Source: `packages/features/ee/organizations/lib/OrganizationPaymentService.ts:137-172`, `.ts:358-365`

### Adding Seats

To add seats to an existing subscription:

1. Navigate to Organization Settings → Billing
2. Click "Manage Subscription"
3. Update seat quantity
4. Confirm changes

**API Approach:**

```typescript theme={null}
// Update subscription quantity
await stripe.subscriptions.update(subscriptionId, {
  items: [{
    id: subscriptionItemId,
    quantity: newSeatCount,
  }],
  proration_behavior: "always_invoice", // Immediate proration
});
```

### Seat Utilization

Monitor seat usage:

* **Active Seats**: Currently occupied by users
* **Available Seats**: Purchased but unassigned
* **Utilization Rate**: Active / Total seats

Recommended utilization: 80-90% for cost efficiency

## Usage Tracking

### Booking Usage Increment

Organization bookings are tracked for billing:

```typescript theme={null}
type PlatformOrganizationBillingTaskPayload = {
  bookingUid: string;
  organizationId: number;
  eventTypeId: number;
  scheduledTime: Date;
  // Additional metadata
};

// Increment usage when booking is created
await billingTasker.incrementUsage({
  bookingUid: booking.uid,
  organizationId: organization.id,
  eventTypeId: eventType.id,
  scheduledTime: booking.startTime,
});
```

Source: `packages/features/ee/organizations/lib/billing/tasker/types.ts:9-26`

### Usage Cancellation

Cancel usage increment when booking is cancelled:

```typescript theme={null}
await billingTasker.cancelUsageIncrement({
  bookingUid: booking.uid,
});
```

Source: `packages/features/ee/organizations/lib/billing/tasker/types.ts:17`

### Usage Rescheduling

Adjust usage tracking when booking is rescheduled:

```typescript theme={null}
await billingTasker.rescheduleUsageIncrement({
  bookingUid: booking.uid,
  rescheduledTime: newStartTime,
});
```

Source: `packages/features/ee/organizations/lib/billing/tasker/types.ts:19-25`

## Billing Periods

### Monthly Billing

```typescript theme={null}
const monthlyConfig = {
  billingPeriod: "MONTHLY" as BillingPeriod,
  seats: 50,
  pricePerSeat: 37,
};

// Total: 50 seats × $37 = $1,850/month
```

### Annual Billing

```typescript theme={null}
const annualConfig = {
  billingPeriod: "ANNUALLY" as BillingPeriod,
  seats: 50,
  pricePerSeat: 37,
};

// Total: 50 seats × $37 × 12 months = $22,200/year
// (Paid upfront)
```

Source: `packages/features/ee/organizations/lib/OrganizationPaymentService.ts:125-135`

## Trial Periods

Configure trial periods for new organizations:

```bash .env theme={null}
# Trial period in days (optional)
STRIPE_ORG_TRIAL_DAYS=14
```

**Trial behavior:**

* Full access to all features during trial
* No payment required to start trial
* Automatic conversion to paid after trial
* Cancel anytime during trial with no charge

Source: `.env.example:221`, `OrganizationPaymentService.ts:288`

## Admin Billing Bypass

Admins can create organizations without payment:

```typescript theme={null}
if (user.role === UserPermissionRole.ADMIN) {
  // Skip Stripe checkout
  return {
    organizationOnboarding,
    subscription: null,
    checkoutUrl: null,
    sessionId: null,
  };
}
```

Source: `packages/features/ee/organizations/lib/OrganizationPaymentService.ts:327-335`

This allows:

* Internal testing
* Special partnerships
* Custom billing arrangements

## Webhook Configuration

### Stripe Webhooks

Configure webhooks to receive billing events:

```bash .env theme={null}
# General webhook secret
STRIPE_WEBHOOK_SECRET=whsec_xxx

# Billing-specific webhook secret
STRIPE_WEBHOOK_SECRET_BILLING=whsec_xxx_billing

# App-specific webhook secret
STRIPE_WEBHOOK_SECRET_APPS=whsec_xxx_apps
```

Source: `.env.example:223-224`, `.env.example:418`

### Webhook Events

Handle the following Stripe events:

| Event                           | Action                                |
| ------------------------------- | ------------------------------------- |
| `checkout.session.completed`    | Activate organization subscription    |
| `customer.subscription.updated` | Update seat count and billing details |
| `customer.subscription.deleted` | Suspend organization access           |
| `invoice.payment_succeeded`     | Confirm payment and extend period     |
| `invoice.payment_failed`        | Notify admin and suspend if needed    |

### Webhook Endpoints

```
General: /api/integrations/stripepayment/webhook
Billing: /api/organizations/billing/webhook
Apps: /api/app-store/stripepayment/webhook
```

## Organization Billing Repository

Access billing data programmatically:

```typescript theme={null}
import { OrganizationBilling } from "@calcom/features/ee/billing/organizations";

class InternalOrganizationBilling extends OrganizationBilling {
  async getStripeCustomerId(): Promise<string | null> {
    // Retrieve customer ID from database
    return this.repository.getStripeCustomerId(this.organization.id);
  }

  async getSubscriptionId(): Promise<string | null> {
    // Retrieve subscription ID
    return this.repository.getSubscriptionId(this.organization.id);
  }

  async getSubscriptionItems(): Promise<{ id: string; quantity: number }[]> {
    // Get subscription line items
    return this.repository.getSubscriptionItems(this.organization.id);
  }
}
```

Source: `packages/features/ee/billing/organizations/organization-billing.ts:5-16`

## Troubleshooting

### Payment Failed

**Solutions:**

1. Verify Stripe API keys are correct
2. Check webhook secrets match Stripe dashboard
3. Ensure Stripe account is not in test mode (production)
4. Verify customer payment method is valid
5. Check for declined transactions in Stripe dashboard

### Missing Stripe Configuration

**Error**: "STRIPE\_ORG\_PRODUCT\_ID is not set"

**Solution**: Configure all required Stripe environment variables:

```bash theme={null}
STRIPE_PRIVATE_KEY=sk_xxx
STRIPE_ORG_PRODUCT_ID=prod_xxx
STRIPE_ORG_MONTHLY_PRICE_ID=price_xxx
STRIPE_ORG_ANNUAL_PRICE_ID=price_xxx
```

Source: `packages/features/ee/organizations/lib/OrganizationPaymentService.ts:222-235`

### Unauthorized Custom Pricing

**Error**: "You do not have permission to modify the default payment settings"

**Solution**: Only admins can set custom pricing. Either:

1. Use standard pricing (\$37/seat)
2. Request admin to create organization with custom pricing
3. Upgrade user to admin role

Source: `packages/features/ee/organizations/lib/OrganizationPaymentService.ts:179-183`

### Webhook Not Receiving Events

**Solutions:**

1. Verify webhook endpoint is publicly accessible
2. Check webhook secret matches Stripe configuration
3. Test webhook endpoint in Stripe dashboard
4. Review webhook logs for errors
5. Ensure HTTPS is enabled (required by Stripe)

## Best Practices

1. **Monitor Seat Utilization**: Keep utilization at 80-90% for cost efficiency
2. **Use Annual Billing**: Save \~8% with annual vs monthly (typically)
3. **Enable Trial Periods**: Let users experience full platform before payment
4. **Set Up Webhooks**: Ensure reliable payment event handling
5. **Track Usage**: Monitor booking usage to optimize seat allocation
6. **Regular Audits**: Review active users and remove inactive seats
7. **Test in Staging**: Always test billing flow in Stripe test mode first

## API Reference

### OrganizationPaymentService

```typescript theme={null}
class OrganizationPaymentService {
  // Create organization onboarding
  async createOrganizationOnboarding(
    input: CreateOnboardingInput
  ): Promise<OrganizationOnboarding>;

  // Create payment intent and Stripe checkout
  async createPaymentIntent(
    input: CreatePaymentIntentInput,
    organizationOnboarding: OrganizationOnboarding
  ): Promise<{
    organizationOnboarding: OrganizationOnboarding;
    subscription: StripeSubscription | null;
    checkoutUrl: string | null;
    sessionId: string | null;
  }>;
}
```

Source: `packages/features/ee/organizations/lib/OrganizationPaymentService.ts:77-417`

## Related Documentation

* [Organizations Setup](/enterprise/organizations)
* [SSO Configuration](/enterprise/sso)
* [SAML Setup](/enterprise/saml)
* [Stripe Documentation](https://stripe.com/docs)
* [Cal.com Sales](https://cal.com/sales)
