# How OpenSEO Handles AI-Powered Features: Onboarding Agent and Content Suggestions Architecture

> Discover how OpenSEO integrates AI features like its onboarding agent and content suggestions using Cloudflare AI Chat SDK and Durable Objects for seamless user experience and efficient resource management.

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

---

**OpenSEO implements AI-powered onboarding and content suggestions through Cloudflare's AI-Chat SDK and DataForSEO-integrated tool functions, using Durable Objects for persistent chat sessions and a unified credit-based billing system to meter both LLM tokens and SEO data API calls.**

The **every-app/open-seo** repository delivers intelligent SEO assistance by combining edge-computing infrastructure with real-world search data. Its **onboarding agent** provides new users with a limited, credit-aware conversational experience that demonstrates the platform's capabilities while enforcing strict usage boundaries. This article examines the technical implementation of these AI features, from the Durable Object architecture to the integrated billing mechanisms that power the **content suggestions** engine.

## The OnboardingChatAgent Durable Object Architecture

OpenSEO's **onboarding agent** is implemented as a Cloudflare Durable Object that ensures persistent state across worker restarts. The `OnboardingChatAgent` class extends `AIChatAgent` and is defined in [`/src/server/features/onboarding/OnboardingChatAgent.ts`](https://github.com/every-app/open-seo/blob/main//src/server/features/onboarding/OnboardingChatAgent.ts), where it manages a short, free-preview chat experience referred to internally as "Sam."

The agent authenticates the user's organization and enforces a free-question limit through a system prompt constructed between lines 26 and 55. This prompt encodes specific UX constraints including tone directives, emoji restrictions, and explicit credit-aware messaging guidelines. The LLM model itself is obtained via `getChatAgentModel` from [`/src/server/lib/openrouter.ts`](https://github.com/every-app/open-seo/blob/main//src/server/lib/openrouter.ts), which connects to OpenRouter for inference.

Each chat session persists in a **SQLite-backed Durable Object**, guaranteeing that conversation state survives across Cloudflare Worker restarts. This architecture enables stateful interactions while maintaining the scalability benefits of edge computing.

## AI Tool Integration with DataForSEO

The agent's capabilities are extended through a curated set of tools constructed by `buildOnboardingTools` in [`/src/server/features/onboarding/onboardingChatTools.ts`](https://github.com/every-app/open-seo/blob/main//src/server/features/onboarding/onboardingChatTools.ts). These tools expose **DataForSEO** functionality to the LLM as callable functions, categorized into low-cost core tools and credit-consuming market tools.

**Low-cost tools** like `read_website`, `get_seo_metrics`, and `research_keywords` are available without consuming credits, while **market-level tools** including `get_domain_overview`, `get_serp_results`, `find_serp_competitors`, `get_competitor_keywords`, and `get_backlinks_overview` deduct from the onboarding credit bucket. Each tool forwards to a service layer that ultimately invokes the DataForSEO client at [`/src/server/lib/dataforseo/client.ts`](https://github.com/every-app/open-seo/blob/main//src/server/lib/dataforseo/client.ts).

The `meter` helper function in the DataForSEO client tags each request with the `creditFeature: "onboarding"` property, enabling the billing layer to track consumption accurately. This design prevents accidental over-spending by requiring explicit credit checks before executing expensive market data calls.

## Content Suggestions Implementation

**Content suggestions** are generated through the `research_keywords` tool, which demonstrates OpenSEO's value by providing real search volume and keyword difficulty data. When given a seed topic, the tool calls `dataforseo.keywords.suggestions` and returns structured data that the agent formats into a Markdown table of target keywords.

This implementation reuses the same DataForSEO service that powers the full-product "SAM" agent, but the onboarding version limits calls to a single seed topic and caps credit usage explicitly. The integration allows new users to receive actionable SEO insights based on actual search data rather than synthetic recommendations.

## Unified Credit-Based Billing System

All AI consumption flows through a unified billing model defined in [`/src/server/billing/subscription.ts`](https://github.com/every-app/open-seo/blob/main//src/server/billing/subscription.ts). Within `OnboardingChatAgent.onChatMessage`, after the model finishes generating a response, the `onFinish` callback (lines 70-84 in [`OnboardingChatAgent.ts`](https://github.com/every-app/open-seo/blob/main/OnboardingChatAgent.ts)) aggregates costs from both OpenRouter token usage and DataForSEO API calls.

The billing logic calculates `openRouterCostUsd` from provider metadata and records spend via `trackUsageCreditSpend` with the parameters `creditFeature: "onboarding"`, `costUsd`, and `customerId`. If the `checkUsageCreditsDepleted` function determines the organization has exhausted its balance, the agent returns a static "Subscribe to continue" message rather than processing further tool calls.

This architecture ensures that **both LLM-token spend and SEO data API costs** are deducted from the same "onboarding-plan" credit balance, simplifying accounting while allowing precise free-tier enforcement.

## Client-Side Integration Example

Frontend applications interact with the onboarding agent through Cloudflare's AI-Chat SDK. The `useAgent` hook connects to the Durable Object endpoint and manages message streaming.

```tsx
import { useAgent } from "@cloudflare/ai-chat";

function OnboardingChat({ projectId }: { projectId: string }) {
  const { sendMessage, messages } = useAgent({
    name: projectId,               // Durable Object name = projectId
    endpoint: "/agents/onboarding", // Route defined in routeTree
  });

  // UI rendering omitted – just call sendMessage and display messages
}

```

The `name` parameter maps directly to the Durable Object identifier, while the endpoint routes to the `OnboardingChatAgent` instance.

## Tool Definition and Metering Example

Tools are defined as structured objects with Zod-like parameter schemas and async handlers. The `research_keywords` tool demonstrates how DataForSEO calls are wrapped with credit attribution:

```ts
export function buildOnboardingTools({ project, billingCustomer }) {
  return {
    read_website: {
      description: "Read pages as plain text",
      parameters: { urls: { type: "array", items: { type: "string" } } },
      handler: async ({ urls }) => {
        // internal scraper, no credits used
        return await readWebsite(urls);
      },
    },
    research_keywords: {
      description:
        "Return related keywords with real search volume and difficulty",
      parameters: {
        seed_topic: { type: "string", description: "Primary topic" },
      },
      handler: async ({ seed_topic }) => {
        // DataForSEO client – credit‑metered
        return await dataforseo.keywords.suggestions({
          keyword: seed_topic,
          country: project.country,
        });
      },
    },
    // …other market tools omitted for brevity
  };
}

```

## Billing Hook Implementation

Credit consumption is recorded in the `onFinish` event handler, which aggregates per-step costs from the LLM provider:

```ts
async function trackSpend(event: FinishEvent) {
  const costUsd = event.steps.reduce(
    (sum, step) => sum + openRouterCostUsd(step.providerMetadata),
    0
  );
  await trackUsageCreditSpend({
    customer: billingCustomer,
    customerId: organizationId,
    creditFeature: "onboarding",
    costUsd,
    monthlyRemaining,
    properties: { provider: "openrouter" },
  });
}

```

This function sums token costs across all reasoning steps and persists the transaction to the organization's credit ledger.

## Summary

- **OpenSEO** implements AI features through Cloudflare Durable Objects that extend `AIChatAgent`, ensuring persistent, stateful conversations at the edge.
- The **onboarding agent** uses a system prompt-driven architecture to enforce UX constraints and tone while limiting responses to credit-aware messaging.
- **DataForSEO integration** provides real search data through function-calling tools, with `research_keywords` powering the content suggestions feature via actual volume and difficulty metrics.
- A **unified billing system** meters both LLM tokens and SEO API calls against a single credit balance, using `trackUsageCreditSpend` with `creditFeature: "onboarding"` to enforce free-tier limits.
- **Tool gating mechanisms** distinguish between free low-cost tools (`read_website`) and credit-consuming market tools (`get_serp_results`), preventing accidental overspending.

## Frequently Asked Questions

### What infrastructure powers OpenSEO's AI onboarding agent?

OpenSEO's onboarding agent runs on **Cloudflare Durable Objects** with SQLite persistence, utilizing the **AI-Chat SDK** for real-time messaging. The agent extends the `AIChatAgent` class defined in [`/src/server/features/onboarding/OnboardingChatAgent.ts`](https://github.com/every-app/open-seo/blob/main//src/server/features/onboarding/OnboardingChatAgent.ts) and obtains its LLM through `getChatAgentModel` from [`/src/server/lib/openrouter.ts`](https://github.com/every-app/open-seo/blob/main//src/server/lib/openrouter.ts), routing inference through OpenRouter while maintaining conversation state across worker restarts.

### How does OpenSEO prevent users from exceeding free AI usage limits?

The platform enforces limits through `trackUsageCreditSpend` in [`/src/server/billing/subscription.ts`](https://github.com/every-app/open-seo/blob/main//src/server/billing/subscription.ts), which checks `customerHasManagedAccess` and `checkUsageCreditsDepleted` before processing requests. Each LLM call and DataForSEO API request is metered against an "onboarding" credit bucket; when exhausted, the `OnboardingChatAgent` returns a static subscription message rather than processing additional tool calls.

### What is the difference between low-cost and market-level tools in OpenSEO?

**Low-cost tools** like `read_website` and `research_keywords` are available during onboarding without consuming credits, while **market-level tools** including `get_domain_overview`, `get_serp_results`, and `get_backlinks_overview` deduct from the organization's credit balance. This distinction is implemented in `buildOnboardingTools` at [`/src/server/features/onboarding/onboardingChatTools.ts`](https://github.com/every-app/open-seo/blob/main//src/server/features/onboarding/onboardingChatTools.ts), where the `meter` helper tags expensive calls for billing attribution.

### How are content suggestions generated in OpenSEO?

Content suggestions are produced by the `research_keywords` tool, which calls `dataforseo.keywords.suggestions` through the client at [`/src/server/lib/dataforseo/client.ts`](https://github.com/every-app/open-seo/blob/main//src/server/lib/dataforseo/client.ts). Given a seed topic, the tool returns real-world search volume and competition difficulty data, which the LLM formats into a Markdown table of target keywords. The onboarding version limits these calls to single seeds and caps credit usage to stay within free-tier boundaries.