Monetization Strategies
Pricing models and revenue strategies for services
Learn how to price, bill, and generate revenue from your services.
Pricing Models
Per-Use Pricing
Charge based on actual usage:
import $, { db, on, send } from 'sdk.do'
// Per-unit pricing
const translationService = await $.Service.create({
name: 'Document Translator',
pricing: {
model: 'per-unit',
unit: 'word',
rate: 0.02, // $0.02 per word
minimumCharge: 5.0,
},
})
on($.ServiceResult.delivered, async (result) => {
if (result.serviceId !== translationService.id) return
const wordCount = result.metadata.wordCount
const cost = Math.max(wordCount * translationService.pricing.rate, translationService.pricing.minimumCharge)
await send($.Payment.charge, {
customerId: result.customerId,
amount: cost,
description: `Translation: ${wordCount} words`,
breakdown: {
words: wordCount,
rate: translationService.pricing.rate,
subtotal: wordCount * translationService.pricing.rate,
minimum: translationService.pricing.minimumCharge,
total: cost,
},
})
})Tiered Pricing
Different rates for different volume levels:
// Volume-based tiers
const imageService = await $.Service.create({
name: 'Image Generator',
pricing: {
model: 'tiered',
tiers: [
{ min: 0, max: 10, rate: 2.0 }, // First 10 images: $2 each
{ min: 11, max: 50, rate: 1.5 }, // Next 40 images: $1.50 each
{ min: 51, max: 100, rate: 1.0 }, // Next 50 images: $1 each
{ min: 101, max: Infinity, rate: 0.75 }, // 100+: $0.75 each
],
},
})
function calculateTieredPrice(quantity: number, tiers: any[]): number {
let cost = 0
let remaining = quantity
for (const tier of tiers) {
if (remaining <= 0) break
const tierSize = tier.max - tier.min + 1
const unitsInTier = Math.min(remaining, tierSize)
cost += unitsInTier * tier.rate
remaining -= unitsInTier
}
return cost
}
on($.ServiceResult.delivered, async (result) => {
if (result.serviceId !== imageService.id) return
const quantity = result.metadata.imageCount
const cost = calculateTieredPrice(quantity, imageService.pricing.tiers)
await send($.Payment.charge, {
customerId: result.customerId,
amount: cost,
description: `${quantity} images generated`,
})
})Subscription Pricing
Recurring charges with usage limits:
// Monthly subscription
const contentService = await $.Service.create({
name: 'Content Generator Pro',
pricing: {
model: 'subscription',
plans: [
{
id: 'starter',
name: 'Starter',
price: 49.0,
interval: 'month',
limits: {
articles: 10,
words: 15000,
images: 20,
},
features: ['basic-templates', 'seo-optimization'],
},
{
id: 'professional',
name: 'Professional',
price: 149.0,
interval: 'month',
limits: {
articles: 50,
words: 100000,
images: 100,
},
features: ['all-templates', 'seo-optimization', 'plagiarism-check', 'priority-support'],
},
{
id: 'enterprise',
name: 'Enterprise',
price: 499.0,
interval: 'month',
limits: {
articles: Infinity,
words: Infinity,
images: Infinity,
},
features: ['all-features', 'dedicated-account-manager', 'custom-templates', 'api-access'],
},
],
overage: {
articles: 5.0,
words: 0.01,
images: 1.0,
},
},
})
// Check subscription limits
on($.ServiceRequest.created, async (request) => {
if (request.serviceId !== contentService.id) return
// Get customer subscription
const subscription = await db.findOne($.Subscription, {
customerId: request.customerId,
serviceId: contentService.id,
status: 'active',
})
if (!subscription) {
return await send($.ServiceRequest.reject, {
requestId: request.id,
reason: 'No active subscription',
})
}
// Check usage limits
const usage = await db.findOne($.Usage, {
subscriptionId: subscription.id,
period: getCurrentBillingPeriod(subscription),
})
const plan = contentService.pricing.plans.find((p) => p.id === subscription.planId)
if (usage.articles >= plan.limits.articles) {
// Calculate overage charge
const overageCost = contentService.pricing.overage.articles
await send($.Payment.charge, {
customerId: request.customerId,
amount: overageCost,
description: 'Overage: Additional article',
type: 'overage',
})
}
// Execute service...
})Value-Based Pricing
Price based on value delivered:
// Price optimization service
const optimizationService = await $.Service.create({
name: 'Price Optimizer',
pricing: {
model: 'value-based',
valueShare: 0.1, // 10% of value created
minimumFee: 100.0,
maximumFee: 10000.0,
calculation: 'projected-revenue-increase',
},
})
on($.ServiceResult.delivered, async (result) => {
if (result.serviceId !== optimizationService.id) return
// Calculate value created
const currentRevenue = result.inputs.currentRevenue
const projectedIncrease = result.outputs.projectedIncrease
const valueCreated = projectedIncrease
let fee = valueCreated * optimizationService.pricing.valueShare
// Apply bounds
fee = Math.max(fee, optimizationService.pricing.minimumFee)
fee = Math.min(fee, optimizationService.pricing.maximumFee)
await send($.Payment.charge, {
customerId: result.customerId,
amount: fee,
description: 'Price Optimization (10% of projected value)',
breakdown: {
currentRevenue,
projectedIncrease,
valueShare: optimizationService.pricing.valueShare,
calculatedFee: valueCreated * optimizationService.pricing.valueShare,
finalFee: fee,
},
})
})Dynamic Pricing
Adjust prices based on demand, customer, or context:
// Dynamic pricing engine
const aiService = await $.Service.create({
name: 'AI Content Generator',
pricing: {
model: 'dynamic',
baseRate: 50.0,
factors: {
demand: { weight: 0.3, min: 0.8, max: 1.5 },
customerTier: { weight: 0.2, discount: true },
timeOfDay: { weight: 0.1, offPeak: 0.9 },
quality: { weight: 0.2, premium: 1.3 },
urgency: { weight: 0.2, rush: 1.5 },
},
},
})
async function calculateDynamicPrice(service, request, context) {
let price = service.pricing.baseRate
const factors = []
// Demand factor
const demandLevel = await getCurrentDemandLevel(service.id)
const demandMultiplier = mapRange(demandLevel, 0, 100, service.pricing.factors.demand.min, service.pricing.factors.demand.max)
price *= demandMultiplier
factors.push({ name: 'demand', level: demandLevel, multiplier: demandMultiplier })
// Customer tier discount
const customer = await db.findOne($.Customer, { id: request.customerId })
if (customer.tier) {
const discount = customer.tier.discount || 0
price *= 1 - discount
factors.push({ name: 'customerTier', tier: customer.tier.name, discount })
}
// Time of day
const hour = new Date().getHours()
if (hour >= 0 && hour < 8) {
// Off-peak
price *= service.pricing.factors.timeOfDay.offPeak
factors.push({ name: 'timeOfDay', period: 'off-peak', multiplier: 0.9 })
}
// Quality level
if (request.inputs.quality === 'premium') {
price *= service.pricing.factors.quality.premium
factors.push({ name: 'quality', level: 'premium', multiplier: 1.3 })
}
// Urgency
if (request.inputs.urgent) {
price *= service.pricing.factors.urgency.rush
factors.push({ name: 'urgency', rush: true, multiplier: 1.5 })
}
return {
price: Math.round(price * 100) / 100,
baseRate: service.pricing.baseRate,
factors,
}
}
on($.ServiceResult.delivered, async (result) => {
if (result.serviceId !== aiService.id) return
const pricing = await calculateDynamicPrice(aiService, result.request, result.context)
await send($.Payment.charge, {
customerId: result.customerId,
amount: pricing.price,
description: 'AI Content Generation',
breakdown: {
baseRate: pricing.baseRate,
factors: pricing.factors,
finalPrice: pricing.price,
},
})
})Freemium Model
Free tier with paid upgrades:
const summarizationService = await $.Service.create({
name: 'Document Summarizer',
pricing: {
model: 'freemium',
free: {
limits: {
daily: 5,
monthlyWords: 10000,
},
features: ['basic-summaries'],
},
paid: [
{
id: 'pro',
price: 19.0,
interval: 'month',
limits: {
daily: 100,
monthlyWords: 500000,
},
features: ['basic-summaries', 'detailed-summaries', 'multi-language'],
},
{
id: 'business',
price: 99.0,
interval: 'month',
limits: {
daily: Infinity,
monthlyWords: Infinity,
},
features: ['all-features', 'api-access', 'priority-processing'],
},
],
},
})
on($.ServiceRequest.created, async (request) => {
if (request.serviceId !== summarizationService.id) return
const customer = await db.findOne($.Customer, { id: request.customerId })
// Check if customer has paid plan
const subscription = await db.findOne($.Subscription, {
customerId: customer.id,
serviceId: summarizationService.id,
status: 'active',
})
let limits, features
if (subscription) {
const plan = summarizationService.pricing.paid.find((p) => p.id === subscription.planId)
limits = plan.limits
features = plan.features
} else {
limits = summarizationService.pricing.free.limits
features = summarizationService.pricing.free.features
}
// Check daily limit
const usage = await getUsageToday(customer.id, summarizationService.id)
if (usage.count >= limits.daily) {
return await send($.ServiceRequest.reject, {
requestId: request.id,
reason: 'Daily limit reached',
upgrade: {
message: 'Upgrade to Pro for unlimited daily summaries',
plans: summarizationService.pricing.paid,
},
})
}
// Check word limit
if (usage.words + request.inputs.wordCount > limits.monthlyWords) {
return await send($.ServiceRequest.reject, {
requestId: request.id,
reason: 'Monthly word limit reached',
upgrade: {
message: 'Upgrade for higher limits',
plans: summarizationService.pricing.paid,
},
})
}
// Execute service (free for subscribers, would be charged for pay-per-use)
// ...
})Billing Implementation
Invoice Generation
// Monthly invoice generation
on($.BillingPeriod.ended, async (period) => {
// Get all active subscriptions
const subscriptions = await db.query($.Subscription, {
where: { status: 'active' },
})
for (const subscription of subscriptions) {
// Get usage for period
const usage = await db.findOne($.Usage, {
subscriptionId: subscription.id,
periodId: period.id,
})
// Calculate charges
const service = await db.findOne($.Service, { id: subscription.serviceId })
const plan = service.pricing.plans.find((p) => p.id === subscription.planId)
let total = plan.price
// Add overage charges
const overages = []
for (const [metric, limit] of Object.entries(plan.limits)) {
if (usage[metric] > limit) {
const overage = usage[metric] - limit
const overageCost = overage * service.pricing.overage[metric]
total += overageCost
overages.push({
metric,
limit,
actual: usage[metric],
overage,
cost: overageCost,
})
}
}
// Create invoice
const invoice = await db.create($.Invoice, {
customerId: subscription.customerId,
subscriptionId: subscription.id,
periodId: period.id,
lineItems: [
{
description: `${plan.name} - ${period.start} to ${period.end}`,
amount: plan.price,
},
...overages.map((o) => ({
description: `Overage: ${o.metric} (${o.overage} over limit)`,
amount: o.cost,
})),
],
subtotal: total,
tax: total * 0.08, // 8% tax
total: total * 1.08,
dueDate: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000), // 14 days
})
// Send invoice to customer
await send($.Invoice.send, {
invoiceId: invoice.id,
customerId: subscription.customerId,
})
}
})Payment Processing
// Process payment
on($.Payment.charge, async (charge) => {
try {
// Get customer payment method
const customer = await db.findOne($.Customer, { id: charge.customerId })
const paymentMethod = await db.findOne($.PaymentMethod, {
customerId: customer.id,
default: true,
})
if (!paymentMethod) {
throw new Error('No payment method on file')
}
// Process with payment provider
const result = await processPayment({
amount: charge.amount,
currency: charge.currency || 'USD',
paymentMethod: paymentMethod.token,
description: charge.description,
metadata: {
serviceId: charge.serviceId,
requestId: charge.requestId,
},
})
// Record successful payment
await db.create($.Payment, {
customerId: charge.customerId,
amount: charge.amount,
status: 'succeeded',
providerId: result.id,
providerData: result,
createdAt: new Date(),
})
// Update service request
if (charge.requestId) {
await db.update($.ServiceRequest, {
where: { id: charge.requestId },
data: { paid: true, paymentId: result.id },
})
}
await send($.Payment.succeeded, {
paymentId: result.id,
customerId: charge.customerId,
amount: charge.amount,
})
} catch (error) {
// Record failed payment
await db.create($.Payment, {
customerId: charge.customerId,
amount: charge.amount,
status: 'failed',
error: error.message,
createdAt: new Date(),
})
await send($.Payment.failed, {
customerId: charge.customerId,
amount: charge.amount,
error: error.message,
})
// Retry logic
if (charge.retry < 3) {
setTimeout(
() => {
send($.Payment.charge, {
...charge,
retry: (charge.retry || 0) + 1,
})
},
1000 * 60 * 60
) // Retry in 1 hour
}
}
})Usage Tracking
// Track service usage
on($.ServiceResult.delivered, async (result) => {
// Get or create usage record
const subscription = await db.findOne($.Subscription, {
customerId: result.customerId,
serviceId: result.serviceId,
status: 'active',
})
if (!subscription) return
const period = getCurrentBillingPeriod(subscription)
const usage =
(await db.findOne($.Usage, {
subscriptionId: subscription.id,
periodId: period.id,
})) ||
(await db.create($.Usage, {
subscriptionId: subscription.id,
periodId: period.id,
metrics: {},
}))
// Update usage metrics
const updates = {}
for (const [metric, value] of Object.entries(result.usage)) {
updates[`metrics.${metric}`] = { increment: value }
}
await db.update(usage, updates)
// Check if approaching limits
const plan = await getPlan(subscription)
for (const [metric, limit] of Object.entries(plan.limits)) {
const current = usage.metrics[metric] || 0
const percentage = current / limit
if (percentage >= 0.8 && percentage < 0.9) {
await send($.Customer.notify, {
customerId: subscription.customerId,
type: 'usage-warning',
message: `You've used 80% of your ${metric} limit`,
data: { metric, current, limit },
})
} else if (percentage >= 0.9) {
await send($.Customer.notify, {
customerId: subscription.customerId,
type: 'usage-critical',
message: `You've used 90% of your ${metric} limit`,
data: { metric, current, limit },
})
}
}
})Revenue Optimization
A/B Testing Prices
// Price experimentation
const pricingExperiment = await $.Experiment.create({
name: 'Content Service Pricing Test',
serviceId: contentService.id,
variants: [
{
id: 'control',
pricing: { rate: 50.0 },
traffic: 0.4,
},
{
id: 'lower',
pricing: { rate: 39.0 },
traffic: 0.3,
},
{
id: 'higher',
pricing: { rate: 69.0 },
traffic: 0.3,
},
],
metrics: ['conversion-rate', 'revenue', 'customer-lifetime-value'],
duration: 30, // days
})
// Assign variant to customer
on($.ServiceRequest.created, async (request) => {
if (request.serviceId !== contentService.id) return
// Get or assign variant
let assignment = await db.findOne($.ExperimentAssignment, {
experimentId: pricingExperiment.id,
customerId: request.customerId,
})
if (!assignment) {
assignment = await assignVariant(pricingExperiment, request.customerId)
}
// Use variant pricing
const variant = pricingExperiment.variants.find((v) => v.id === assignment.variantId)
const pricing = variant.pricing
// Track conversion
await db.create($.ExperimentEvent, {
experimentId: pricingExperiment.id,
variantId: variant.id,
customerId: request.customerId,
event: 'service-requested',
pricing: pricing.rate,
})
// Process with variant pricing...
})
// Analyze results
on($.Experiment.complete, async (experiment) => {
const results = await analyzeExperiment(experiment)
// Winner: highest revenue per customer
const winner = results.variants.reduce((best, current) => (current.revenuePerCustomer > best.revenuePerCustomer ? current : best))
// Update service pricing
await db.update(experiment.service, {
pricing: winner.pricing,
})
await send($.Team.notify, {
message: `Pricing experiment complete. Winner: ${winner.id} with ${winner.revenuePerCustomer}/customer`,
data: results,
})
})Pricing Analytics
// Revenue analytics dashboard
async function getRevenueAnalytics(serviceId: string, period: string) {
const service = await db.findOne($.Service, { id: serviceId })
// Total revenue
const totalRevenue = await db.aggregate($.Payment, {
where: {
serviceId,
status: 'succeeded',
createdAt: { gte: getStartOfPeriod(period) },
},
sum: 'amount',
})
// Revenue by pricing model
const byModel = await db.aggregate($.Payment, {
where: { serviceId },
groupBy: 'pricingModel',
sum: 'amount',
count: true,
})
// Customer segments
const byTier = await db.query(
`
SELECT c.tier, SUM(p.amount) as revenue, COUNT(DISTINCT p.customerId) as customers
FROM payments p
JOIN customers c ON p.customerId = c.id
WHERE p.serviceId = ? AND p.status = 'succeeded'
GROUP BY c.tier
`,
[serviceId]
)
// Cohort analysis
const cohorts = await calculateCohortRevenue(serviceId)
// Churn analysis
const churn = await calculateChurnRate(serviceId, period)
return {
totalRevenue,
byModel,
byTier,
cohorts,
churn,
averageTransactionValue: totalRevenue / byModel.reduce((sum, m) => sum + m.count, 0),
customerLifetimeValue: await calculateCLV(serviceId),
}
}Best Practices
1. Transparent Pricing
Always show customers what they'll pay:
// Price preview before execution
on($.ServiceRequest.created, async (request) => {
// Calculate price
const estimate = await estimatePrice(request)
// Show to customer
await send($.Customer.priceEstimate, {
customerId: request.customerId,
requestId: request.id,
estimate: {
base: estimate.base,
additions: estimate.additions,
discounts: estimate.discounts,
total: estimate.total,
breakdown: estimate.breakdown,
},
requireApproval: estimate.total > 100, // Require approval for large charges
})
})2. Fair Billing
Only charge for successful deliveries:
on($.ServiceRequest.fail, async (request) => {
// Refund if already charged
if (request.paymentId) {
await send($.Payment.refund, {
paymentId: request.paymentId,
reason: 'Service execution failed',
})
}
})3. Clear Communication
Notify customers about charges:
on($.Payment.succeeded, async (payment) => {
await send($.Email.send, {
to: payment.customer.email,
template: 'payment-receipt',
data: {
amount: payment.amount,
service: payment.service.name,
description: payment.description,
date: payment.createdAt,
},
})
})Next Steps
- Deploy Services → - Launch to production
- Explore Examples → - See pricing in action
- Learn Best Practices → - Optimize your services