# OpenSEO Onboarding Flow and AI Agent Skills: A Complete Technical Guide

> Explore the OpenSEO onboarding flow and discover AI agent skills. Learn about the credit-limited chat experience enforced by a Cloudflare Durable Object for core tools and market research.

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

---

**The OpenSEO onboarding flow guides new users through a credit-limited chat experience backed by a Cloudflare Durable Object named "Sam," which enforces a 7-question free limit while exposing core site tools and credit-metered market research capabilities.**

New users entering the every-app/open-seo repository encounter a sophisticated onboarding system designed to demonstrate value while protecting platform resources. This technical guide examines how the platform orchestrates the initial user experience through durable objects, credit enforcement, and a curated set of AI agent skills. Understanding this flow is essential for developers customizing the OpenSEO architecture or integrating similar patterns into their own applications.

## Understanding the OpenSEO Onboarding Flow

The onboarding pipeline orchestrates ten discrete steps across the codebase, from initial authentication to message persistence.

### Step 1: Authentication and Project Creation

When a user first signs in, [`src/server.ts`](https://github.com/every-app/open-seo/blob/main/src/server.ts) authorizes the `/agents/*` route and ensures an Autumn customer exists for billing purposes. The system automatically creates a default project that will host the onboarding conversation and consume credits.

### Step 2: Retrieving Onboarding State

The front-end queries `getOnboardingChatState` in [`src/serverFunctions/onboardingChat.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/onboardingChat.ts) via `GET /onboarding/state` to obtain the project's `id` and current `domain`. This determines whether the user must complete domain configuration before accessing the chat interface.

### Step 3: Domain Configuration

Users submit their site domain and optional location code through `saveOnboardingSite`, which persists the data via `POST /onboarding/site`. The `locationCode` parameter (e.g., **2840** for United States, English) configures regional SEO analysis for subsequent tool calls.

### Step 4: Durable Object Instantiation

The client initializes the chat by calling `useAgent({ name: projectId })`, where the project ID serves as the Durable Object name. The implementation class `OnboardingChatAgent` in [`src/server/features/onboarding/OnboardingChatAgent.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/onboarding/OnboardingChatAgent.ts) handles WebSocket connections and SQLite persistence through its `persistMessages` method with built-in retry logic.

### Step 5: Credit and Limit Enforcement

Before invoking the LLM, `OnboardingChatAgent.onChatMessage` validates two constraints defined in [`src/shared/onboardingChat.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/onboardingChat.ts): the `FREE_ONBOARDING_QUESTION_LIMIT` (**7 questions**) and the organization's onboarding credit balance. Exceeding either threshold immediately triggers a subscription prompt rather than processing the query.

### Step 6: System Prompt and Tool Construction

The agent calls `buildSystemPrompt` to assemble an SEO-focused instruction set including fact-sheet markdown. It then invokes `buildOnboardingTools` from [`src/server/features/onboarding/onboardingChatTools.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/onboarding/onboardingChatTools.ts) to construct a **ToolSet** merging free core utilities with credit-metered market intelligence functions.

### Step 7: LLM Streaming and Tool Execution

Using `streamText` from the `ai` library, the agent streams responses from the OpenRouter model configured in [`src/server/lib/openrouter.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/openrouter.ts). Tool calls route through DataForSEO via the shared `dfsClient`, with spend calculated by utilities in [`src/server/lib/chatAgent.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/chatAgent.ts) and automatically tagged with `creditFeature: "onboarding"`.

### Step 8: Conversation Persistence

Each assistant turn appends to `this.messages` within the Durable Object's SQLite store, ensuring conversation continuity across requests. The generated routing table in [`src/routeTree.gen.ts`](https://github.com/every-app/open-seo/blob/main/src/routeTree.gen.ts) exposes `/onboarding/` and `/onboarding/chat` endpoints to wire these server components to the client interface.

## AI Agent Skills and Tool Architecture

The OpenSEO agent exposes capabilities through a dynamically constructed **ToolSet**, explicitly separated into free core utilities and credit-metered market intelligence functions.

### Core Site Tools (Free Tier)

Three tools remain available without credit consumption during the onboarding phase:

- **read_website**: Fetches plain-text content from user-supplied URLs or the configured domain, enabling Sam to analyze homepage content or competitor pages. Implemented in [`src/server/features/onboarding/onboardingChatTools.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/onboarding/onboardingChatTools.ts) (lines 71-90).

- **get_seo_metrics**: Retrieves organic traffic estimates, keyword counts, and top-ranking keywords for the user's site within Labs-enabled markets. Available in [`onboardingChatTools.ts`](https://github.com/every-app/open-seo/blob/main/onboardingChatTools.ts) (lines 91-110).

- **research_keywords**: Generates keyword opportunities from seed topics, returning search volume, difficulty scores, and intent classification via the `KeywordResearchService.research` method. Defined in [`onboardingChatTools.ts`](https://github.com/every-app/open-seo/blob/main/onboardingChatTools.ts) (lines 111-152).

### Market and Competitor Tools (Credit-Metered)

Advanced capabilities consume onboarding credits when invoked through the [`onboardingMarketTools.ts`](https://github.com/every-app/open-seo/blob/main/onboardingMarketTools.ts) module:

- **get_domain_overview**: Returns high-level organic footprints including traffic volumes and keyword counts for any domain.

- **get_serp_results**: Retrieves live Google SERP data for 1-3 specified keywords, showing top organic results.

- **find_serp_competitors**: Identifies top competing domains for a given set of user-provided keywords.

- **get_competitor_keywords**: Lists keywords a competitor ranks for, including position, volume, and difficulty metrics.

- **get_backlinks_overview**: Summarizes total backlink counts and referring domains (the most expensive operation).

All market tools reside in [`src/server/features/onboarding/onboardingMarketTools.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/onboarding/onboardingMarketTools.ts) (lines 15-240). Each invocation validates inputs with Zod, calls DataForSEO through the shared `dfsClient`, and attaches `creditFeature: "onboarding"` for accurate billing attribution.

## Implementation Examples

### Fetching the Onboarding Project State

Client applications retrieve the initial configuration to determine if domain setup is required before initializing the chat agent:

```typescript
// GET /onboarding/state
const { projectId, domain } = await fetch("/onboarding/state")
  .then(r => r.json());
// projectId serves as the durable object name for subsequent chat calls

```

### Configuring Site Domain and Location

Submit the project configuration before initiating the chat to ensure the agent can analyze the correct property:

```typescript
await fetch("/onboarding/site", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    projectId: "proj_123",
    domain: "example.com",
    locationCode: 2840 // United States, English
  })
});

```

### Initializing the Chat Agent

Connect to the Durable Object using the project ID as the unique identifier:

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

const chat = useAgent({ name: projectId });
const response = await chat.sendMessage("What SEO opportunities do I have?");

```

## Summary

- The **OpenSEO onboarding flow** combines automatic project creation, domain configuration, and credit-enforced chat limits to guide new users through a guided SEO consultation.
- **Seven free questions** (`FREE_ONBOARDING_QUESTION_LIMIT`) are permitted before requiring credits, enforced within `OnboardingChatAgent.onChatMessage`.
- **Core site tools** (`read_website`, `get_seo_metrics`, `research_keywords`) execute without credit consumption via [`src/server/features/onboarding/onboardingChatTools.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/onboarding/onboardingChatTools.ts).
- **Market tools** require onboarding credits and route through DataForSEO with automatic `creditFeature: "onboarding"` tagging in [`src/server/features/onboarding/onboardingMarketTools.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/onboarding/onboardingMarketTools.ts).
- All conversation state persists in **Cloudflare Durable Objects** with SQLite storage, defined in [`src/server/features/onboarding/OnboardingChatAgent.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/onboarding/OnboardingChatAgent.ts).

## Frequently Asked Questions

### How many free questions does OpenSEO allow during onboarding?

OpenSEO enforces a strict limit of **seven free questions** per new user, defined by the constant `FREE_ONBOARDING_QUESTION_LIMIT` in [`src/shared/onboardingChat.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/onboardingChat.ts). After this threshold is exceeded, the `OnboardingChatAgent` checks the organization's onboarding credit balance before processing additional queries.

### What happens when a user exhausts their onboarding credits?

When the free question limit is exceeded or the credit balance reaches zero, the `OnboardingChatAgent` immediately returns a subscription upgrade prompt instead of invoking the LLM or executing tools. This check occurs within the `onChatMessage` method before any streaming begins.

### Which AI agent skills require credits to use?

While **read_website**, **get_seo_metrics**, and **research_keywords** remain free, all market intelligence tools—including **get_domain_overview**, **get_serp_results**, **find_serp_competitors**, **get_competitor_keywords**, and **get_backlinks_overview**—consume credits from the onboarding pool when invoked against DataForSEO.

### Where is the onboarding chat state persisted?

The conversation history and user metadata persist in a **Cloudflare Durable Object** named after the project ID, implemented in [`src/server/features/onboarding/OnboardingChatAgent.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/onboarding/OnboardingChatAgent.ts). The class uses an internal SQLite store (`this.messages`) with retry logic handled by the `persistMessages` method, ensuring durability across edge locations.