Docs / schema

Billing Configuration Files

Raterunner defines a universal billing configuration format — a structured way to describe your entire pricing setup: plans, prices, entitlements, addons, and promotions.

File Format

The configuration can be written in YAML or JSON:

  • YAML (billing.yaml) — recommended for human editing, cleaner syntax
  • JSON (billing.json) — useful for programmatic generation or tooling integration

Both formats are validated against the same JSON Schema, ensuring consistency regardless of which format you choose.

yaml
# billing.yaml (YAML format)version: 1plans:  - id: pro    name: Pro    prices:      monthly: { amount: 2900 }
json
// billing.json (JSON format){  "version": 1,  "plans": [    {      "id": "pro",      "name": "Pro",      "prices": {        "monthly": { "amount": 2900 }      }    }  ]}

Most examples in this documentation use YAML for readability.

Why a Configuration Schema?

SaaS billing configuration is typically scattered across multiple places:

LocationWhat lives there
Payment provider dashboardsProducts, prices, coupons
Backend codeEntitlement checks, feature flags
Marketing sitesPricing pages, feature comparisons
Infrastructure configsEnvironment-specific settings

This leads to configuration drift, inconsistencies between environments, and provider lock-in.

How Other Approaches Fall Short

ApproachLimitation
Provider dashboardsLock-in, no version control, manual sync between environments
Terraform/PulumiInfrastructure-focused, provider-specific resources, no entitlement logic
Feature flag servicesPricing logic separate from access control, another vendor dependency
Custom codeScattered across codebase, hard to audit, no single source of truth

The Raterunner Solution

PrincipleHow it works
Single source of truthOne billing.yaml defines all plans, prices, and entitlements
Provider-agnosticSync to Stripe, Paddle, or Chargebee from the same config
Environment separationProvider IDs stored separately per sandbox/production
Version controlledYAML in Git = full history, PR reviews, rollback
Backend owns entitlementsYour code checks limits, not the provider
Marketing includedHeadlines, feature lists live with pricing data

Architecture

The system works in three layers:

  1. Source of Truth — your billing.yaml file with plans, prices, and entitlements
  2. Provider ID Files — auto-generated files (stripe_sandbox.yaml, stripe_production.yaml) that map your config to provider-specific IDs
  3. Payment Providers — Stripe, Paddle, Chargebee where the actual products and prices live

Your billing.yaml file is never modified by the CLI — it's read-only. Provider ID files are auto-generated and contain only ID mappings, no business logic.

File Structure

A typical Raterunner setup in your project:

text
your-project/└── raterunner/    ├── billing.yaml           # Source of truth (you edit this)    ├── stripe_sandbox.yaml    # Auto-generated: Stripe IDs for test env    └── stripe_production.yaml # Auto-generated: Stripe IDs for live env
FilePurposeCommitted to Git?
billing.yamlYour billing configurationYes
stripe_sandbox.yamlStripe IDs for test environmentYes
stripe_production.yamlStripe IDs for live environmentYes

All files should be committed to Git — this gives you full history of what plans existed at any point in time.

Schema Structure

yaml
version: 1providers:  - stripe                    # Payment providers to sync withsettings:  currency: usd  trial_days: 14  grace_days: 7entitlements:                 # Define available limits  projects:    type: int    unit: projectplans:                        # Your pricing tiers  - id: pro    name: Pro    headline: For growing teams    prices:      monthly: { amount: 1900 }      yearly: { amount: 19000 }    limits:      projects: 25    features:      - Advanced analytics      - Priority supportaddons:                       # Optional add-ons  - id: extra_projects    name: Extra Projects    price: { amount: 1000 }    grants:      projects: "+10"promotions:                   # Coupon codes  - code: LAUNCH50    discount: { percent: 50 }    duration: { months: 3 }

Top-Level Properties

PropertyTypeRequiredDescription
version1YesSchema version (always 1)
providersarrayNoPayment providers to sync with (stripe, paddle, chargebee)
settingsobjectNoDefault settings for all plans
entitlementsobjectNoDefine available limit types
plansarrayYesList of pricing plans (minimum 1)
addonsarrayNoOptional add-on products
promotionsarrayNoPromotional codes and discounts

Settings

Default settings apply to all plans unless overridden.

yaml
settings:  currency: usd       # Default currency (ISO 4217, lowercase)  trial_days: 14      # Default trial period  grace_days: 7       # Grace period after failed payment
PropertyTypeDefaultDescription
currencystringusdThree-letter currency code (multi-currency support is planned)
trial_daysinteger0Default trial period in days
grace_daysinteger7Days before cancellation on failed payment

Entitlements

Entitlements define what limits and features your plans can include. Define them once, reference them in plans.

yaml
entitlements:  projects:    type: int    unit: project    description: Number of active projects  team_members:    type: int    unit: seat    description: Team members per workspace  api_requests:    type: rate    unit: request    description: API rate limit  sso:    type: bool    description: Single sign-on support

Entitlement Types

TypeDescriptionExample value in plan
intNumeric limitprojects: 25 or projects: unlimited
boolFeature flagsso: true
rateRate limitapi_requests: { limit: 1000, per: minute }

Rate Limits

For API rate limiting or similar time-based limits:

yaml
limits:  api_requests: { limit: 1000, per: minute }

Available time periods: second, minute, hour, day.

Plans

Plans define your pricing tiers.

yaml
plans:  - id: pro                     # Unique ID (snake_case)    name: Pro                   # Display name    headline: For growing teams # Pricing page tagline    description: Everything you need to scale    type: team                  # personal, team, enterprise    public: true                # Show on pricing page    default: false              # Mark as default plan    trial_days: 14              # Override global setting    prices:      monthly: { amount: 2900 }      yearly: { amount: 29000 }    limits:      projects: 25      api_requests: { limit: 1000, per: minute }      sso: false    features:                   # Marketing bullets      - Up to 25 projects      - Priority support    upgrades_to:                # Valid upgrade paths      - business      - enterprise    metadata:                   # Custom data      contact_sales: false

Plan Properties

PropertyTypeRequiredDescription
idstringYesUnique identifier (^[a-z][a-z0-9_]*$)
namestringYesDisplay name
headlinestringNoShort tagline for pricing page
descriptionstringNoLonger description
typeenumNopersonal, team, or enterprise
billing_modelenumNosubscription (default) or one_time
versionintegerNoPlan version for grandfathering (see Plan Versioning)
publicbooleanNoShow on pricing page (default: true)
defaultbooleanNoMark as default plan (default: false)
trial_daysintegerNoTrial period, overrides global setting
pricesobjectYesPricing configuration
limitsobjectNoEntitlement values
featuresarrayNoMarketing bullet points
upgrades_toarrayNoPlan IDs this plan can upgrade to
metadataobjectNoCustom key-value data

Pricing Models

Raterunner supports multiple pricing models.

1. Flat Pricing

Simple fixed price per billing period.

yaml
prices:  monthly: { amount: 2900 }   # $29/month  yearly: { amount: 29000 }   # $290/year

The amount is always in cents (smallest currency unit).

2. One-Time Payments

For lifetime access or one-time purchases, use billing_model: one_time:

yaml
plans:  - id: pro_lifetime    name: Pro Lifetime    billing_model: one_time    prices:      one_time: { amount: 7900 }   # $79 once

Key points: - Use billing_model: one_time to indicate a non-recurring plan - The price key must be one_time instead of monthly/yearly - Creates a Stripe Payment Intent instead of Subscription - No trial period support (trials only apply to subscriptions)

Use cases: - Lifetime deals (pay once, access forever) - One-time setup fees - Digital product purchases - Limited-time offers

3. Per-Unit Pricing

Price multiplied by quantity (seats, users, etc.).

yaml
prices:  monthly:    per_unit: 1200            # $12 per seat per month    unit: seat    min: 1                    # Minimum 1 seat    max: 100                  # Maximum 100 seats    included: 1               # First seat free
PropertyTypeDescription
per_unitintegerPrice per unit in cents
unitstringUnit name (seat, user, etc.)
minintegerMinimum quantity
maxintegerMaximum quantity
includedintegerUnits included in base price

4. Tiered Pricing

Progressive pricing that changes at quantity thresholds.

yaml
prices:  monthly:    tiers:      - up_to: 10        amount: 2000          # $20/seat for 1-10      - up_to: 50        amount: 1500          # $15/seat for 11-50      - up_to: unlimited        amount: 1000          # $10/seat for 51+    mode: graduated           # or: volume

Modes: - graduated — Each tier's price applies only to units within that tier (default) - volume — The tier's price applies to ALL units once you reach that tier

PropertyTypeDescription
up_tointeger or "unlimited"Upper bound of this tier
amountintegerPrice per unit in cents
flatintegerFlat fee for reaching this tier

Billing Periods

Plans can have any combination of billing periods:

yaml
prices:  monthly: { amount: 2900 }  quarterly: { amount: 7900 }  yearly: { amount: 29000 }

Addons

Addons are optional products that extend a plan's limits or add features.

yaml
addons:  - id: extra_projects    name: Extra Projects Pack    description: Add 10 additional projects to your plan    price:      amount: 1000            # $10/month    grants:      projects: "+10"         # Add 10 to existing limit  - id: priority_support    name: Priority Support    description: 24/7 priority support with 1-hour response time    price:      amount: 9900    requires_plan:            # Only available for these plans      - pro      - business

Grant Syntax

SyntaxMeaning
"+10"Add 10 to existing limit
"-5"Subtract 5 from existing limit
25Set to exactly 25
trueEnable boolean feature
unlimitedRemove limit entirely

Promotions

Promotional codes for discounts.

yaml
promotions:  - code: LAUNCH50              # Code customers enter (uppercase)    description: 50% off first 3 months    discount: { percent: 50 }   # 50% discount    duration: { months: 3 }     # For 3 months    new_customers_only: true    expires: "2026-06-30"    active: true  - code: ANNUAL20    description: 20% off annual plans    discount: { percent: 20 }    duration: forever           # Applies forever    applies_to:                 # Only these plans      - pro      - business    max_uses: 1000              # Total redemptions allowed

Discount Types

yaml
# Percentage discountdiscount: { percent: 50 }     # 50% off# Fixed amount discountdiscount: { fixed: 1000 }     # $10 off

Duration Options

yaml
duration: once                # First payment onlyduration: forever             # Every paymentduration: { months: 3 }       # First 3 months

Promotion Properties

PropertyTypeDescription
codestringPromo code (^[A-Z0-9_]+$)
descriptionstringInternal description
discountobject{ percent: N } or { fixed: N }
durationstring/objectonce, forever, or { months: N }
applies_toarrayPlan IDs (empty = all plans)
new_customers_onlybooleanOnly for new customers (default: true)
max_usesintegerTotal redemption limit
expiresstringExpiration date (YYYY-MM-DD)
activebooleanEnable/disable code

Provider ID Files

When you sync to a payment provider, Raterunner creates a provider ID file:

yaml
# stripe_sandbox.yaml (auto-generated)provider: stripeenvironment: sandboxsynced_at: "2026-01-22T18:45:00Z"plans:  pro:    product_id: prod_PwQ5lMnOpRsTuV    prices:      monthly: price_2OyBcDeFgHiJkLmN      yearly: price_3PzCdEfGhIjKlMnOaddons:  extra_projects:    product_id: prod_SzT8oPqRsTuVwXy    price_id: price_7TdGhIjKlMnOpQrS

These files are: - Auto-generated — don't edit manually - Environment-specific — separate files for sandbox/production - ID mappings only — no business logic - Committed to Git — for tracking and debugging

Provider Support

FeatureStripePaddleChargebee
Flat pricing
Per-unit pricing
Tiered graduated✅ Native❌ Backend
Tiered volume✅ Native❌ Backend
Metered billing✅ Native⚠️ Limited
Subscription mgmt
Tax handling✅ Stripe Tax✅ MoRVia gateway
Note: Currently only Stripe is fully supported. Paddle and Chargebee support is planned.

Validation

Validate your configuration before syncing:

bash
raterunner validate raterunner/billing.yaml

The CLI validates against the JSON Schema and checks for logical errors like: - Missing required fields - Invalid entitlement references - Duplicate IDs - Invalid pricing configurations

Full Example

See a complete billing.yaml with all features:

yaml
version: 1providers:  - stripesettings:  currency: usd  trial_days: 14  grace_days: 7entitlements:  projects:    type: int    unit: project  team_members:    type: int    unit: seat  api_requests:    type: rate    unit: request  sso:    type: bool  audit_logs:    type: boolplans:  - id: free    name: Free    headline: Get started for free    public: true    default: true    prices:      monthly: { amount: 0 }    limits:      projects: 3      team_members: 1      api_requests: { limit: 100, per: minute }      sso: false    features:      - Up to 3 projects      - Basic analytics      - Community support  - id: pro    name: Pro    headline: For growing teams    trial_days: 14    prices:      monthly:        per_unit: 1900        unit: seat        min: 1        included: 1      yearly:        per_unit: 15900        unit: seat        min: 1        included: 1    limits:      projects: 25      team_members: 25      api_requests: { limit: 1000, per: minute }      sso: false      audit_logs: true    features:      - Up to 25 projects      - Advanced analytics      - Priority email support    upgrades_to:      - enterprise  - id: pro_lifetime    name: Pro Lifetime    headline: Pay once, use forever    billing_model: one_time    prices:      one_time: { amount: 7900 }    limits:      projects: unlimited      team_members: 10      api_requests: { limit: 5000, per: minute }      sso: false      audit_logs: true    features:      - Lifetime access      - All future updates      - Priority support  - id: enterprise    name: Enterprise    headline: Custom solutions    prices:      monthly: { amount: 0 }    limits:      projects: unlimited      team_members: unlimited      api_requests: { limit: 50000, per: minute }      sso: true      audit_logs: true    features:      - Unlimited everything      - Dedicated success manager      - SLA guarantee    metadata:      contact_sales: trueaddons:  - id: extra_projects    name: Extra Projects Pack    price: { amount: 1000 }    grants:      projects: "+10"promotions:  - code: LAUNCH50    description: 50% off first 3 months    discount: { percent: 50 }    duration: { months: 3 }    new_customers_only: true    expires: "2026-06-30"    active: true