Billing Subscriptions
Overviewβ
AttuneLogic billing now treats SaaS packaging as two separate concerns:
planTier: the tenant entitlement level that drives feature flags and product access.billing.collectionMethod: how the tenant pays AttuneLogic.
This separation allows some customers to pay inside the platform with Stripe while other customers stay on manual monthly contracts without creating custom feature-gating code paths per tenant.
Billing Modelβ
The API customer record remains the source of truth for billing state:
billing.providerbilling.collectionMethodbilling.planKeybilling.statusplanTier
Current collection methods:
manual_invoice: customer pays outside the product on contract, invoice, ACH, or check.stripe_subscription: customer uses Stripe Checkout and the Stripe billing portal.
Current self-serve plan keys:
founding_monthlystarter_monthlygrowth_monthlypro_monthly
If some Stripe price ids are not configured for the current environment:
- the plan still appears in the catalog as a known commercial package
- only plans with configured price ids are available for new self-serve checkout
- the default checkout plan falls back to the first configured self-serve plan
Recommended mental model:
billing.planKeyanswers "what commercial package was selected?"planTieranswers "what product access should this tenant have?"billing.collectionMethodanswers "how are we collecting money from this tenant?"
Entitlementsβ
Feature access still flows through tenant config and resolved feature flags:
- Tenant
planTieris loaded from the customer record. - Tier defaults are resolved from system config.
- Tenant
featureFlagOverridesare merged on top. - Clients and guarded routes read
featureFlags, not Stripe state directly.
This keeps backward compatibility for:
- manual-pay customers
- legacy founding tenants
- enterprise tenants with negotiated feature overrides
API Summary Contractβ
GET /api/v1/billing/summary returns billing data for settings and admin billing screens.
Important response sections:
billingAccount: normalized account state for the tenantoffer: the current commercial plan, when one is knowncatalog: available billing offers and default checkout planentitlements: monitor-only billing posture plus tier contextactions: whether checkout or portal actions are allowed
Example shape:
{
"billing": {
"billingAccount": {
"provider": "stripe",
"collectionMethod": "stripe_subscription",
"planKey": "growth_monthly",
"planTier": "tier2",
"status": "active"
},
"catalog": {
"defaultCheckoutPlanKey": "founding_monthly",
"selfServeOffers": [
{
"key": "growth_monthly",
"planTier": "tier2",
"collectionMethod": "stripe_subscription"
}
]
},
"actions": {
"canStartCheckout": false,
"canManagePortal": true
}
}
}
Migration Guidanceβ
For existing customers:
- Leave current manual-pay tenants on
billing.collectionMethod = manual_invoice. - Assign or confirm the correct
planTierfor each tenant. - Keep enterprise-specific exceptions in
featureFlagOverrides. - Move only opted-in customers to Stripe self-serve plans.
- Let Stripe checkout and webhooks update
billing.planKey,billing.status, andplanTier.
Backfill script:
npm run migrate:billing-backfill
Write mode:
npm run migrate:billing-backfill -- --write
Useful options:
--customerId=<id>to preview or update one tenant--limit=<n>to cap the number of processed tenants--verboseto print one line per changed tenant
For legacy founding customers:
- Keep
founding_monthlyin the catalog for backward compatibility. - Do not force-migrate existing tenants to a new plan key unless pricing and entitlements are intentionally changing.
Rollout Notesβ
- Billing remains monitor-only for product access in this phase.
requiresBillingAttentionshould drive messaging and follow-up, not hard lockouts.- If stricter delinquency enforcement is added later, implement it as an explicit entitlement policy with grace periods instead of raw webhook-state checks.
- Mobile should stay read-only for billing and deep-link to web billing management instead of implementing native app-store subscriptions first.
Billing & Subscriptions
AttuneLogic billing is implemented as a tenant-level SaaS subscription flow.
This document covers how the platform bills organizations for access to the software. It does not describe operational job invoicing, customer payments, or payout workflows inside a tenant.
Scopeβ
Keep these concerns separate:
- SaaS billing: what an AttuneLogic tenant pays AttuneLogic
- Entitlements: what features and limits the tenant can access
- Operational invoicing: what the tenant bills their own customers for jobs
Current Billing Modelβ
The current commercial model is intentionally simple:
- One flat monthly subscription per organization
- Stable internal plan key:
founding_monthly - Billing provider: Stripe
- Billing state is tracked on the tenant
Customerrecord
This gives the product a clean launch path while leaving room for future add-ons such as seats, locations, or advanced modules.
Tenant Billing Stateβ
Billing data is stored on Customer.billing and kept backward compatible with the existing paymentStatus field.
Key fields:
providercustomerIdsubscriptionIdstatusplanKeybillingModelpriceIdpriceIntervalcurrentPeriodEndcancelAtPeriodEndprocessedWebhookEventIdseventLog
Legacy field retained during rollout:
paymentStatus
API Endpointsβ
Authenticated tenant admin endpoints:
GET /api/v1/billing/summaryPOST /api/v1/billing/checkout-sessionPOST /api/v1/billing/portal
Webhook endpoints:
POST /api/v1/billing/webhookPOST /api/v1/billing/subscriptions
/subscriptions remains as a backward-compatible alias for older webhook wiring.
Webhook Source Of Truthβ
Stripe webhooks are the source of truth for subscription state.
Handled events include:
checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_succeededinvoice.paidinvoice.payment_failed
Webhook processing is idempotent per tenant through processedWebhookEventIds, with a small event log retained on the billing object for debugging.
Summary Response Shapeβ
GET /api/v1/billing/summary returns a normalized payload for web settings screens:
billingAccountoffersubscriptionpaymentMethodsinvoiceshasStripeCustomeractions
This lets clients render a stable billing experience without reading raw tenant metadata directly.
Entitlementsβ
Billing does not directly replace feature flags or tier logic.
Use:
Customer.planTierfor packaging- tenant config and feature flags for per-feature behavior
- billing status for subscription health and rollout enforcement
During rollout, billing can remain monitor-only while existing tenants stay on manual terms.