Billing Service Setup
Learn how to set up, configure, and run the Reloop billing service locally.
The Billing Service is an ElysiaJS microservice that handles user subscription tiers, credits tracking, billing transaction history, and Stripe portal integration. It acts as the platform's gatekeeper, enforcing sending limits and managing invoice history.
Before configuring the Billing Service, ensure you have completed the base Local Development Setup (installed Bun, configured the hosts file, and started the Docker container stack).
Quick Start
You can boot the Billing Service development server instantly using workspace scripts from the root directory:
# Start the dev server with hot reloading
bun be:billing:dev
cd apps/backend/billing
bun dev
Once running, the Billing API will listen locally on port 8023. You can query it via Caddy at https://local.reloop.sh/api/billing 🎉
Service Information
- Directory Location:
apps/backend/billing/ - Local Listening Port:
8023(ElysiaJS gateway) - Unified Access Route: https://local.reloop.sh/api/billing
- Primary Tech Stack: ElysiaJS, PostgreSQL, Redis, NATS
- Dependencies: Relies on PostgreSQL (to record transactions/invoices) and NATS (to receive subscription & usage update triggers).
Environment Configuration
The Billing Service environment variables are defined in apps/backend/billing/.env. Running bun env:setup at the root populates this file with defaults:
# Server Port Configuration
BILLING_PORT=8023
NODE_ENV=development
# Core Platform Settings
INITIAL_CREDITS=100
# Event Bus Broker
NATS_URL=nats://localhost:4222
Reference Table
| Variable | Required | Default Value | Description |
|---|---|---|---|
BILLING_PORT | YES | 8023 | Port number the Elysia app listens to on the host. |
INITIAL_CREDITS | YES | 100 | Free email credits allocated to a newly created organization. |
NATS_URL | YES | nats://localhost:4222 | Connection URL for NATS event bus messaging. |
NODE_ENV | YES | development | Active environment scope (development or production). |
(Note: Shared database connections inherit PG_URL globally through root workspace parameters.)
Development Commands
Execute billing service operations from the monorepo root directory using Turborepo filters:
| Command | Action | Scope |
|---|---|---|
bun be:billing:dev | Start the Billing dev server with hot reloading | Development |
bun run --filter=billing build | Compile the Billing service for production | Production |
bun run --filter=billing start | Run the compiled production build locally | Production |
bun run --filter=billing check-types | Verify TypeScript type-safety | Code Quality |
Database Schema & Relationships
The relational subscription packages, invoice schedules, credit balance ledgers, and usage tracking schemas are defined inside packages/db/src/schema/billing.ts.
Enums
| Enum | Values | Description |
|---|---|---|
billing_cycle | monthly · annual | Charging occurrence frequency |
subscription_status | active · past_due · cancelled · trialing · paused | Workspace subscription lifecycle state |
ledger_entry_type | credit_purchased · email_sent · rollover_applied · manual_adjustment · refund · plan_change · period_reset | Transaction types in the credit ledger |
email_send_status | queued · sent · skipped · failed · bounced | Overage/credit tracking delivery state |
skip_reason | over_limit · unsubscribed · duplicate · invalid_address · suppressed · dry_run | Logical skips avoiding billing drops |
invoice_status | draft · open · paid · void · uncollectible | Invoice collection states |
plan Table
Defines base tiers, credits entitlements, and limits for the platform.
| Column | Type | Default | Description |
|---|---|---|---|
id | text | pln_{cuid} | Primary Key |
name | varchar(100) | — | Tier display name (e.g. Startup) |
monthlyCredits | integer | — | Total default monthly volume capacity |
overageLimit | integer | 0 | Absolute ceiling allowed past base capacity |
basePriceUsd | decimal | — | Base charge rate for the package |
overagePricePerEmail | decimal | 0 | Unit charge per overage dispatch |
billingCycle | billing_cycle | monthly | Plan billing recurring frequency |
rolloverEnabled | boolean | false | Carries unused volume to next periods |
maxRolloverCredits | integer | null | Cap limit for rollovers |
ratePerSecond | integer | 50 | Concurrent throughput limit |
maxAttachmentSizeMb | integer | 10 | Size capacity for outbound uploads |
isActive | boolean | true | System offering visibility flag |
subscription Table
Links a workspace/organization to a chosen subscription tier with active usage counters.
| Column | Type | Default | Description |
|---|---|---|---|
id | text | sub_{cuid} | Primary Key |
organizationId | text | — | Foreign Key → organization.id |
planId | text | — | Foreign Key → plan.id |
status | subscription_status | active | Active subscription state |
creditsUsed | integer | 0 | Base package credits spent |
creditsRemaining | integer | 0 | Unspent base credits remaining |
rolloverCredits | integer | 0 | Accumulated unspent credits carried over |
overageCreditsUsed | integer | 0 | Total overage consumed in period |
currentPeriodStart | timestamp | — | Current billing window lower bound |
currentPeriodEnd | timestamp | — | Current billing window upper bound |
externalSubscriptionId | varchar | null | Associated Stripe subscription ID |
credit_ledger Table
Audit ledger tracking every transactional shift in a workspace's credit balance.
| Column | Type | Default | Description |
|---|---|---|---|
id | text | cld_{cuid} | Primary Key |
organizationId | text | — | Foreign Key → organization.id |
subscriptionId | text | — | Foreign Key → subscription.id |
entryType | ledger_entry_type | — | Type of transaction performed |
delta | integer | — | Change magnitude (+/- credits shift) |
balanceAfter | integer | — | Updated ledger total after change |
reason | text | null | Narrative descriptive context |
referenceId | text | null | External reference (e.g. email_send.id) |
Other Billing Tables
billing_invoice: Captures invoice statements, credits included/used, overage totals, and payment confirmation statuses.email_send: High-performance operational log mapping outbound messages to specific subscription balances for overage verification and credit deductions.
Interactive API Docs (Swagger)
The Billing Service exposes dynamic OpenAPI specs. To run test queries and explore billing endpoints:
- Open your browser and navigate to the interactive Swagger playground at https://local.reloop.sh/api/billing/openapi (or http://localhost:8023/api/billing/openapi directly).
- Use the interactive Swagger UI playground to inspect schemas and execute test queries.
Core Features Explained
- Credit Tracking & Balances: Deducts outbound email credits in real time, communicating with mailing queues to block campaigns if a workspace's balance reaches
0. - NATS Pub-Sub Integration: Subscribes to events like
billing.quota-warningto trigger system emails when a workspace consumes 80% or 100% of its credit allocation. - Stripe Subscriptions: Interfaces with Stripe billing portals, handling webhook listeners for webhook payments, subscription renewals, cancellations, and invoicing records.
- Relational Billing Schemas: Stores ledger data, monthly credits usage metrics, Stripe metadata, and transaction states inside the PostgreSQL
billing_invoiceandbilling_transactiontables.