Hybrid Pricing Models
Combine multiple pricing strategies for flexible monetization
Hybrid pricing combines multiple monetization strategies to balance predictable revenue with usage-based scaling. This approach captures value from different customer segments while providing flexibility for varying usage patterns.
When to Use Hybrid Pricing
Best for:
- Variable usage patterns across customers
- Services with fixed + variable costs
- Market with diverse customer segments
- Complex value propositions
- Transitioning between pricing models
Not ideal for:
- Simple, single-dimension services
- Customers preferring straightforward pricing
- Early-stage MVPs
- Highly commoditized offerings
Core Patterns
Pattern 1: Base Fee + Usage
Combine recurring subscription with usage charges:
import $, { db, on, send } from 'sdk.do'
const messagingService = await $.Service.create({
name: 'Business Messaging Platform',
type: $.ServiceType.Messaging,
pricing: {
model: 'base-plus-usage',
plans: [
{
id: 'starter',
name: 'Starter',
baseFee: 49.0, // $49/month base
included: {
messages: 10000, // 10k messages included
users: 5,
channels: 10,
},
usage: {
messages: 0.005, // $0.005 per message over limit
users: 10.0, // $10 per additional user
channels: 5.0, // $5 per additional channel
},
features: ['basic-support', 'standard-api'],
},
{
id: 'professional',
name: 'Professional',
baseFee: 149.0, // $149/month base
included: {
messages: 50000, // 50k messages included
users: 25,
channels: 50,
},
usage: {
messages: 0.003, // $0.003 per message (better rate)
users: 8.0, // $8 per additional user
channels: 3.0, // $3 per additional channel
},
features: ['priority-support', 'advanced-api', 'analytics'],
},
{
id: 'enterprise',
name: 'Enterprise',
baseFee: 499.0, // $499/month base
included: {
messages: 250000, // 250k messages included
users: 100,
channels: 200,
},
usage: {
messages: 0.002, // $0.002 per message (best rate)
users: 6.0, // $6 per additional user
channels: 2.0, // $2 per additional channel
},
features: ['dedicated-support', 'enterprise-api', 'custom-integrations', 'sla'],
},
],
},
})
on($.Subscription.create, async (subscriptionRequest) => {
const plan = messagingService.pricing.plans.find((p) => p.id === subscriptionRequest.planId)
// Create subscription
const subscription = await db.create($.Subscription, {
customerId: subscriptionRequest.customerId,
serviceId: messagingService.id,
planId: plan.id,
status: 'active',
baseFee: plan.baseFee,
included: plan.included,
usageRates: plan.usage,
currentPeriodStart: new Date(),
currentPeriodEnd: addMonths(new Date(), 1),
})
// Charge base fee
await send($.Payment.charge, {
customerId: subscriptionRequest.customerId,
amount: plan.baseFee,
description: `${plan.name} - Monthly Base Fee`,
subscriptionId: subscription.id,
})
return subscription
})
// Track usage during the month
on($.Message.sent, async (message) => {
const subscription = await db.findOne($.Subscription, {
customerId: message.customerId,
serviceId: messagingService.id,
status: 'active',
})
if (!subscription) return
// Record usage
await db.create($.UsageRecord, {
subscriptionId: subscription.id,
customerId: message.customerId,
type: 'message',
units: 1,
timestamp: new Date(),
metadata: {
messageId: message.id,
channel: message.channel,
},
})
})
// End-of-month billing for overage
on($.BillingCycle.ended, async (cycle) => {
const subscriptions = await db.query($.Subscription, {
where: {
serviceId: messagingService.id,
status: 'active',
},
})
for (const subscription of subscriptions) {
const plan = messagingService.pricing.plans.find((p) => p.id === subscription.planId)
// Get usage for the period
const usage = await db.query($.UsageRecord, {
where: {
subscriptionId: subscription.id,
timestamp: { gte: cycle.start, lte: cycle.end },
},
})
// Calculate overage charges
const usageSummary = {
messages: usage.filter((u) => u.type === 'message').length,
users: await getCurrentUserCount(subscription.id),
channels: await getCurrentChannelCount(subscription.id),
}
let overageTotal = 0
const overageItems = []
// Message overage
if (usageSummary.messages > plan.included.messages) {
const overage = usageSummary.messages - plan.included.messages
const cost = overage * plan.usage.messages
overageTotal += cost
overageItems.push({
description: `Message Overage - ${overage.toLocaleString()} messages`,
quantity: overage,
rate: plan.usage.messages,
amount: cost,
})
}
// User overage
if (usageSummary.users > plan.included.users) {
const overage = usageSummary.users - plan.included.users
const cost = overage * plan.usage.users
overageTotal += cost
overageItems.push({
description: `Additional Users - ${overage} users`,
quantity: overage,
rate: plan.usage.users,
amount: cost,
})
}
// Channel overage
if (usageSummary.channels > plan.included.channels) {
const overage = usageSummary.channels - plan.included.channels
const cost = overage * plan.usage.channels
overageTotal += cost
overageItems.push({
description: `Additional Channels - ${overage} channels`,
quantity: overage,
rate: plan.usage.channels,
amount: cost,
})
}
// Create invoice if there are overages
if (overageTotal > 0) {
await send($.Invoice.create, {
customerId: subscription.customerId,
subscriptionId: subscription.id,
period: { start: cycle.start, end: cycle.end },
lineItems: overageItems,
total: overageTotal,
})
}
// Renew subscription base fee for next month
await send($.Payment.charge, {
customerId: subscription.customerId,
amount: plan.baseFee,
description: `${plan.name} - Monthly Base Fee`,
subscriptionId: subscription.id,
})
// Update period
await db.update(subscription, {
currentPeriodStart: cycle.end,
currentPeriodEnd: addMonths(cycle.end, 1),
})
}
})
async function getCurrentUserCount(subscriptionId: string): Promise<number> {
const users = await db.query($.User, {
where: { subscriptionId, status: 'active' },
count: true,
})
return users.count
}
async function getCurrentChannelCount(subscriptionId: string): Promise<number> {
const channels = await db.query($.Channel, {
where: { subscriptionId, status: 'active' },
count: true,
})
return channels.count
}
function addMonths(date: Date, months: number): Date {
const result = new Date(date)
result.setMonth(result.getMonth() + months)
return result
}Example Billing:
- Starter ($49) + 15k messages (5k overage @ $0.005) = $74/month
- Professional ($149) + 75k messages (25k overage @ $0.003) = $224/month
- Enterprise ($499) + 300k messages (50k overage @ $0.002) = $599/month
Pattern 2: Credit System (Prepaid + Overage)
Customers buy credits upfront, with flexible overage options:
const aiProcessingService = await $.Service.create({
name: 'AI Processing Platform',
type: $.ServiceType.AIProcessing,
pricing: {
model: 'credit-system',
creditPackages: [
{
id: 'starter',
name: 'Starter Credits',
credits: 100,
price: 20.0, // $0.20 per credit
bonus: 0,
expiry: 90, // 90 days
},
{
id: 'professional',
name: 'Professional Credits',
credits: 500,
price: 85.0, // $0.17 per credit (15% discount)
bonus: 50, // 50 bonus credits
expiry: 180,
},
{
id: 'enterprise',
name: 'Enterprise Credits',
credits: 2000,
price: 300.0, // $0.15 per credit (25% discount)
bonus: 300, // 300 bonus credits
expiry: 365,
},
],
usage: {
'image-generation': {
cost: 2, // 2 credits per image
quality: {
standard: 1.0,
hd: 2.0,
ultra: 4.0,
},
},
'text-generation': {
cost: 0.1, // 0.1 credits per 100 words
},
'audio-transcription': {
cost: 1, // 1 credit per minute
},
'video-analysis': {
cost: 10, // 10 credits per minute
},
},
overage: {
enabled: true,
rate: 0.25, // $0.25 per credit (25% premium over base)
autoRecharge: true,
minimumRecharge: 50, // Minimum 50 credits
},
},
})
on($.CreditPackage.purchase, async (purchase) => {
const pkg = aiProcessingService.pricing.creditPackages.find((p) => p.id === purchase.packageId)
// Charge for credit package
await send($.Payment.charge, {
customerId: purchase.customerId,
amount: pkg.price,
description: `${pkg.name} - ${pkg.credits} credits`,
})
// Add credits to customer balance
const expiryDate = new Date()
expiryDate.setDate(expiryDate.getDate() + pkg.expiry)
await db.create($.CreditBalance, {
customerId: purchase.customerId,
credits: pkg.credits + pkg.bonus,
purchased: pkg.credits,
bonus: pkg.bonus,
pricePerCredit: pkg.price / pkg.credits,
expiresAt: expiryDate,
status: 'active',
})
return {
credits: pkg.credits + pkg.bonus,
expiresAt: expiryDate,
}
})
on($.ServiceRequest.created, async (request) => {
if (request.serviceId !== aiProcessingService.id) return
const operation = request.inputs.operation
const usageConfig = aiProcessingService.pricing.usage[operation]
// Calculate credit cost
let creditCost = usageConfig.cost
if (operation === 'image-generation' && request.inputs.quality) {
creditCost *= usageConfig.quality[request.inputs.quality]
} else if (operation === 'text-generation' && request.inputs.wordCount) {
creditCost *= Math.ceil(request.inputs.wordCount / 100)
}
try {
// Check and deduct credits
const balance = await getCustomerCreditBalance(request.customerId)
if (balance.available < creditCost) {
// Check if overage is allowed
if (aiProcessingService.pricing.overage.enabled) {
// Auto-recharge if enabled
if (aiProcessingService.pricing.overage.autoRecharge) {
const rechargeAmount = Math.max(aiProcessingService.pricing.overage.minimumRecharge, creditCost - balance.available)
await send($.Payment.charge, {
customerId: request.customerId,
amount: rechargeAmount * aiProcessingService.pricing.overage.rate,
description: `Credit Auto-Recharge - ${rechargeAmount} credits`,
})
await db.create($.CreditBalance, {
customerId: request.customerId,
credits: rechargeAmount,
purchased: rechargeAmount,
bonus: 0,
pricePerCredit: aiProcessingService.pricing.overage.rate,
status: 'active',
})
} else {
return await send($.ServiceRequest.reject, {
requestId: request.id,
reason: 'Insufficient credits',
balance: balance.available,
required: creditCost,
rechargeUrl: 'https://platform.do/credits/purchase',
})
}
} else {
return await send($.ServiceRequest.reject, {
requestId: request.id,
reason: 'Insufficient credits',
balance: balance.available,
required: creditCost,
})
}
}
// Deduct credits
await deductCredits(request.customerId, creditCost)
// Process request
const result = await processAIRequest(request.inputs)
// Deliver result
await send($.ServiceResult.deliver, {
requestId: request.id,
outputs: result,
creditsCost: creditCost,
creditsRemaining: balance.available - creditCost,
})
} catch (error) {
await send($.ServiceRequest.fail, {
requestId: request.id,
error: error.message,
})
}
})
async function getCustomerCreditBalance(customerId: string) {
const balances = await db.query($.CreditBalance, {
where: {
customerId,
status: 'active',
expiresAt: { gte: new Date() },
},
orderBy: { expiresAt: 'asc' }, // Use credits closest to expiry first
})
const available = balances.reduce((sum, b) => sum + b.credits, 0)
return { available, balances }
}
async function deductCredits(customerId: string, amount: number) {
const { balances } = await getCustomerCreditBalance(customerId)
let remaining = amount
for (const balance of balances) {
if (remaining <= 0) break
const deduction = Math.min(remaining, balance.credits)
await db.update(balance, {
credits: { decrement: deduction },
})
if (balance.credits - deduction === 0) {
await db.update(balance, { status: 'depleted' })
}
remaining -= deduction
}
// Record usage
await db.create($.CreditUsage, {
customerId,
credits: amount,
timestamp: new Date(),
})
}
async function processAIRequest(inputs: any) {
// Simulate AI processing
return {
result: 'processed',
outputs: {},
}
}Credit System Benefits:
- Flexible usage across different services
- Prepaid cash flow
- Volume discounts through packages
- Bonus credits incentivize larger purchases
Example Usage:
- Buy 500 credits for $85 (+ 50 bonus) = 550 total credits
- Generate 100 HD images (200 credits)
- Transcribe 300 minutes audio (300 credits)
- Generate 5,000 words text (50 credits)
- Total used: 550 credits, $0 additional charges
Pattern 3: Freemium + Paid Add-ons
Free base tier with paid feature upgrades:
const projectManagementService = await $.Service.create({
name: 'Project Management Suite',
type: $.ServiceType.ProjectManagement,
pricing: {
model: 'freemium-addons',
free: {
limits: {
projects: 3,
users: 5,
storage: 1, // 1 GB
tasks: 100,
},
features: ['basic-kanban', 'task-management', 'comments'],
},
addons: [
{
id: 'unlimited-projects',
name: 'Unlimited Projects',
price: 9.0, // $9/month
interval: 'month',
unlocks: { projects: Infinity },
},
{
id: 'advanced-users',
name: 'Advanced User Pack (25 users)',
price: 19.0, // $19/month
interval: 'month',
unlocks: { users: 25 },
},
{
id: 'extra-storage',
name: 'Extra Storage (50 GB)',
price: 14.0, // $14/month
interval: 'month',
unlocks: { storage: 50 },
},
{
id: 'time-tracking',
name: 'Time Tracking',
price: 12.0, // $12/month
interval: 'month',
features: ['time-tracking', 'timesheets', 'billing-integration'],
},
{
id: 'advanced-reporting',
name: 'Advanced Reporting',
price: 15.0, // $15/month
interval: 'month',
features: ['custom-reports', 'dashboards', 'export-pdf'],
},
{
id: 'automation',
name: 'Automation Engine',
price: 29.0, // $29/month
interval: 'month',
features: ['workflow-automation', 'custom-triggers', 'integrations'],
},
{
id: 'priority-support',
name: 'Priority Support',
price: 49.0, // $49/month
interval: 'month',
features: ['24-7-support', 'dedicated-account-manager', 'phone-support'],
},
],
},
})
on($.Addon.subscribe, async (addonRequest) => {
const addon = projectManagementService.pricing.addons.find((a) => a.id === addonRequest.addonId)
// Create addon subscription
const subscription = await db.create($.AddonSubscription, {
customerId: addonRequest.customerId,
serviceId: projectManagementService.id,
addonId: addon.id,
price: addon.price,
status: 'active',
startDate: new Date(),
nextBillingDate: addMonths(new Date(), 1),
})
// Charge for addon
await send($.Payment.charge, {
customerId: addonRequest.customerId,
amount: addon.price,
description: `${addon.name} - First month`,
subscriptionId: subscription.id,
})
// Update customer limits/features
if (addon.unlocks) {
await updateCustomerLimits(addonRequest.customerId, addon.unlocks)
}
if (addon.features) {
await enableCustomerFeatures(addonRequest.customerId, addon.features)
}
return subscription
})
// Check limits before operations
on($.Project.create, async (projectRequest) => {
const limits = await getCustomerLimits(projectRequest.customerId)
const currentProjects = await db.query($.Project, {
where: { customerId: projectRequest.customerId },
count: true,
})
if (currentProjects.count >= limits.projects) {
return await send($.Project.createRejected, {
projectId: projectRequest.id,
reason: 'Project limit reached',
current: currentProjects.count,
limit: limits.projects,
upgrade: {
addon: 'unlimited-projects',
price: 9.0,
},
})
}
// Create project...
})
async function getCustomerLimits(customerId: string) {
const addons = await db.query($.AddonSubscription, {
where: {
customerId,
serviceId: projectManagementService.id,
status: 'active',
},
})
// Start with free tier limits
const limits = { ...projectManagementService.pricing.free.limits }
// Apply addon unlocks
for (const addon of addons) {
const addonConfig = projectManagementService.pricing.addons.find((a) => a.id === addon.addonId)
if (addonConfig?.unlocks) {
for (const [key, value] of Object.entries(addonConfig.unlocks)) {
if (value === Infinity) {
limits[key] = Infinity
} else {
limits[key] = (limits[key] || 0) + value
}
}
}
}
return limits
}
async function updateCustomerLimits(customerId: string, unlocks: any) {
// Implementation to update limits
}
async function enableCustomerFeatures(customerId: string, features: string[]) {
// Implementation to enable features
}Freemium + Add-ons Examples:
- Free: 3 projects, 5 users, 1 GB storage = $0/month
- Free + Unlimited Projects = $9/month
- Free + Unlimited Projects + Time Tracking + Reporting = $36/month
- Free + All Add-ons = $147/month (complete suite)
Benefits:
- Low barrier to entry (free tier)
- Customers pay only for what they need
- Gradual revenue expansion
- Clear upgrade path
Pattern 4: Platform Fee + Transaction Percentage
Combine flat platform fee with transaction-based revenue:
const marketplaceService = await $.Service.create({
name: 'E-Commerce Marketplace',
type: $.ServiceType.Marketplace,
pricing: {
model: 'platform-plus-transaction',
plans: [
{
id: 'basic',
name: 'Basic',
platformFee: 29.0, // $29/month platform access
transactionFee: 0.05, // 5% of transaction value
fixedFee: 0.3, // $0.30 per transaction
limits: {
products: 100,
orders: Infinity,
},
features: ['basic-storefront', 'payment-processing', 'email-support'],
},
{
id: 'professional',
name: 'Professional',
platformFee: 99.0, // $99/month platform access
transactionFee: 0.03, // 3% of transaction value (better rate)
fixedFee: 0.3,
limits: {
products: 1000,
orders: Infinity,
},
features: ['advanced-storefront', 'payment-processing', 'analytics', 'custom-domain', 'priority-support'],
},
{
id: 'enterprise',
name: 'Enterprise',
platformFee: 299.0, // $299/month platform access
transactionFee: 0.02, // 2% of transaction value (best rate)
fixedFee: 0.2, // Lower fixed fee
limits: {
products: Infinity,
orders: Infinity,
},
features: ['custom-storefront', 'payment-processing', 'advanced-analytics', 'multi-store', 'api-access', 'dedicated-support', 'custom-integrations'],
},
],
},
})
on($.Subscription.create, async (subscriptionRequest) => {
const plan = marketplaceService.pricing.plans.find((p) => p.id === subscriptionRequest.planId)
const subscription = await db.create($.Subscription, {
customerId: subscriptionRequest.customerId,
serviceId: marketplaceService.id,
planId: plan.id,
status: 'active',
platformFee: plan.platformFee,
transactionFee: plan.transactionFee,
fixedFee: plan.fixedFee,
currentPeriodStart: new Date(),
currentPeriodEnd: addMonths(new Date(), 1),
})
// Charge platform fee
await send($.Payment.charge, {
customerId: subscriptionRequest.customerId,
amount: plan.platformFee,
description: `${plan.name} - Monthly Platform Fee`,
subscriptionId: subscription.id,
})
return subscription
})
// Charge transaction fees on each sale
on($.Order.completed, async (order) => {
const subscription = await db.findOne($.Subscription, {
customerId: order.sellerId,
serviceId: marketplaceService.id,
status: 'active',
})
if (!subscription) return
// Calculate transaction fee
const percentageFee = order.total * subscription.transactionFee
const totalFee = percentageFee + subscription.fixedFee
// Deduct from seller payout
await send($.Payment.charge, {
customerId: order.sellerId,
amount: totalFee,
description: `Transaction Fee - Order #${order.number}`,
breakdown: {
orderTotal: order.total,
percentageFee: subscription.transactionFee,
percentageAmount: percentageFee,
fixedFee: subscription.fixedFee,
total: totalFee,
},
metadata: {
orderId: order.id,
orderNumber: order.number,
},
})
// Record transaction fee
await db.create($.TransactionFee, {
subscriptionId: subscription.id,
orderId: order.id,
orderTotal: order.total,
fee: totalFee,
timestamp: new Date(),
})
})
// Monthly summary report
on($.BillingCycle.ended, async (cycle) => {
const subscriptions = await db.query($.Subscription, {
where: {
serviceId: marketplaceService.id,
status: 'active',
},
})
for (const subscription of subscriptions) {
const plan = marketplaceService.pricing.plans.find((p) => p.id === subscription.planId)
// Get transaction fees for period
const transactions = await db.query($.TransactionFee, {
where: {
subscriptionId: subscription.id,
timestamp: { gte: cycle.start, lte: cycle.end },
},
})
const totalSales = transactions.reduce((sum, t) => sum + t.orderTotal, 0)
const totalFees = transactions.reduce((sum, t) => sum + t.fee, 0)
// Send monthly statement
await send($.Email.send, {
to: subscription.customer.email,
template: 'monthly-statement',
data: {
plan: plan.name,
period: { start: cycle.start, end: cycle.end },
platformFee: plan.platformFee,
transactionCount: transactions.length,
totalSales,
transactionFees: totalFees,
totalCharges: plan.platformFee + totalFees,
effectiveRate: totalSales > 0 ? (totalFees / totalSales) * 100 : 0,
},
})
// Renew platform fee
await send($.Payment.charge, {
customerId: subscription.customerId,
amount: plan.platformFee,
description: `${plan.name} - Monthly Platform Fee`,
subscriptionId: subscription.id,
})
// Update period
await db.update(subscription, {
currentPeriodStart: cycle.end,
currentPeriodEnd: addMonths(cycle.end, 1),
})
}
})Platform + Transaction Examples:
- Basic ($29/mo) + $10k sales = $29 + (10k × 5% + 100 × $0.30) = $559
- Professional ($99/mo) + $50k sales = $99 + (50k × 3% + 300 × $0.30) = $1,689
- Enterprise ($299/mo) + $200k sales = $299 + (200k × 2% + 1000 × $0.20) = $4,499
Pattern 5: Seat-Based + Usage
Per-user licensing combined with usage charges:
const collaborationService = await $.Service.create({
name: 'Team Collaboration Platform',
type: $.ServiceType.Collaboration,
pricing: {
model: 'seat-plus-usage',
plans: [
{
id: 'team',
name: 'Team',
pricePerSeat: 15.0, // $15/user/month
minimumSeats: 3,
included: {
storage: 10, // 10 GB per user
meetings: 100, // 100 meeting minutes per user
recordings: 10, // 10 hours per user
},
usage: {
extraStorage: 0.5, // $0.50 per GB over limit
extraMeetings: 0.05, // $0.05 per minute over limit
extraRecordings: 2.0, // $2 per hour over limit
},
features: ['unlimited-channels', 'screen-sharing', 'file-sharing'],
},
{
id: 'business',
name: 'Business',
pricePerSeat: 25.0, // $25/user/month
minimumSeats: 10,
included: {
storage: 25, // 25 GB per user
meetings: 500, // 500 meeting minutes per user
recordings: 50, // 50 hours per user
},
usage: {
extraStorage: 0.4, // Better overage rates
extraMeetings: 0.03,
extraRecordings: 1.5,
},
features: ['all-team-features', 'advanced-admin', 'sso', 'audit-logs', 'priority-support'],
},
],
},
})
on($.TeamSubscription.create, async (request) => {
const plan = collaborationService.pricing.plans.find((p) => p.id === request.planId)
if (request.seats < plan.minimumSeats) {
throw new Error(`Minimum ${plan.minimumSeats} seats required for ${plan.name} plan`)
}
const subscription = await db.create($.TeamSubscription, {
teamId: request.teamId,
serviceId: collaborationService.id,
planId: plan.id,
seats: request.seats,
pricePerSeat: plan.pricePerSeat,
included: plan.included,
usageRates: plan.usage,
status: 'active',
currentPeriodStart: new Date(),
currentPeriodEnd: addMonths(new Date(), 1),
})
// Charge for seats
const seatCharge = request.seats * plan.pricePerSeat
await send($.Payment.charge, {
teamId: request.teamId,
amount: seatCharge,
description: `${plan.name} - ${request.seats} seats`,
subscriptionId: subscription.id,
})
return subscription
})
// Add/remove seats mid-month
on($.Subscription.updateSeats, async (updateRequest) => {
const subscription = await db.findOne($.TeamSubscription, {
id: updateRequest.subscriptionId,
})
const plan = collaborationService.pricing.plans.find((p) => p.id === subscription.planId)
const seatDiff = updateRequest.newSeats - subscription.seats
// Prorate the charge/credit
const now = new Date()
const periodLength = subscription.currentPeriodEnd.getTime() - subscription.currentPeriodStart.getTime()
const timeRemaining = subscription.currentPeriodEnd.getTime() - now.getTime()
const prorationFactor = timeRemaining / periodLength
const proratedCharge = seatDiff * plan.pricePerSeat * prorationFactor
if (proratedCharge > 0) {
// Adding seats
await send($.Payment.charge, {
teamId: subscription.teamId,
amount: proratedCharge,
description: `Added ${seatDiff} seats (prorated)`,
subscriptionId: subscription.id,
})
} else if (proratedCharge < 0) {
// Removing seats - credit for next billing
await db.create($.Credit, {
teamId: subscription.teamId,
amount: Math.abs(proratedCharge),
reason: `Removed ${Math.abs(seatDiff)} seats`,
expiresAt: subscription.currentPeriodEnd,
})
}
await db.update(subscription, {
seats: updateRequest.newSeats,
})
})
// Track usage during month
on($.Meeting.ended, async (meeting) => {
await db.create($.UsageRecord, {
teamId: meeting.teamId,
type: 'meeting',
units: Math.ceil(meeting.durationSeconds / 60), // minutes
timestamp: new Date(),
metadata: {
meetingId: meeting.id,
participants: meeting.participants.length,
},
})
})
// End-of-month usage billing
on($.BillingCycle.ended, async (cycle) => {
const subscriptions = await db.query($.TeamSubscription, {
where: {
serviceId: collaborationService.id,
status: 'active',
},
})
for (const subscription of subscriptions) {
const plan = collaborationService.pricing.plans.find((p) => p.id === subscription.planId)
// Calculate included limits for the team
const totalLimits = {
storage: subscription.seats * plan.included.storage,
meetings: subscription.seats * plan.included.meetings,
recordings: subscription.seats * plan.included.recordings,
}
// Get actual usage
const usage = await getTeamUsage(subscription.teamId, cycle.start, cycle.end)
// Calculate overages
let overageTotal = 0
const overageItems = []
if (usage.storage > totalLimits.storage) {
const overage = usage.storage - totalLimits.storage
const cost = overage * plan.usage.extraStorage
overageTotal += cost
overageItems.push({
description: `Extra Storage - ${overage.toFixed(2)} GB`,
quantity: overage,
rate: plan.usage.extraStorage,
amount: cost,
})
}
if (usage.meetings > totalLimits.meetings) {
const overage = usage.meetings - totalLimits.meetings
const cost = overage * plan.usage.extraMeetings
overageTotal += cost
overageItems.push({
description: `Extra Meeting Minutes - ${overage} minutes`,
quantity: overage,
rate: plan.usage.extraMeetings,
amount: cost,
})
}
if (usage.recordings > totalLimits.recordings) {
const overage = usage.recordings - totalLimits.recordings
const cost = overage * plan.usage.extraRecordings
overageTotal += cost
overageItems.push({
description: `Extra Recording Storage - ${overage.toFixed(2)} hours`,
quantity: overage,
rate: plan.usage.extraRecordings,
amount: cost,
})
}
// Create invoice
const lineItems = [
{
description: `${plan.name} - ${subscription.seats} seats`,
quantity: subscription.seats,
rate: plan.pricePerSeat,
amount: subscription.seats * plan.pricePerSeat,
},
...overageItems,
]
const total = subscription.seats * plan.pricePerSeat + overageTotal
await send($.Invoice.create, {
teamId: subscription.teamId,
subscriptionId: subscription.id,
period: { start: cycle.start, end: cycle.end },
lineItems,
total,
})
// Update period
await db.update(subscription, {
currentPeriodStart: cycle.end,
currentPeriodEnd: addMonths(cycle.end, 1),
})
}
})
async function getTeamUsage(teamId: string, start: Date, end: Date) {
const records = await db.query($.UsageRecord, {
where: {
teamId,
timestamp: { gte: start, lte: end },
},
})
return {
storage: await getCurrentStorageGB(teamId),
meetings: records.filter((r) => r.type === 'meeting').reduce((sum, r) => sum + r.units, 0),
recordings: records.filter((r) => r.type === 'recording').reduce((sum, r) => sum + r.units, 0),
}
}
async function getCurrentStorageGB(teamId: string): Promise<number> {
const files = await db.query($.File, {
where: { teamId },
})
const totalBytes = files.reduce((sum, f) => sum + f.sizeBytes, 0)
return totalBytes / 1024 ** 3
}Seat + Usage Examples:
- Team: 5 seats @ $15 = $75/mo + overages
- If 60 GB used (50 GB included) = $75 + (10 GB × $0.50) = $80
- Business: 20 seats @ $25 = $500/mo + overages
- If 600 GB used (500 GB included) = $500 + (100 GB × $0.40) = $540
Pattern 6: Tiered Subscription + Credits
Monthly subscription with included credits plus top-up options:
const apiGatewayService = await $.Service.create({
name: 'API Gateway Service',
type: $.ServiceType.APIGateway,
pricing: {
model: 'subscription-credits',
plans: [
{
id: 'starter',
name: 'Starter',
monthlyFee: 29.0,
includedCredits: 100000, // 100k API calls included
features: ['basic-analytics', 'rate-limiting', 'caching'],
},
{
id: 'growth',
name: 'Growth',
monthlyFee: 99.0,
includedCredits: 500000, // 500k API calls included
features: ['advanced-analytics', 'custom-domains', 'webhooks', 'transformations'],
},
{
id: 'scale',
name: 'Scale',
monthlyFee: 299.0,
includedCredits: 2000000, // 2M API calls included
features: ['all-features', 'dedicated-infrastructure', 'sla-guarantee', 'priority-support'],
},
],
creditTopup: {
packages: [
{ credits: 100000, price: 25.0 }, // $0.00025 per call
{ credits: 500000, price: 100.0 }, // $0.0002 per call
{ credits: 1000000, price: 180.0 }, // $0.00018 per call
],
autoTopup: {
enabled: true,
threshold: 0.1, // Auto-topup at 10% remaining
defaultPackage: 0, // Default to smallest package
},
},
},
})
on($.Subscription.create, async (subscriptionRequest) => {
const plan = apiGatewayService.pricing.plans.find((p) => p.id === subscriptionRequest.planId)
const subscription = await db.create($.Subscription, {
customerId: subscriptionRequest.customerId,
serviceId: apiGatewayService.id,
planId: plan.id,
monthlyFee: plan.monthlyFee,
status: 'active',
currentPeriodStart: new Date(),
currentPeriodEnd: addMonths(new Date(), 1),
})
// Charge monthly fee
await send($.Payment.charge, {
customerId: subscriptionRequest.customerId,
amount: plan.monthlyFee,
description: `${plan.name} - Monthly subscription`,
subscriptionId: subscription.id,
})
// Add included credits
await db.create($.CreditBalance, {
subscriptionId: subscription.id,
customerId: subscriptionRequest.customerId,
credits: plan.includedCredits,
type: 'included',
periodStart: new Date(),
periodEnd: addMonths(new Date(), 1),
})
return subscription
})
// Track API usage
on($.APIRequest.completed, async (apiRequest) => {
const subscription = await db.findOne($.Subscription, {
customerId: apiRequest.customerId,
serviceId: apiGatewayService.id,
status: 'active',
})
if (!subscription) return
// Deduct 1 credit per API call
const balance = await db.findOne($.CreditBalance, {
subscriptionId: subscription.id,
periodEnd: { gte: new Date() },
})
await db.update(balance, {
credits: { decrement: 1 },
})
// Check for auto-topup
const plan = apiGatewayService.pricing.plans.find((p) => p.id === subscription.planId)
const threshold = plan.includedCredits * apiGatewayService.pricing.creditTopup.autoTopup.threshold
if (balance.credits - 1 <= threshold && apiGatewayService.pricing.creditTopup.autoTopup.enabled) {
// Trigger auto-topup
const packageIndex = apiGatewayService.pricing.creditTopup.autoTopup.defaultPackage
const topupPackage = apiGatewayService.pricing.creditTopup.packages[packageIndex]
await send($.Payment.charge, {
customerId: subscription.customerId,
amount: topupPackage.price,
description: `Credit Top-up - ${topupPackage.credits.toLocaleString()} API calls`,
subscriptionId: subscription.id,
})
await db.create($.CreditBalance, {
subscriptionId: subscription.id,
customerId: subscription.customerId,
credits: topupPackage.credits,
type: 'topup',
periodStart: new Date(),
periodEnd: subscription.currentPeriodEnd,
})
await send($.Customer.notify, {
customerId: subscription.customerId,
type: 'auto-topup',
message: `Added ${topupPackage.credits.toLocaleString()} credits for $${topupPackage.price}`,
})
}
})
// Monthly renewal
on($.BillingCycle.ended, async (cycle) => {
const subscriptions = await db.query($.Subscription, {
where: {
serviceId: apiGatewayService.id,
status: 'active',
},
})
for (const subscription of subscriptions) {
const plan = apiGatewayService.pricing.plans.find((p) => p.id === subscription.planId)
// Get total topups this period
const topups = await db.query($.CreditBalance, {
where: {
subscriptionId: subscription.id,
type: 'topup',
periodStart: { gte: cycle.start, lte: cycle.end },
},
})
const totalTopupCost = topups.reduce((sum, t) => {
const pkg = apiGatewayService.pricing.creditTopup.packages.find((p) => p.credits === t.credits)
return sum + (pkg?.price || 0)
}, 0)
// Send usage summary
const includedBalance = await db.findOne($.CreditBalance, {
subscriptionId: subscription.id,
type: 'included',
})
const includedUsed = plan.includedCredits - (includedBalance?.credits || 0)
const totalTopupCredits = topups.reduce((sum, t) => sum + t.credits, 0)
await send($.Email.send, {
to: subscription.customer.email,
template: 'usage-summary',
data: {
plan: plan.name,
period: { start: cycle.start, end: cycle.end },
includedCredits: plan.includedCredits,
includedUsed,
topupCredits: totalTopupCredits,
totalUsed: includedUsed + totalTopupCredits,
monthlyFee: plan.monthlyFee,
topupCosts: totalTopupCost,
totalCost: plan.monthlyFee + totalTopupCost,
},
})
// Renew subscription and reset credits
await send($.Payment.charge, {
customerId: subscription.customerId,
amount: plan.monthlyFee,
description: `${plan.name} - Monthly subscription`,
subscriptionId: subscription.id,
})
// Expire old balances
await db.update($.CreditBalance, {
where: {
subscriptionId: subscription.id,
periodEnd: { lte: cycle.end },
},
data: { status: 'expired' },
})
// Add new included credits
await db.create($.CreditBalance, {
subscriptionId: subscription.id,
customerId: subscription.customerId,
credits: plan.includedCredits,
type: 'included',
periodStart: cycle.end,
periodEnd: addMonths(cycle.end, 1),
})
// Update subscription period
await db.update(subscription, {
currentPeriodStart: cycle.end,
currentPeriodEnd: addMonths(cycle.end, 1),
})
}
})Subscription + Credits Examples:
- Starter ($29/mo) + 100k calls included
- Use 150k calls → $29 + $25 topup = $54
- Growth ($99/mo) + 500k calls included
- Use 800k calls → $99 + $100 topup = $199
- Scale ($299/mo) + 2M calls included
- Use 2M calls → $299 (no topup needed)
Best Practices
1. Transparent Pricing Breakdown
Always show customers exactly what they're paying for:
async function generatePricingBreakdown(customerId: string, period: { start: Date; end: Date }) {
const subscriptions = await db.query($.Subscription, {
where: { customerId, status: 'active' },
})
const breakdown = {
recurring: [],
usage: [],
oneTime: [],
total: 0,
}
for (const subscription of subscriptions) {
// Recurring fees
breakdown.recurring.push({
service: subscription.service.name,
plan: subscription.plan.name,
amount: subscription.baseFee || subscription.monthlyFee,
})
// Usage charges
const usage = await getUsageCharges(subscription.id, period)
if (usage.length > 0) {
breakdown.usage.push({
service: subscription.service.name,
charges: usage,
})
}
}
// Calculate totals
breakdown.total = [...breakdown.recurring, ...breakdown.usage.flatMap((u) => u.charges), ...breakdown.oneTime].reduce((sum, item) => sum + item.amount, 0)
return breakdown
}2. Usage Forecasting
Help customers predict costs:
async function forecastMonthlyBill(customerId: string) {
const currentUsage = await getCurrentMonthUsage(customerId)
const daysInMonth = 30
const daysPassed = new Date().getDate()
const projectionFactor = daysInMonth / daysPassed
const forecast = {
current: currentUsage.total,
projected: currentUsage.total * projectionFactor,
breakdown: currentUsage.breakdown.map((item) => ({
...item,
projected: item.amount * projectionFactor,
})),
confidence: daysPassed >= 7 ? 'high' : 'low',
}
return forecast
}3. Flexible Upgrades
Make it easy to switch between models:
async function suggestOptimalPricing(customerId: string) {
const usage = await getHistoricalUsage(customerId, 6) // 6 months
const averageMonthly = usage.reduce((sum, m) => sum + m.total, 0) / 6
// Compare different pricing models
const models = [
{ type: 'pay-as-you-go', cost: calculatePayAsYouGo(averageMonthly) },
{ type: 'subscription', cost: findBestSubscription(averageMonthly) },
{ type: 'hybrid', cost: findBestHybrid(averageMonthly) },
]
const optimal = models.reduce((best, current) => (current.cost < best.cost ? current : best))
return {
current: { type: 'current', cost: averageMonthly },
optimal,
savings: averageMonthly - optimal.cost,
savingsPercentage: ((averageMonthly - optimal.cost) / averageMonthly) * 100,
}
}Real-World Examples
Twilio (Communication Platform)
const pricing = {
model: 'pay-as-you-go',
services: {
sms: { rate: 0.0079 }, // $0.0079 per SMS
voice: { rate: 0.014 }, // $0.014 per minute
video: { rate: 0.004 }, // $0.004 per participant-minute
},
}
// No base fee, pure usage-based
// Example: 10k SMS + 1k voice minutes = $93Stripe (Payment Processing)
const pricing = {
model: 'transaction-percentage',
standard: {
percentage: 0.029, // 2.9%
fixed: 0.3, // + $0.30 per transaction
},
volume: {
threshold: 1000000, // $1M/month
percentage: 0.027, // Custom pricing available
},
}
// Example: $100k in payments = $3,200 in feesAWS (Cloud Infrastructure)
const pricing = {
model: 'multi-service-tiered',
services: {
compute: { tiered: true, rates: [...] },
storage: { tiered: true, rates: [...] },
transfer: { tiered: true, rates: [...] },
},
commitments: {
reserved: { discount: 0.40 }, // 40% off with 1-3 year commit
savings: { discount: 0.72 }, // Up to 72% with flexible commitment
},
}
// Highly flexible, multiple dimensionsNext Steps
- Per-Use Pricing → - Usage-based billing
- Subscription Pricing → - Recurring revenue
- Tiered Pricing → - Volume discounts
- Monetization Overview → - Compare all models
Resources
- Building Services → - Implement hybrid pricing
- Best Practices → - Optimize your model
- Deployment → - Launch your service