Billing
Cowliss billing is a prepaid wallet, not a subscription. There are no plans, tiers, or feature entitlements: money is the only gate, and every feature (journeys, custom sending domains, segments) is available to every org from the first event.
The model
Section titled “The model”- Free allowance: every org gets $1 per month, resetting at each UTC calendar month. Unused allowance does not roll over.
- Wallet: spend past the allowance draws a prepaid balance, funded by fixed-amount one-time payments ($10 / $50 / $200) through Stripe Checkout. Wallet credit carries over month to month; it is never debited per use and never expires.
- Remaining credit =
max(0, allowance + balance − spend). At zero, the platform hard-stops: ingestion answers 429over_quotawithRetry-After, and email/webhook sends recordskipped_quotain the delivery log. Nothing is ever billed in arrears: there is no overage invoice, because nothing can run past zero. - Warning: from 80% of credit used, the dashboard shows a top-up banner, so a rejection never surprises you.
What usage costs
Section titled “What usage costs”Usage is one weighted spend across four metered actions, computed in integer micro-dollars (1,000,000 per dollar: no float ever touches money):
| Action | Rate |
|---|---|
| Event ingested | $2 per 10,000 |
| Email sent | $4 per 10,000 |
| Webhook delivered | $2 per 10,000 |
| Journey run | $2 per 10,000 |
What counts, and what does not:
- Events are customer-written rows, including batch backfill. System events Cowliss writes itself (
system.segment_entered,system.segment_exited,system.consent_revoked, delivery feedback) are never metered. - Sends count when settled;
failed, skipped (skipped_*), and dry-run (would_*) deliveries are excluded by construction. - Journey runs count real instances; dry runs cost nothing.
- A price of 0 would make an action free without removing it from the meter: the catalog is one map in
packages/shared, tweakable in one place.
Spend is counted, never incremented: every number is a query over rows that already exist (ClickHouse events, the delivery log, journey_runs), so the meter can never drift from the thing it bills for, and a replayed webhook or a re-run backfill cannot double-charge.
Topping up
Section titled “Topping up”POST /v1/billing/topups { amountMicros: 10_000_000 } (a preset, not free-form) → 201 { checkoutUrl } (Stripe-hosted Checkout)… pay on Stripe …Stripe webhook checkout.session.completed → wallet credited, ledger row completedThe wallet is credited by the webhook and nowhere else: the topup endpoint answers only a checkout URL, and the ledger row it writes stays pending until Stripe confirms. The wallet_topups ledger is the audit trail of every top-up and its state.
Reading usage
Section titled “Reading usage”- Dashboard: Settings → Billing shows balance, this month’s spend against the allowance, remaining credit, and top-up history.
- API:
GET /v1/billingreturns the allowance, wallet balance, period bounds, spend, remaining credit withwarn/overflags, the usage counts, and the price catalog that weighted them. - CLI:
cow billingprints the same summary.
The hard stop is the same everywhere
Section titled “The hard stop is the same everywhere”One creditMicros function in packages/shared computes remaining credit for the API’s 429 on ingestion, the worker’s skipped_quota send gate, the billing page, and the dashboard banner: four surfaces, one arithmetic. A runaway journey hits the same wall as a runaway import: no bug can burn unbounded volume or credit.