# Core Entry Points for OpenSEO Cloudflare Workers: A Complete Technical Guide

> Explore the core entry points for OpenSEO Cloudflare Workers: fetch handler, scheduled handler, Durable Objects, and Workflow classes. Unlock powerful serverless applications.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: how-to-guide
- Published: 2026-08-20

---

**OpenSEO Cloudflare Workers expose three primary entry points—the `fetch` handler, `scheduled` handler, and Workflow classes—plus three Durable Object bindings that enable stateful chat agents and audit scratchpads.**

The OpenSEO codebase (`every-app/open-seo`) is architected as a modern Cloudflare Workers application that handles real-time SEO audits, rank tracking, and AI-powered chat agents. Understanding its entry points is essential for contributors, operators, and anyone extending the platform. This guide maps every invocation path with precise file references from the source code.

## The Three Primary Entry Points

Cloudflare Workers runtime invokes OpenSEO through handlers exported from [`src/server.ts`](https://github.com/every-app/open-seo/blob/main/src/server.ts). These form the backbone of the application's execution model.

### fetch Handler: HTTP Request Routing

The **`fetch` handler** ([src/server.ts#L29-L78](https://github.com/every-app/open-seo/blob/main/src/server.ts#L29-L78)) processes every HTTP request hitting the worker. It multiplexes traffic across:

- **API calls** – REST endpoints for project management
- **Web UI requests** – Dashboard and application frontend
- **`/agents/*` endpoints** – Durable Object chat routing (WebSocket upgrade)
- **OAuth flow** – Authentication callbacks
- **Specialized handlers** – GDPR erasure, Autumn webhook, and MCP route forwarding

```typescript
// src/server.ts — simplified fetch handler structure
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);
    
    // Route to Durable Object chat agents
    if (url.pathname.startsWith('/agents/')) {
      // WebSocket upgrade to OnboardingChatAgent or SamChatAgent
    }
    
    // API and UI routing
    // ...
    
    return new Response('Not Found', { status: 404 });
  }
} satisfies ExportedHandler<Env>;

```

### scheduled Handler: Cron-Triggered Background Jobs

The **`scheduled` handler** ([src/server.ts#L93-L132](https://github.com/every-app/open-seo/blob/main/src/server.ts#L93-L132)) executes on Cloudflare Cron triggers defined in `wrangler.jsonc`. Two schedules drive operations:

| Cron Pattern | Purpose |
|-------------|---------|
| `*/5 * * * *` | Every 5 minutes: stale-audit reconciliation, rank-check scheduling |
| `17 3 * * *` | Daily at 03:17 UTC: OAuth KV garbage collection |

```typescript
// Simulated local invocation of the scheduled handler
import server from './src/server';

await server.scheduled(
  { cron: "*/5 * * * *", scheduledTime: Date.now() } as ScheduledController,
  env,
  {} as ExecutionContext
);

```

### Workflow Classes: Long-Running Multi-Step Operations

OpenSEO exports **two Workflow classes** from [`src/server.ts`](https://github.com/every-app/open-seo/blob/main/src/server.ts) ([src/server.ts#L80-L88](https://github.com/every-app/open-seo/blob/main/src/server.ts#L80-L88)) that Cloudflare Workflows orchestrates:

- `SiteAuditWorkflow` – Encapsulates multi-stage site crawling and analysis
- `RankCheckWorkflow` – Manages distributed rank checking across search engines

Workflows run in isolated execution contexts, surviving individual worker invocations to handle operations that exceed request timeouts.

```typescript
// Internal invocation from the RankCheckWorkflow scheduler
import { RankCheckWorkflow } from './server/workflows/RankCheckWorkflow';

const workflow = new RankCheckWorkflow();
await workflow.run({
  projectId: 'proj_123',
  keywords: ['seo tools', 'rank tracker'],
  engines: ['google', 'bing']
});

```

## Durable Object Entry Points

Beyond handlers and workflows, OpenSEO registers **three Durable Object classes** that act as entry points for stateful, long-lived computing.

### ONBOARDING_CHAT → OnboardingChatAgent

`OnboardingChatAgent` ([src/server/features/onboarding/OnboardingChatAgent.ts](https://github.com/every-app/open-seo/blob/main/src/server/features/onboarding/OnboardingChatAgent.ts)) provides per-project AI strategy chat using the Agents SDK. Each `projectId` maps to a unique Durable Object instance.

```typescript
// Client-side WebSocket connection
const ws = new WebSocket(
  'wss://open-seo.example.com/agents/onboarding/proj_123'
);

```

### SAM_CHAT → SamChatAgent

`SamChatAgent` ([src/server/features/sam/SamChatAgent.ts](https://github.com/every-app/open-seo/blob/main/src/server/features/sam/SamChatAgent.ts)) handles in-app SAM (Search Action Model) chat sessions. Unlike onboarding chat, this creates one instance per chat session rather than per project.

### AUDIT_SCRATCHPAD → AuditScratchpad

`AuditScratchpad` ([src/server/features/audit/AuditScratchpad.ts](https://github.com/every-app/open-seo/blob/main/src/server/features/audit/AuditScratchpad.ts)) maintains crawl state for active audits:

- **Frontier** – URLs queued for crawling
- **Link edges** – Discovered internal/external links
- **Page mirror** – Cached page content for analysis

## Configuration: How Entry Points Are Wired Together

The **`wrangler.jsonc`** configuration binds all entry points declaratively:

| Section | Purpose | Lines |
|--------|---------|-------|
| `main` | Points to [`src/server.ts`](https://github.com/every-app/open-seo/blob/main/src/server.ts) as the entry module | Line 22 |
| `workflows` | Declares `SiteAuditWorkflow` and `RankCheckWorkflow` bindings | ~22-40 |
| `durable_objects` | Registers the three DO classes with their binding names | ~40-58 |

```jsonc
// wrangler.jsonc — entry point configuration excerpt
{
  "main": "src/server.ts",
  "workflows": [
    {
      "name": "site-audit-workflow",
      "class_name": "SiteAuditWorkflow"
    },
    {
      "name": "rank-check-workflow", 
      "class_name": "RankCheckWorkflow"
    }
  ],
  "durable_objects": {
    "bindings": [
      { "name": "ONBOARDING_CHAT", "class_name": "OnboardingChatAgent" },
      { "name": "SAM_CHAT", "class_name": "SamChatAgent" },
      { "name": "AUDIT_SCRATCHPAD", "class_name": "AuditScratchpad" }
    ]
  }
}

```

See the complete configuration at [wrangler.jsonc](https://github.com/every-app/open-seo/blob/main/wrangler.jsonc).

## Entry Point Execution Flow

```

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  HTTP Request   │────→│  fetch handler  │────→│  API / UI / DO  │
│  (any client)   │     │  src/server.ts  │     │  agents routing │
└─────────────────┘     └─────────────────┘     └─────────────────┘

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Cron Trigger   │────→│scheduled handler│────→│ Background jobs │
│  (Cloudflare)   │     │  src/server.ts  │     │  audit/rank/KV  │
└─────────────────┘     └─────────────────┘     └─────────────────┘

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Internal call  │────→│   Workflow.init │────→│ Multi-step exec │
│  (worker code)  │     │ Site/Rank Check │     │  (survives DO)  │
└─────────────────┘     └─────────────────┘     └─────────────────┘

```

## Summary

- **`fetch` handler** in [`src/server.ts`](https://github.com/every-app/open-seo/blob/main/src/server.ts) routes all HTTP traffic including WebSocket upgrades to Durable Objects
- **`scheduled` handler** runs cron jobs for reconciliation, scheduling, and cleanup every 5 minutes and daily
- **Workflow classes** (`SiteAuditWorkflow`, `RankCheckWorkflow`) enable fault-tolerant, long-running operations
- **Durable Objects** provide stateful entry points for chat agents (`OnboardingChatAgent`, `SamChatAgent`) and audit state (`AuditScratchpad`)
- **`wrangler.jsonc`** declaratively binds all entry points for Cloudflare deployment

## Frequently Asked Questions

### What triggers the OpenSEO scheduled handler to run?

The `scheduled` handler triggers automatically based on cron patterns in `wrangler.jsonc`. OpenSEO configures two schedules: every 5 minutes for operational tasks and daily at 03:17 UTC for garbage collection. Cloudflare invokes the handler—no external HTTP request required.

### How do OpenSEO Durable Objects maintain state across requests?

Each Durable Object class (`OnboardingChatAgent`, `SamChatAgent`, `AuditScratchpad`) runs in its own isolated V8 isolate with persistent in-memory state. Cloudflare routes requests to the same physical instance based on the object ID (derived from `projectId` or session ID), enabling WebSocket chat histories and crawl scratchpads to persist beyond single requests.

### Can I invoke OpenSEO Workflows from outside the Worker?

No. According to the OpenSEO source code, Workflows are **internal entry points** invoked only from server-side Worker code. They are not exposed as HTTP endpoints. To trigger a workflow, make an API call that the `fetch` handler routes to internal workflow-initialization logic.

### What's the difference between the two chat Durable Objects?

**`OnboardingChatAgent`** creates one instance per `projectId` for strategy discussions during project setup. **`SamChatAgent`** creates one instance per chat session for ongoing in-app assistance. The distinction affects routing logic in the `fetch` handler and how clients construct WebSocket URLs.