Skip to main content

Feature: Billing

Last updated: 2026-05-07

Plan Tiers

PlanPriceTarget
agent_starter$500/monthSmall orgs, single Specialist
agent_pro$1,500/monthGrowing orgs, multi-Specialist
enterpriseCustomLarge 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

  1. Org admin or superadmin creates a subscription: POST /billing/subscriptions
  2. Stripe creates the subscription and returns a subscription_id
  3. Webhook events update the subscriptions table:
    • invoice.payment_succeeded → subscription active
    • subscription_updated → plan change applied
    • subscription_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

MethodPathAuthDescription
POST/billing/subscriptionsJWTCreate Stripe subscription
GET/billing/subscriptionsSuperadminList all subscriptions
GET/billing/subscriptions/:orgIdJWTGet org subscription
DELETE/billing/subscriptions/:orgIdSuperadminCancel subscription
POST/billing/webhooksStripe sigStripe 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 date
  • TrialBanner component 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/subscriptions with payment_method: 'crypto' falls back to Stripe
  • POST /billing/webhooks/request-finance returns 404

Status

  • Stripe API integration: ✅ Implemented (mock mode if STRIPE_SECRET_KEY absent)
  • Request Finance: Disabled (PR #219) — ENABLE_REQUEST_FINANCE defaults to false
  • 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