Skip to main content

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.provider
  • billing.collectionMethod
  • billing.planKey
  • billing.status
  • planTier

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_monthly
  • starter_monthly
  • growth_monthly
  • pro_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.planKey answers "what commercial package was selected?"
  • planTier answers "what product access should this tenant have?"
  • billing.collectionMethod answers "how are we collecting money from this tenant?"

Entitlements​

Feature access still flows through tenant config and resolved feature flags:

  1. Tenant planTier is loaded from the customer record.
  2. Tier defaults are resolved from system config.
  3. Tenant featureFlagOverrides are merged on top.
  4. 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 tenant
  • offer: the current commercial plan, when one is known
  • catalog: available billing offers and default checkout plan
  • entitlements: monitor-only billing posture plus tier context
  • actions: 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:

  1. Leave current manual-pay tenants on billing.collectionMethod = manual_invoice.
  2. Assign or confirm the correct planTier for each tenant.
  3. Keep enterprise-specific exceptions in featureFlagOverrides.
  4. Move only opted-in customers to Stripe self-serve plans.
  5. Let Stripe checkout and webhooks update billing.planKey, billing.status, and planTier.

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
  • --verbose to print one line per changed tenant

For legacy founding customers:

  • Keep founding_monthly in 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.
  • requiresBillingAttention should 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 Customer record

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:

  • provider
  • customerId
  • subscriptionId
  • status
  • planKey
  • billingModel
  • priceId
  • priceInterval
  • currentPeriodEnd
  • cancelAtPeriodEnd
  • processedWebhookEventIds
  • eventLog

Legacy field retained during rollout:

  • paymentStatus

API Endpoints​

Authenticated tenant admin endpoints:

  • GET /api/v1/billing/summary
  • POST /api/v1/billing/checkout-session
  • POST /api/v1/billing/portal

Webhook endpoints:

  • POST /api/v1/billing/webhook
  • POST /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.completed
  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • invoice.payment_succeeded
  • invoice.paid
  • invoice.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:

  • billingAccount
  • offer
  • subscription
  • paymentMethods
  • invoices
  • hasStripeCustomer
  • actions

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.planTier for 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.