This document inventories the APIs currently called by the web UI. It is derived from the client calls in web/app/dashboard/page.tsx, the same-origin proxy in web/app/api/platform/[...path]/route.ts, and the Hono route groups in platform/src/routes.
The browser does not call the platform Worker directly:
Browser
→ /api/platform/* on the Next.js app
→ Cloudflare PLATFORM service binding in production
or http://localhost:8787 in local development
→ versioned /v1/* route on the platform Worker
- Browser base path:
/api/platform - Direct local platform base URL:
http://localhost:8787 - API format: JSON unless noted otherwise
- Private routes: Better Auth session cookie required
- Local development:
x-demo-usercreates a stable demo user whenENVIRONMENTis notproduction - Public protection: selected discovery routes use a Cloudflare rate limiter;
/v1/resolvealso requires Turnstile in production - Landing proof:
POST /v1/demo/youtube/inspectis anonymous and allows five distinct videos per visitor in a rolling 24-hour window - Errors:
{ "error": { "code": string, "message": string, "details"?: unknown, "requestId"?: string } } - Traceability: every platform response receives an
X-Request-Idheader
The UI loads these requests concurrently:
GET /v1/projectsGET /v1/monitorsGET /v1/providers/youtube/browseGET /v1/providers/youtube/trends?q=AI%20agents&limit=20from the default Trend Lab view
- The search box sends its value to
POST /v1/resolve. - A recognized YouTube URL or video ID opens the matching entity endpoint directly.
- Provider discovery uses
GET /v1/providers/youtube/search; private evidence usesGET /v1/search; cited questions usePOST /v1/answers. - Opening a video loads its entity record, transcript, and comments. Transcript or comment failure does not prevent the main video record from opening.
- The UI uses the most recent project or creates a “Research inbox” with
POST /v1/projects. - It saves the source with
POST /v1/projects/:id/items. - It starts durable ingestion with
POST /v1/imports. Import failure is currently non-blocking for the initial save.
GET /v1/providers/youtube/trendscalculates topic signals from public YouTube data, stores metric snapshots, and adds evidence-grounded GLM insights by default.- The user can explicitly call
POST /v1/trends/planto turn those signals into a Kimi-generated plan. A normal topic scan does not consume user AI credits.
| Method | Platform route | UI purpose | Access |
|---|---|---|---|
GET/POST |
/api/auth/* |
Email and Google sign-in | Public |
GET |
/v1/projects |
Load project sidebar and project view | Private |
POST |
/v1/projects |
Create a project | Private |
POST |
/v1/projects/:id/items |
Save a source or transcript into a project | Private |
GET |
/v1/monitors |
Load monitor view and counts | Private |
POST |
/v1/monitors |
Monitor the inspected channel or topic | Private |
GET |
/v1/providers |
List supported providers and capabilities | Authenticated |
GET |
/v1/providers/youtube/browse |
Seed the source inbox | Authenticated, rate-limited |
POST |
/v1/resolve |
Internal universal-input routing helper | First-party UI, protected |
POST |
/v1/demo/youtube/inspect |
Bounded video, transcript, and comments preview | Public landing page, IP quota |
GET |
/v1/providers/youtube/search |
Search YouTube | Authenticated, rate-limited |
GET |
/v1/search |
Search private indexed evidence | Authenticated |
GET |
/v1/providers/youtube/videos/:id |
Inspect a video | Authenticated |
GET |
/v1/providers/youtube/channels/:id |
Inspect a channel | Authenticated |
GET |
/v1/providers/youtube/channels/:id/videos |
Load a channel's videos | Authenticated |
GET |
/v1/providers/youtube/channels/:id/playlists |
Load a channel's playlists | Authenticated |
GET |
/v1/providers/youtube/playlists/:id |
Inspect a playlist | Authenticated |
GET |
/v1/providers/youtube/videos/:id/transcript |
Load timed transcript evidence | Authenticated |
GET |
/v1/providers/youtube/videos/:id/comments |
Load audience comments | Authenticated |
POST |
/v1/imports |
Start durable ingestion and indexing | Private |
POST |
/v1/answers |
Generate a cited answer for an inspected source | Private, metered |
GET |
/v1/providers/youtube/trends |
Calculate topic momentum and patterns | Authenticated, rate-limited |
POST |
/v1/trends/plan |
Generate an evidence-grounded video plan | Private, metered |
Sends the email sign-in link used by the sign-in dialog.
{
"email": "creator@example.com",
"callbackURL": "/"
}How it works:
- Better Auth creates a hashed, single-use token with a 15-minute expiry.
- The platform queues the email through
EMAIL_TASKS; provider work does not block the request. - Following the link establishes the session cookie used by private
/v1routes.
Starts the Google sign-in flow.
{
"provider": "google",
"callbackURL": "/"
}The response includes a redirect url. Better Auth handles the OAuth callback and session creation.
These routes support first-party interface behavior and are not primary public consumer APIs.
The public landing page sends { "url": "https://www.youtube.com/watch?v=..." } directly to the platform so Cloudflare can identify the visitor IP. The response includes normalized video metadata, up to 16 timestamped transcript segments, up to four comments, partial-data status, and the visitor's current quota.
The quota is five distinct YouTube video IDs per HMAC-hashed IP in the trailing 24 hours. Repeating a video in that window does not consume another slot. Upstash Redis evaluates the cleanup, duplicate check, count, and insert atomically. Production requests fail closed if Redis or the hashing salt is unavailable.
Classifies the universal search-box input before the UI decides what to open.
{ "input": "https://www.youtube.com/watch?v=VIDEO_ID" }Possible responses:
{ "kind": "video", "id": "VIDEO_ID" }{ "kind": "channel", "id": "@handle" }{ "kind": "playlist", "id": "PLAYLIST_ID" }{ "kind": "search", "query": "plain text query" }How it works:
- Uses deterministic parsing rather than AI.
- Recognizes 11-character video IDs,
youtu.be, watch, Shorts, live, playlist, channel, and handle URLs. - Rejects non-YouTube URLs and malformed identifiers.
Seeds the source inbox with a normalized public YouTube discovery feed.
Query parameters:
category: required;music,news,sports, orliveregion:USorIN; defaults toUSlanguage:enorhi; defaults toencontinuation: opaque pagination token
How it works:
- Calls the platform's normalized YouTube browse adapter; it does not use the official YouTube Data API.
- Uses current public YouTube destination IDs rather than the retired anonymous Trending feed.
- Normalizes videos, channels, and playlists into application entities.
- Returns both a mixed
resultslist and explicitvideos,channels, andplaylistsarrays. - Caches each option set in Workers KV for five minutes.
- Returns a stale cached snapshot if the upstream call fails and a previous snapshot exists.
Searches one external video provider. The current supported value for provider is youtube.
Parameters:
q: required query
Additional filters:
type:all,video,channel, orplaylistchannel: channel IDlanguage: language codeduration:short,medium, orlongsort:relevance,date,views, orratingcaptions=true: videos with captions onlylive:live,upcoming, orcompletedcontinuation: opaque token returned by the previous YouTube search page
How it works:
- Calls the platform's YouTube search adapter and returns one mixed
resultsarray of videos, channels, and playlists. Each item has atypediscriminator. - Preserves YouTube's interleaved result order and returns a continuation token when another page is available.
- Caches the query/filter combination in Workers KV for five minutes with stale fallback.
- The UI currently exposes type, duration, and captions filters.
Searches transcript and research content previously saved by the user.
Parameters:
q: required queryprojectId: optional private-project restriction
How it works:
- Requires a session.
- Queries the user’s isolated Cloudflare AI Search instance.
- Uses hybrid keyword/vector retrieval, reciprocal-rank fusion, and BGE reranking.
- Can filter to one project and returns up to 12 evidence chunks with scores, source IDs, and timestamps.
Answers the query using the user’s indexed evidence.
How it works:
- Runs the same private retrieval used by
insidemode. - Sends the retrieved excerpts to Workers AI using
@cf/meta/llama-3.3-70b-instruct-fp8-fast. - Requires bracketed evidence citations and rejects an answer with no valid citations.
- Reserves AI credits before inference and settles or releases them afterward.
Returns core normalized video metadata: title, channel, description, thumbnails, duration, views, keywords, availability, and URL.
How it works:
- Calls YouTube player data through fallback client profiles when necessary.
- Caches the normalized record in Workers KV for 30 minutes.
- Track metadata and endscreen elements are available from their dedicated video subresources.
- Does not fetch the desktop caption catalog or expose media-format data, raw renderer data, tracking data, signed URLs, or ads.
Returns channel identity plus an about object aligned to YouTube's About UI:
description: the complete public channel descriptionlinks: every public link with its title, display URL, and direct destination URLmoreInfo: canonical channel URL, joined date, subscriber/video/view totals, their display text, and whether YouTube offers its protected business-email action
How it works:
- A channel ID loads directly through the platform's YouTube browse adapter.
- An
@handleis first resolved through channel search and then loaded by channel ID. - YouTube redirect links are unwrapped; temporary redirect tokens are never returned.
- The protected business email is not accessed. Public email addresses written into the description remain part of the description.
- Results are cached in Workers KV for one hour.
Returns one page of normalized video summaries from the channel's Videos tab.
- Accepts
sort=latest|popular|oldest, matching the three controls in YouTube's UI. The default islatest. - Accepts the optional
continuationtoken returned by the previous response. - Returns
channelId, the effectivesort,videos,continuation, andmeta. - Each video carries the UI card data: title, thumbnail, duration, views, published age, caption state, and canonical watch URL.
- Pages are cached in Workers KV for 15 minutes.
Returns one page of normalized playlist summaries from the channel's Playlists tab.
- Accepts
sort=newest|last-video-added, matching YouTube's Sort by menu. The default isnewest. - Accepts the optional
continuationtoken returned by the previous response. - Returns
channelId, the effectivesort,playlists,continuation, andmeta. - Each card includes its title, thumbnail, displayed video/episode count, optional
updatedTimeText,isPodcast, canonical playlist URL, and the optionalplayUrlused by the card itself. - Pages are cached in Workers KV for 15 minutes.
Returns playlist metadata, videos, and a continuation when more items are available.
How it works:
- Uses the platform's normalized YouTube playlist adapter.
- Normalizes the catalog and caches it in Workers KV for one hour.
Returns the video's actual source caption tracks and available auto-translation targets.
How it works:
- Returns source-track metadata as both
tracksand the clearersourceTracksalias. - Merges the desktop player catalog used by Chrome so
translationLanguagesandautoTranslationTargetscontain the complete auto-translation target list exposed for the video. - Does not expose signed caption URLs or caption text.
Returns the synchronized transcript displayed beside the video.
Optional query parameter:
lang: desired output language from the tracks API's auto-translation targets
How it works:
- The backend selects YouTube's default source caption track automatically.
- If
langdiffers from that source, the platform requests YouTube's translated caption data and normalizes the result. - Without
lang, it returns the original default-track transcript. - Normalizes every segment to
text,startMs,durationMs, andendMs. - Returns the source
trackplustranslatedTowhen auto-translation was requested. - Caches transcripts in Workers KV for seven days.
Returns comments for the audience-evidence panel.
The response includes totalCount when YouTube reports it in the initial comments payload. This is the
video's displayed total; comments.length, topLevelCount, and replyCount describe the comments actually
returned or crawled by this request.
Parameters:
all=true: crawl all available top-level comment and reply continuations up to the 100-page safety limitcontinuation: fetch one additional page whenallis not enabled
How it works:
- Uses YouTube continuation tokens and normalizes comment/thread data.
- The UI currently requests
all=truebut only displays the first three comments. - Full comment collections are cached for 15 minutes.
- Internal reply/newest continuation bookkeeping is removed from the public response.
Returns the signed-in user’s projects and each project’s saved-item count, newest first.
How it works:
- Reads D1 and joins
projectswithproject_items. - User ownership is enforced in the query.
- The UI uses the result in the sidebar, Projects view, and save flow.
Creates a private research project.
{
"name": "AI agent research",
"description": "Optional description",
"tags": ["agents", "video ideas"]
}How it works:
- Requires a non-empty name and enforces the user’s plan limit.
- Stores the project in D1 and returns
201with its ID and name.
Saves a video, channel, playlist, exact moment, note, or transcript content into a project.
Representative request from the UI:
{
"provider": "youtube",
"entityType": "video",
"entityId": "VIDEO_ID",
"title": "Video title",
"content": "[0] Transcript text..."
}How it works:
- Verifies project ownership and writes item metadata to D1.
- If
contentis present, queues anindex-documenttask. - The task stores the private document in R2 and uploads it to the user’s isolated AI Search instance.
- Duplicate project/entity records are ignored by the database constraint.
Deletes a project after removing its private objects from R2 and its indexed items from the user’s AI Search instance. D1 foreign keys then cascade the project’s document metadata and saved items.
Starts durable ingestion after the user saves a source.
{
"provider": "youtube",
"kind": "video",
"entityId": "VIDEO_ID",
"projectId": "PROJECT_ID"
}Supported kind values are video, channel, playlist, comments, and deep-comments.
How it works:
- Enforces daily import and plan limits.
- Uses the
Idempotency-Keyheader or a deterministic fallback to prevent duplicate jobs. - Creates a D1 job and starts a Cloudflare Workflow, returning
202immediately. - Video imports fetch and store the transcript in R2, index a public copy, and optionally index a private project copy.
- Channel and playlist imports fan out up to ten eager child video imports.
- The current UI starts the job but does not yet poll its status.
Generates the cited brief and source answers shown by the UI.
{
"question": "What are the main claims?",
"entityId": "VIDEO_ID"
}How it works for the current UI:
- Fetches the video transcript.
- Selects transcript segments that match important question terms, with an early-segment fallback.
- Sends only those segments to the Workers AI answer model.
- Treats transcript text as untrusted evidence and requires citations such as
[1]on substantive claims. - Returns the answer plus only the evidence records actually cited.
- Uses AI Gateway retries/caching when configured and a direct Workers AI fallback when the gateway is unavailable.
The endpoint can also search private project evidence when entityId is omitted, or the public corpus when scope is public.
Builds the Trend Lab dashboard for a topic.
Parameters:
q: required topiclimit: requested enriched sample size, clamped to 8–30; defaults to 20insights:ai(default) ordeterministic
How it works:
- Searches up to three YouTube result pages and limits over-representation by any one channel.
- Enriches the sample in bounded batches with video, engagement, and publication signals.
- Persists views, likes, and comments in
analytics_snapshots; later scans calculate observed velocity and acceleration. - Scores freshness, engagement, topic-relative velocity, acceleration, and channel-relative performance, with per-video and report confidence.
- Aggregates visible hashtags, repeated title terms, and duration buckets.
- Uses
@cf/zai-org/glm-4.7-flashto extract evidence-linked themes, audience intent, saturation, and content gaps; model failure degrades to the deterministic report. - Returns a deterministic starter plan and transparent methodology alongside the chart data.
- It does not claim access to CTR, retention, recommendation traffic, or proof of market demand. First scans explicitly report low confidence until snapshot history exists.
Turns an existing Trend Lab report into a richer video strategy.
{
"report": {
"provider": "youtube",
"query": "AI agents",
"sampleSize": 20,
"summary": {},
"videos": [],
"hashtags": [],
"titlePatterns": [],
"durationMix": []
}
}How it works:
- Requires a session and AI credits.
- Validates and bounds every client-supplied signal before prompt construction.
- Tries
@cf/moonshotai/kimi-k2.6first with bounded reasoning and a strict JSON schema, then falls back to@cf/openai/gpt-oss-120bif Kimi inference is unavailable. - Produces an angle, audience, hook, duration, story arc, titles, hashtags, differentiation, evidence references, and caveats.
- Treats all titles and signal strings as untrusted data and accepts only evidence IDs present in the submitted sample.
- Supports both Workers AI Chat Completions and Responses API envelopes.
- Uses AI Gateway retries/caching when configured and releases reserved credits on failure.
Returns the signed-in user’s monitors for the monitor view and workspace counts.
How it works:
- Reads the user-owned monitor rows from D1, newest first.
- Includes enabled state, cadence, last cursor, and last checked time.
Creates a channel, topic, or search monitor.
{
"provider": "youtube",
"kind": "channel",
"target": "CHANNEL_ID",
"cadence": "hourly"
}How it works:
- Enforces the user’s plan limit and stores the monitor in D1.
- The hourly scheduled Monitor Workflow searches YouTube sorted by date.
- A newly observed leading video creates a notification and advances the monitor cursor.
- The UI currently creates and lists monitors; notification display is not wired yet.
These platform contracts exist, but no current UI action calls them:
GET /healthGET /v1/providers/:provider/videos/:id/tracksGET /v1/providers/:provider/videos/:id/endscreenGET /v1/providers/:provider/channels/:id/videosGET /v1/providers/:provider/channels/:id/playlistsGET /v1/projects/:idDELETE /v1/projects/:idGET /v1/jobs/:idPOST /v1/comparisonsPOST /v1/reportsPOST /v1/projects/:id/exportsGET /v1/exports/:id/downloadDELETE /v1/monitors/:id- Notification and notification-preference routes
- YouTube OAuth connection routes
- Billing, usage, admin, and account-deletion routes
They should remain outside the UI API contract until a visible user flow depends on them.
The APIs are versioned, typed internally, and published as OpenAPI 3.1 at /openapi.json. Scalar serves the interactive contract at /docs. The remaining contract gaps are:
- Request and response types are duplicated between
web/app/page.tsxand the platform implementation. - The OpenAPI document is maintained alongside the route code, but it does not yet generate the web client or enforce runtime schema validation.
- The UI starts import jobs but does not use
GET /v1/jobs/:idto report durable progress or failure. - Pagination tokens exist for search, browse, channel, playlist, and comments, but the UI does not expose “load more” flows.
- The UI hard-codes demo headers and credit copy instead of loading session/usage state through a formal client.
A strong next step is to generate the web client and shared types from the OpenAPI contract, add runtime schema validation, and add contract tests at the Next.js proxy boundary.