Skip to main content

Billing

Optional Stripe subscriptions for deployments that charge users. Off by default.

NeoAgent includes an optional Stripe-based billing system for deployments that charge users for access. When disabled โ€” the default โ€” no billing routes are exposed, no payment UI is shown, and no payment-related information appears anywhere in the application.

โšก Enable billingโ€‹

Run the interactive setup wizard:

neoagent billing setup

The wizard prompts for your Stripe API keys, webhook secret, and trial length, then asks whether to enable billing immediately. Use Stripe test keys (sk_test_..., pk_test_...) during development.

To check the current configuration at any time:

neoagent billing # or: neoagent billing status

To toggle billing without re-running setup:

neoagent billing enable # set NEOAGENT_BILLING_ENABLED=true and restart
neoagent billing disable # set NEOAGENT_BILLING_ENABLED=false and restart

When enabled the admin dashboard will show a Billing navigation item and the /api/billing/* endpoints become active.

Manual alternative

You can also set variables directly and restart:

neoagent env set STRIPE_SECRET_KEY sk_live_...
neoagent env set STRIPE_PUBLISHABLE_KEY pk_live_...
neoagent env set STRIPE_WEBHOOK_SECRET whsec_...
neoagent env set NEOAGENT_BILLING_ENABLED true
neoagent restart

๐Ÿ“ฆ Subscription plansโ€‹

Plans are managed in Admin โ€บ Billing โ€บ Plans. Each plan controls:

FieldPurpose
NameDisplayed to users in the settings submenu
PriceStripe price in cents (0 = free)
Billing intervalmonth, year, or blank for one-time or free
Stripe Price IDThe price_... ID from your Stripe dashboard
4h token limitPer-user 4-hour rolling token budget (blank = server default)
Weekly token limitPer-user 7-day rolling token budget (blank = server default)
Allowed modelsComma-separated model IDs; blank = all models accessible
FeaturesMarketing feature strings shown on pricing pages

Token limits are written directly to the user account when a subscription becomes active or is updated via webhook. The existing rate-limit enforcement in the runtime reads them with no additional configuration.

Token limits gate interactive run starts. Background task executions are not blocked by the internal token admission check, so scheduled and integration tasks can still run while a user is over quota. Their token usage is still recorded and included in account and admin usage snapshots.

๐ŸŽ Free planโ€‹

Create a plan with Price = 0 to serve as the default for new users. If a free plan exists when billing is enabled, every new registration is automatically placed on it.

๐ŸŽš๏ธ Model restrictionsโ€‹

If Allowed models is non-empty, users on that plan can only use the listed model IDs. Models outside the allowlist remain visible in the UI with an unavailable status so users understand what upgrading unlocks.

Leave the field blank to allow all configured models on a plan.

๐Ÿ‘ค User subscriptionsโ€‹

Users manage their subscription in Settings โ€บ Billing (Flutter client). From there they can:

  • View their current plan, status, and next renewal date
  • Upgrade or downgrade through Stripe Checkout
  • Open the Stripe Customer Portal to update a payment method or download invoices
  • Cancel at the end of the current billing period

๐Ÿ’ณ Stripe Checkoutโ€‹

When a user selects a paid plan, the server creates a Stripe Checkout session and redirects the client to Stripe's hosted payment page. No card data passes through NeoAgent.

๐Ÿงพ Stripe Customer Portalโ€‹

The Customer Portal is a Stripe-hosted page where users can update their payment method, view billing history, and cancel. Configure the portal in your Stripe dashboard before enabling it.

โณ Free trialsโ€‹

Enable free trials in Admin โ€บ Billing โ€บ Plans by configuring a Stripe price that supports trials, then setting BILLING_TRIAL_DAYS to the desired length (default: 14 days).

Trials start when a user calls POST /api/billing/trial with a plan ID. The server runs anti-abuse checks before granting a trial:

CheckLimit
IP address2 trials per IP per 30 days
Email domain3 trials per non-common domain per 30 days
Account ageAccount must be at least 1 day old
Device fingerprint1 trial per device per 90 days (opaque hash from client)

The Flutter client is responsible for generating and sending the device fingerprint. The server hashes it with SHA-256 before storage โ€” the raw fingerprint is never persisted.

๐Ÿช Webhooksโ€‹

Stripe sends events to POST /api/billing/webhook. Register this URL in your Stripe dashboard under the endpoint for your account.

Handled events:

EventEffect
customer.subscription.createdCreates a local subscription row
customer.subscription.updatedSyncs status, period dates, and token limits
customer.subscription.deletedMarks subscription canceled
customer.subscription.trial_will_endSends trial-ending email (fires 3 days before)
invoice.payment_succeededRecords payment; sends renewal email on cycle
invoice.payment_failedRecords failure; sends payment-failed email

Events are idempotent โ€” replaying a webhook event produces the same result.

Required events to enable in Stripe:

customer.subscription.created
customer.subscription.updated
customer.subscription.deleted
customer.subscription.trial_will_end
invoice.payment_succeeded
invoice.payment_failed

๐Ÿงช Verifying the webhook locallyโ€‹

Use the Stripe CLI to forward events to a local server during development:

stripe listen --forward-to http://localhost:3333/api/billing/webhook

๐Ÿ“ง Email notificationsโ€‹

If service email is configured, users receive emails at the following billing events:

EventEmail
Trial startedConfirmation with trial end date
Trial ending soonReminder 3 days before end
Subscription activatedWelcome to the paid plan
Subscription renewedRenewal confirmation
Payment failedAction required with next retry date
Subscription canceledCancellation confirmation
Plan changedSummary of the new plan

No emails are sent if SMTP is not configured.

๐Ÿง  AI context awarenessโ€‹

When billing is enabled, each AI request includes the user's subscription in the system prompt:

SUBSCRIPTION: User is on the "Pro" plan, status: active.

This lets the agent answer questions about the user's plan without promising models or limits the plan does not include.

๐Ÿ› ๏ธ Admin controlsโ€‹

The Admin โ€บ Billing page provides:

  • Plans โ€” create, edit, and deactivate subscription plans
  • Subscriptions โ€” every user subscription, with status filter and pagination
  • Override โ€” per-row button that assigns a user to a plan without going through Stripe, for comped accounts or repairing a missed webhook

๐Ÿ“‹ Configuration referenceโ€‹

VariableDefaultPurpose
NEOAGENT_BILLING_ENABLEDfalseMaster on/off switch
STRIPE_SECRET_KEYrequiredStripe server-side API key
STRIPE_PUBLISHABLE_KEYrequiredStripe client-side key (returned to Flutter)
STRIPE_WEBHOOK_SECRETrequiredWebhook signing secret from Stripe dashboard
BILLING_TRIAL_DAYS14Free trial length in days

All variables can be set with neoagent env set or added to ~/.neoagent/.env directly. Restart the server after any change.

๐Ÿ” Security notesโ€‹

  • NeoAgent stores only Stripe customer IDs and subscription IDs โ€” no card numbers, bank details, or PII beyond what Stripe already holds.
  • Webhook payloads are verified with HMAC-SHA256 using STRIPE_WEBHOOK_SECRET before any processing. Requests missing the stripe-signature header are rejected with 400.
  • Trial fingerprints are stored as SHA-256 hashes โ€” the raw IP, email domain, and device identifiers are never persisted.
  • All billing routes return 404 when NEOAGENT_BILLING_ENABLED is not set, regardless of whether Stripe credentials are present.