Feature: Billing
Last updated: 2026-05-07
Plan Tiers
| Plan | Price | Target |
|---|---|---|
agent_starter | $500/month | Small orgs, single Specialist |
agent_pro | $1,500/month | Growing orgs, multi-Specialist |
enterprise | Custom | Large orgs; manual billing |
Plans are created in Stripe and referenced by plan ID in the subscriptions table.
Quota Model
Each org has a message quota per billing period. Quota status is available via:
GET /orgs/:orgId/quota
Returns hourly message usage against the org's plan limit.
Quota enforcement: OrgThrottlerGuard (wired as APP_GUARD) applies per-org rate limiting. Current implementation is global per-IP — true per-org quota tracking is tracked in GitHub issue #5.
Stripe Integration
The BillingService wraps the Stripe API. When STRIPE_SECRET_KEY is not set, the service runs in mock mode — all billing operations succeed with fake data, allowing development and testing without real credentials.
Subscription Lifecycle
- Org admin or superadmin creates a subscription:
POST /billing/subscriptions - Stripe creates the subscription and returns a
subscription_id - Webhook events update the
subscriptionstable:invoice.payment_succeeded→ subscription activesubscription_updated→ plan change appliedsubscription_canceled→ org subscription marked inactive
Webhook Handler
POST /billing/webhooks — raw body required (Stripe signature verification).
Configure in Stripe dashboard: point to https://api.h.work/billing/webhooks (prod) / https://api.h852.work/billing/webhooks (staging).
API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /billing/subscriptions | JWT | Create Stripe subscription |
| GET | /billing/subscriptions | Superadmin | List all subscriptions |
| GET | /billing/subscriptions/:orgId | JWT | Get org subscription |
| DELETE | /billing/subscriptions/:orgId | Superadmin | Cancel subscription |
| POST | /billing/webhooks | Stripe sig | Stripe webhook handler |
Trial Period
- Default duration: 30 days from Phase 2 onboarding completion
- Set by AM at Step 1.4 (adjustable before invite is sent)
- Trial clock starts when client completes onboarding (
org.trialStartedAt) GET /client/trial-info— returns days remaining and trial end dateTrialBannercomponent displays countdown throughout client portal- Billing configuration is required before trial end (not before onboarding completion)
- AMs can extend the trial by updating
org.trialEndDate
Admin Billing View
GET /admin/billing — superadmin endpoint listing all org subscriptions and quota status.
Payment Providers
Stripe is the sole active payment provider. All subscriptions go through Stripe.
Request Finance (crypto payments) — code is preserved in billing/request-finance.provider.ts but disabled by default. Set ENABLE_REQUEST_FINANCE=true to re-enable. When disabled:
POST /billing/subscriptionswithpayment_method: 'crypto'falls back to StripePOST /billing/webhooks/request-financereturns 404
Status
- Stripe API integration: ✅ Implemented (mock mode if
STRIPE_SECRET_KEYabsent) - Request Finance: Disabled (PR #219) —
ENABLE_REQUEST_FINANCEdefaults tofalse - Real Stripe credentials: Required before first paying client
- Invoice history / download: Not implemented (tracked in USER_STORIES.md C-B3, P2 gap)
- VAT/tax ID: Collected at billing step, not yet wired to Stripe Tax