# How OpenSEO Handles Billing for the Hosted Version: Autumn Platform Integration

> OpenSEO's hosted version uses Autumn platform for usage-based billing with a 28% markup. Discover how billing works for SaaS and self-hosted instances.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: internals
- Published: 2026-08-30

---

**OpenSEO’s hosted version implements usage-based billing through the Autumn platform, applying a 28% markup to DataForSEO costs while maintaining separate credit pools for monthly allocations and top-ups, with all logic gated behind an environment detection check that distinguishes SaaS deployments from self-hosted instances.**

The OpenSEO repository (every-app/open-seo) supports two distinct deployment models: self-hosted instances running on private infrastructure and the managed SaaS offering at openseo.so. The hosted version implements a comprehensive billing system that transforms raw DataForSEO API costs into customer charges through the Autumn billing platform, featuring automatic customer provisioning, subscription verification, and real-time usage credit tracking. Understanding how OpenSEO handles billing for the hosted version requires examining the runtime environment detection, the Autumn SDK integration, and the shared credit pool mechanics that differentiate SaaS pricing from self-hosted raw rates.

## Runtime Environment Detection: Hosted vs. Self-Hosted

All billing logic depends on `isHostedServerAuthMode()` defined in [`src/server/lib/runtime-env.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/runtime-env.ts). When this function returns `true`, the request executes billing-aware code paths including customer creation and credit deduction. Self-hosted deployments return `false` here, skipping all Autumn-related charges entirely and allowing users to utilize their own DataForSEO API keys at raw cost.

```typescript
import { isHostedServerAuthMode } from "@/server/lib/runtime-env";

if (await isHostedServerAuthMode()) {
  // Execute billing logic: check credits, apply markup, track usage
} else {
  // Self-hosted mode: use raw DataForSEO pricing without platform fees
}

```

## Customer Lifecycle and Subscription Management

### Provisioning Autumn Customers

When users first access paid features, `getOrCreateOrganizationCustomer()` in [`src/server/billing/subscription.ts`](https://github.com/every-app/open-seo/blob/main/src/server/billing/subscription.ts) creates an Autumn customer record tied to the organization ID. The function caches customer existence in Workers KV to minimize redundant API calls and ensure idempotent operations.

```typescript
import { getOrCreateOrganizationCustomer } from "@/server/billing/subscription";

const billingCustomer = await getOrCreateOrganizationCustomer({
  organizationId: ctx.organizationId,
  userEmail: ctx.userEmail,
  userId: ctx.userId,
});

```

### Validating Plan Access

The system verifies entitlements through `customerHasPaidPlan()` and `customerHasManagedAccess()` in the same file. These functions call `autumn.check()` with feature identifiers `AUTUMN_PAID_PLAN_FEATURE_ID` and `AUTUMN_MANAGED_ACCESS_FEATURE_ID` to determine whether an organization may access managed-service features beyond the trial tier.

```typescript
import { customerHasPaidPlan } from "@/server/billing/subscription";

const hasPlan = await customerHasPaidPlan(billingCustomer.id);
if (!hasPlan) {
  // Trigger upgrade flow or restrict feature access
}

```

## Usage Credit System and Cost Calculation

### Shared Credit Pool Architecture

Hosted customers draw from a unified credit pool comprising a monthly balance (`usage_credits`) and an optional top-up balance (`topup_credits`). The function `getUsageCreditsRemaining()` retrieves available balances, while `trackUsageCreditSpend()` deducts costs atomically against this pool before executing costly operations like DataForSEO lookups or LLM inference.

```typescript
import { trackUsageCreditSpend } from "@/server/billing/subscription";

await trackUsageCreditSpend({
  customer: billingCustomer,
  customerId: billingCustomer.id,
  creditFeature: "seo_data_usage",
  costUsd: rawDataForSeoCost,
  monthlyRemaining: remainingCredits.monthlyRemaining,
});

```

### The 28% Platform Markup

Unlike self-hosted users who pay DataForSEO directly, hosted customers incur a platform fee. The `applyBillingMarkupUsd()` function in [`src/shared/billing.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/billing.ts) implements this by multiplying raw USD amounts by `SEO_DATA_COST_MARKUP` (set to 1.28) and rounding via `roundUsdForBilling()`.

```typescript
import { applyBillingMarkupUsd } from "@/shared/billing";

const rawCost = 0.12; // DataForSEO API cost
const billedCost = applyBillingMarkupUsd(rawCost); // Returns 0.154 USD (28% markup)

```

## Telemetry and Analytics Integration

After each credit deduction, `trackUsageCreditSpend()` emits PostHog events defined in [`src/server/lib/posthog.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/posthog.ts), including `usage:credits_consume` for successful transactions and `usage:credits_gate_refused` when the pool is depleted. These events feed the hosted UI with real-time usage statistics and trigger "Insufficient credits" alerts when balances approach zero.

If the credit pool is exhausted during a request, the system throws `AppError("INSUFFICIENT_CREDITS")`, blocking the operation and prompting the user to purchase additional top-up credits.

## Retrieving Historical Billing Data

The server function `getBillingUsageEvents()` in [`src/serverFunctions/billing.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/billing.ts) queries the Autumn event log to display usage history in the hosted dashboard. This endpoint contains an additional `isHostedServerAuthMode()` guard to ensure self-hosted instances never attempt to fetch Autumn data or expose billing history endpoints.

## Key Files in the Billing Pipeline

- **[`src/server/lib/runtime-env.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/runtime-env.ts)** – Determines deployment mode via `isHostedServerAuthMode()`, gating all billing logic
- **[`src/server/billing/subscription.ts`](https://github.com/every-app/open-seo/blob/main/src/server/billing/subscription.ts)** – Core billing orchestration: customer creation, plan verification, and credit management
- **[`src/shared/billing.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/billing.ts)** – Constants for Autumn feature IDs, markup calculations (`applyBillingMarkupUsd`), and rounding utilities
- **[`src/server/billing/autumn.ts`](https://github.com/every-app/open-seo/blob/main/src/server/billing/autumn.ts)** – Lazy-initialized SDK wrapper providing `check`, `track`, and `customers.getOrCreate` methods
- **[`src/serverFunctions/billing.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/billing.ts)** – API surface for historical billing event retrieval (hosted-only)
- **[`src/server/lib/posthog.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/posthog.ts)** – Analytics telemetry for credit consumption and gating events

## Summary

- OpenSEO uses the **Autumn** billing platform exclusively for its hosted SaaS deployment, while self-hosted instances bypass all billing logic through `isHostedServerAuthMode()`
- All DataForSEO costs incur a **28% platform markup** via `applyBillingMarkupUsd()` in [`src/shared/billing.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/billing.ts), implemented as `SEO_DATA_COST_MARKUP = 1.28`
- Usage operates on a **shared credit pool** combining monthly allocations and top-up balances, tracked through `getUsageCreditsRemaining()` and `trackUsageCreditSpend()` in [`src/server/billing/subscription.ts`](https://github.com/every-app/open-seo/blob/main/src/server/billing/subscription.ts)
- The system emits **PostHog telemetry events** for credit consumption and refusal, enabling real-time usage monitoring in the hosted UI
- **Self-hosted users** pay raw DataForSEO rates using their own API keys, while hosted customers manage billing entirely through Autumn with automatic customer provisioning and usage-based deductions

## Frequently Asked Questions

### How does OpenSEO distinguish between hosted and self-hosted billing?

OpenSEO checks `isHostedServerAuthMode()` in [`src/server/lib/runtime-env.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/runtime-env.ts) at the start of every request. When this returns `true`, the code executes Autumn billing logic including credit checks and markup application. Self-hosted deployments return `false`, causing the system to skip all billing gates and use raw DataForSEO pricing with the user's own API credentials.

### What percentage markup does the hosted version apply to DataForSEO costs?

The hosted version applies a **28% platform markup** to all DataForSEO usage. The constant `SEO_DATA_COST_MARKUP` is set to `1.28` in [`src/shared/billing.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/billing.ts), and the function `applyBillingMarkupUsd()` multiplies raw costs by this factor before deducting from the user's credit balance.

### Can self-hosted users access the Autumn billing features or usage credit system?

No. Self-hosted instances cannot access Autumn billing features, usage credit tracking, or the markup calculation system. The `getBillingUsageEvents()` endpoint and all `trackUsageCreditSpend()` calls are gated behind `isHostedServerAuthMode()`, ensuring self-hosted users interact directly with DataForSEO and LLM providers using their own accounts and API keys.

### What happens when a hosted customer exhausts their usage credits?

When the combined monthly and top-up credit balance reaches zero, `trackUsageCreditSpend()` emits a `usage:credits_gate_refused` PostHog event and throws `AppError("INSUFFICIENT_CREDITS")`. This blocks the current request and triggers the UI to display an "Insufficient credits" message, requiring the user to purchase additional credits before continuing to use paid features.