Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions BILLING.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,42 @@
WORKSPACE BILLING

Pro is $20/month with $20 of usage, three seats and 10x classification quotas.
Plans change rate limits and included credits, nothing else. Pro is $20/month
with $20 of usage, three seats and 10x classification quotas. Scale is
$200/month with $200 of usage, unlimited seats and 100x quotas.
WorkOS handles sign-in. Workspace API keys use the classifier_agent_ prefix.
REST and MCP share the workspace balance and quota bucket.

Pay-as-you-go top-ups ($5-$1,000, whole dollars) add purchased funds that
never expire. Owners may enable auto recharge: when the balance falls below
their threshold, the saved payment method is charged their recharge amount,
bounded by an optional calendar-month maximum. Auto charges are claimed in
app_auto_topup_attempts first (single-flight lock, one-hour failure cooldown,
monthly-cap ledger) and the wallet is only ever credited by invoice-verified
reconciliation, exactly once per invoice id.

Autumn manages Stripe checkout and subscriptions. Verified paid invoices grant
each period's allowance once; returning from checkout does not activate Pro.
Customers manage subscriptions at /app/plans and credentials at /app/keys.
each period's allowance once; returning from checkout does not activate a plan.
Customers manage subscriptions at /app/plans, top-ups and auto recharge at
/app/credits, and credentials at /app/keys.

SETUP

- Set AUTUMN_SECRET_KEY, AUTUMN_WEBHOOK_SECRET and AUTUMN_PRO_PLAN_ID as Worker
secrets. The runtime key needs customer and billing read/write permissions.
- Set AUTUMN_SECRET_KEY, AUTUMN_WEBHOOK_SECRET, AUTUMN_PRO_PLAN_ID,
AUTUMN_SCALE_PLAN_ID and AUTUMN_TOPUP_PLAN_ID as Worker secrets. The runtime
key needs customer and billing read/write permissions. Plan ids are the
autumn.config.ts slugs (pro, scale, top_up); push config with npx atmn push.
- Keep BILLING_SIGNING_KEY stable: it derives personal billing customer IDs
from verified email addresses. Customer mappings and subscriptions remain
in place when credentials rotate.
- Configure /webhooks/autumn for billing.updated events. Scheduled reconciliation
repairs missed events. Billing state and credit reservations live in Neon.
repairs missed events and sweeps auto recharges. Billing state and credit
reservations live in Neon.
- Use npm run dev with sandbox credentials in ignored .dev.vars. Production
builds and deployments use wrangler.example.toml through CI.

VERIFICATION

Run npm test, npm run typecheck and the CLI tests. Account integration checks
are documented in docs/account-verification.md. Verify checkout, invoice grants,
repeat reconciliation, cancellation and key revocation with sandbox accounts.
top-up purchases, auto recharge caps, repeat reconciliation, cancellation and
key revocation with sandbox accounts.
38 changes: 38 additions & 0 deletions autumn.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import { atmn, feature, plan } from "atmn";

// Credits are Autumn's purchase unit for pay-as-you-go top-ups: 100,000 credits
// equal one dollar (src/lib/billing.ts). The wallet itself stays in Neon; a
// purchase only becomes spendable after its paid invoice is verified.
export default atmn({
features: [feature({
internalId: "fe_3JY0YuagHNTmVmJE9LZYjGWnMZJ",
featureId: "classifier_pro_limits",
name: "10× classification rate limits",
type: "boolean",
}), feature({
internalId: "fe_3JtqAMjN11vaz5HeK7iB4G2Ax39",
featureId: "classifier_scale_limits",
name: "100× classification rate limits",
type: "boolean",
}), feature({
internalId: "fe_3JtqAOKawqnNIF802NDz16CfBAy",
featureId: "credits",
name: "Usage credits",
type: "metered",
consumable: true,
})],
plans: [plan({
internalId: "prod_3JY0YnGHp7SRlaypoKnlwzJH4jE",
Expand All @@ -15,5 +29,29 @@ export default atmn({
name: "Pro",
price: { amount: 20, interval: "month" },
items: [{ featureId: "classifier_pro_limits" }],
}), plan({
internalId: "prod_3JtqAKECpKM8l96gDlt3K83BMvA",
planId: "scale",
versionSlug: "v1",
active: true,
name: "Scale",
price: { amount: 200, interval: "month" },
items: [{ featureId: "classifier_scale_limits" }],
}), plan({
internalId: "prod_3JtqAPIcSNONBZLSBBVD5eGvvfE",
planId: "top_up",
versionSlug: "v1",
active: true,
addOn: true,
name: "Credit top-up",
items: [{
featureId: "credits",
price: {
amount: 1,
billingUnits: 100_000,
billingMethod: "prepaid",
interval: "one_off",
},
}],
})],
});
4 changes: 3 additions & 1 deletion docs/autumn-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Autumn controls subscription checkout and portal access. Neon is the authoritati
- `AUTUMN_SECRET_KEY`: appropriate sandbox or production key.
- `AUTUMN_WEBHOOK_SECRET`: Svix signing secret for `/webhooks/autumn`.
- `AUTUMN_PRO_PLAN_ID`: the configured $20 monthly Pro plan.
- `AUTUMN_SCALE_PLAN_ID`: the configured $200 monthly Scale plan (optional).
- `AUTUMN_TOPUP_PLAN_ID`: the configured one-off prepaid top-up plan (optional; enables pay-as-you-go and auto recharge).
- `APP_ORIGIN`: trusted HTTPS application origin for checkout/portal return URLs; defaults to `https://classifier.dev`. Set an isolated preview origin for sandbox checkout.

Configure `billing.updated` delivery. Scheduled reconciliation repairs missed events with at most ten accounts per invocation and a global maximum of sixty provider calls per UTC day. The budget counts failed attempts; webhook calls are separate. Customers rotate by last attempted reconciliation, including provider failures, so unavailable customers cannot starve the rest of the queue. This repair budget is deliberately small and must be reviewed as the paid customer count grows.
Expand All @@ -15,7 +17,7 @@ Configure `billing.updated` delivery. Scheduled reconciliation repairs missed ev

Activation is not proof of payment. The reconciler fetches canonical customer state and `invoices.list`, then matches a paid invoice's base-plan line to the active subscription's current billing period. Invoice ID and account/period constraints prevent a second grant. New allowances wait for pending requests to settle and replace the previous included allowance; they do not stack. An overdue-payment flag does not cancel an active subscription or erase a verified paid allowance: the current period still requires its matching paid invoice.

The initial implementation supports **exactly $20 USD paid monthly Pro invoices**, with one $20 base-plan line and no refunded amount. Discounts, taxes, prorations, bundled invoices and historical invoices without recorded line items require explicit reconciliation; they do not silently grant credits. Only the first 100 paid invoices are inspected. Unsupported cases return a retryable synchronization failure and retain `reconciliation_required`.
Subscription grants support **exactly the configured USD monthly plan invoices** ($20 Pro, $200 Scale), with one matching base-plan line and no refunded amount. One-off top-up invoices for the configured top-up plan credit purchased funds once per invoice id, and their refunds revoke once. Discounts, taxes, prorations, bundled invoices and historical invoices without recorded line items require explicit reconciliation; they do not silently grant credits. Only the first 100 paid invoices are inspected. Unsupported cases return a retryable synchronization failure and retain `reconciliation_required`.

Refunds of a previously granted current-period invoice place the account on a billing hold immediately, preventing new reservations. Once pending work settles, the remaining included allowance is removed. The grant is marked revoked and cannot be reissued for the same period. Already consumed usage is not reverse-charged. A verified new paid period clears the hold. Pending requests are not retroactively interrupted. Refund webhooks remain retryable until those requests settle and allowance removal completes. Superseded reconciliations cannot acknowledge completion or mutate refund state.

Expand Down
4 changes: 2 additions & 2 deletions docs/billing-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ must be recorded as blockers, not bypassed with fabricated success.
- **Signup credit is $5, one-time and personal.** Each user gets only one personal workspace/plan and one initial grant. There is no daily or monthly free replenishment.
- **Teams have independent balances.** New team workspaces start with zero usage credit. Creating or switching to a team neither transfers personal credit nor creates another free grant. Members consume the team's balance; per-key/agent budgets are an optional further control.
- **Subscriptions replenish paid usage.** Pro costs $20/month and includes $20 at the published retail token rates. Paid tiers have higher rate/usage limits. Other tier prices, allowances, seat counts, rollover rules, and exact rate limits in the demo are provisional unless separately approved.
- **No top-ups for this release.** Neither manual purchases of extra credit nor automatic top-ups are enabled. An exhausted account receives a clear API error and can select an available subscription plan; there are no surprise overage charges.
- **Top-ups are live.** Manual purchases ($5-$1,000, whole dollars) and owner-enabled automatic recharges credit purchased funds only after their paid invoice is verified. Automatic recharges honor a balance threshold and an optional calendar-month maximum; an exhausted account with auto recharge disabled still receives a clear API error and no surprise overage charges.
- **Anonymous access stays as it is today.** Do not reduce its allowance or require signup as part of this migration. Authenticated free accounts on hosting/datacenter IPs should have stricter limits; paid plans retain their documented limits. Country alone does not identify abuse. No specific Jina residential-versus-datacenter rule has been verified; see [migration research](./migration-research.md).
- **One pricing and limits system for everyone.** Existing customers migrate into the new system at the verified cutover. No grandfathered tier or permanent parallel legacy billing path. Keep the current deployment working until the replacement and customer migration are ready.
- **Use one dollar presentation.** Balances, consumption, budgets, and subscription allowances display USD. The current accounting scale is 100,000 integer credits per dollar; choose rounding and minimum-charge rules explicitly with the retail token schedule before billing is enabled.
Expand Down Expand Up @@ -66,7 +66,7 @@ Autumn idempotency keys are not a permanent exactly-once guarantee. Its inspecte

## Product surfaces

- **Billing (`/app/credits`):** dollar balance, selected subscription, included usage, renewal/cancellation information, and transaction history. No Add funds or auto-top-up flow. Local actions must be labeled simulated.
- **Billing (`/app/credits`):** dollar balance, selected subscription, included usage, renewal/cancellation information, transaction history, a Top up balance flow and the auto recharge controls. Local actions must be labeled simulated.
- **Usage (`/app/usage`):** Spend / Tokens / Requests, date and credential filters, timeline, and sampling-aware AE aggregates. Show unknown token counts as unavailable and distinguish estimated retail charges from exact wallet balances.
- **Keys and agents:** credentials belong to the selected organization, and any optional spending cap uses the same dollar units and server-side policy as billing.
- **Onboarding and team creation:** one personal signup allowance; teams start unfunded. UI navigation or successful checkout navigation is never payment proof.
Expand Down
5 changes: 3 additions & 2 deletions docs/dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ configuring development credentials in `.dev.vars`; see
anonymous MCP remains available where a client supports it.
- Usage provides hourly/daily charts and filters. Activity lists recent requests.
Analytics can be delayed or sampled and must not be used as a billing ledger.
- Billing displays the plan, available funds, usage and upgrade actions. No
pay-as-you-go purchases or automatic top-ups are offered.
- Billing displays the plan, available funds, usage and upgrade actions, plus
pay-as-you-go top-ups ($5-$1,000) and owner-configured auto recharge with a
balance threshold and an optional calendar-month maximum.
- All app pages retain the sidebar. `/app/onboarding` remains directly accessible
for testing, with no ordinary navigation back to the completed onboarding.

Expand Down
27 changes: 27 additions & 0 deletions migrations/postgres/0016_pay_as_you_go.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-- Pay-as-you-go top-ups. A purchase becomes spendable only after its paid
-- invoice is verified; the invoice id is the idempotency key for the grant.
CREATE TABLE app_autumn_topups (
invoice_id TEXT PRIMARY KEY,
account_id TEXT NOT NULL REFERENCES app_accounts(id),
credits BIGINT NOT NULL CHECK(credits > 0),
amount_cents BIGINT NOT NULL CHECK(amount_cents > 0),
kind TEXT NOT NULL CHECK(kind IN ('top_up','auto_top_up')),
operation_id TEXT NOT NULL,
created_at TEXT NOT NULL,
revoked_at TEXT
);
CREATE INDEX app_autumn_topups_account ON app_autumn_topups(account_id,created_at);
-- Every automatic charge is claimed here first: the claim is the single-flight
-- lock, the failure cooldown, and the calendar-month spending-cap ledger.
CREATE TABLE app_auto_topup_attempts (
id TEXT PRIMARY KEY,
account_id TEXT NOT NULL REFERENCES app_accounts(id),
month TEXT NOT NULL,
amount_cents BIGINT NOT NULL CHECK(amount_cents > 0),
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','charged','failed')),
invoice_id TEXT,
reason TEXT,
created_at TEXT NOT NULL,
updated_at TEXT
);
CREATE INDEX app_auto_topup_attempts_account ON app_auto_topup_attempts(account_id,month,created_at);
2 changes: 1 addition & 1 deletion src/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ TYPESAFE SDK COMPATIBILITY
classifier_agent_...
A workspace key from https://classifier.dev/app/keys. Requests use the
workspace's shared quota and credit balance. Free workspaces keep the
same ceilings; Pro workspaces get 10x limits. Charges use TypeSafe's
same ceilings; Pro workspaces get 10x limits and Scale 100x. Charges use TypeSafe's
returned token usage and appear in workspace usage history.

Do not put a real TypeSafe API key here: classifier.dev never forwards caller
Expand Down
Loading
Loading