How it works

The architecture under both products.

Onto has two delivery paths but one cleaning engine. The Read API serves AI developers fetching arbitrary URLs. The Serve SDK serves site owners who want their own pages to be agent-readable. Both produce the same kind of clean Markdown from the same underlying extractor.

Read API path

text
Agent / your code
       │  POST /v1/read { url }

api.buildonto.dev (Vercel)
       │  1. Authenticate Bearer token
       │  2. Check rate-limit + concurrency slot
       │  3. Hit cache (1h TTL)
       │     ┌─ HIT  → return cached payload
       │     └─ MISS → continue
       │  4. Honor target's robots.txt (short-circuit if disallowed)
       │  5. Fetch target URL (10s timeout)
       │  6. Run @ontosdk/core extractor → clean Markdown
       │  7. Score (if /v1/score or /v1/read-and-score)
       │  8. Cache + log usage + return

JSON or text/markdown response

Serve SDK path

text
Build time (your CI):
  next build → onto-next
    ↳ for each route, extract HTML → Markdown
    ↳ write public/.onto/{route}.md
    ↳ POST manifest to /api/files (so dashboard sees them)

Request time (your edge):
  Bot request → @ontosdk/next middleware
    ↳ detect bot (UA or Accept)
    ↳ fetch /.onto/{route}.md (internal subrequest)
    ↳ POST /api/track (telemetry, fire-and-forget)
    ↳ GET /api/injections?route={path} (Pro+, append if found)
    ↳ return clean Markdown with X-Onto-* headers

  Human request → middleware passes through, full HTML served

Shared engine

Both paths run the same @ontosdk/core package (currently 0.1.0 on npm). The Read API runs it at request time on the URL the customer sends; the Serve SDK runs it at build time on the customer's own HTML output. Same cleaning rules, same heading hierarchy normalization, same scoring penalties.

Where data lives

DataStorePurpose
API keys (hashed)Supabase api_keysBearer auth for Read API
Per-site keysSupabase sitesSDK auth for Serve
Per-route .md filesSupabase onto_filesManifest the dashboard reads
Agent visit telemetrySupabase agent_eventsDrives /serve/analytics
Read API usage eventsSupabase usage_eventsDrives /read/usage + billing reconciliation
Monthly counter, concurrencyVercel KV (Redis)Hot-path rate limiting + cache
Cleaned Markdown cacheVercel KV (1h TTL)Avoid re-extraction on the Read API
Subscriptions + creditsSupabase subscriptions + profiles.credit_balancePolar webhook syncs these
Two products, two billing lines, one auth. A single signed-in user can hold one Read subscription AND one Serve subscription via the same Supabase auth — the subscriptions table is keyed on (user_id, kind) where kind is read or serve.