# ViewTube API — LLM Reference > Base URL: (same origin as the app) > Auth: session cookie `viewtube-session` (iron-session). Call POST /api/auth/login first. > All requests/responses are JSON unless noted (uploads use multipart/form-data). > Machine-readable OpenAPI 3.0 spec: GET /api/openapi > Interactive Swagger UI: GET /api/openapi-ui --- ## Authentication ### POST /api/auth/register Create a new user account. Auto-creates channel+wallet for creators, wallet for advertisers. Body (JSON): username* (string, 3–30 chars), email* (string), password* (string, min 6), role (user|creator|advertiser, default: user), displayName (string) Response 200: {id, username, email, role, createdAt} Response 400: {message} — username/email taken or validation error ### POST /api/auth/login Authenticate and receive a session cookie. Body (JSON): username* (string), password* (string) Response 200: {id, username, email, role} Response 401: {message: "Invalid credentials"} ### GET /api/auth/me [AUTH REQUIRED] Get the currently logged-in user. Response 200: {id, username, email, role, createdAt} Response 401: {message: "Not authenticated"} ### POST /api/auth/logout [AUTH REQUIRED] Clear session cookie. Response 200: {message: "Logged out"} --- ## Videos (Public) ### GET /api/videos List all public, published videos. Query params: limit (int, default 20), offset (int, default 0), category (string), trending (bool — sort by views) Response 200: Array of Video objects ### POST /api/videos [AUTH REQUIRED] Create a raw video record (prefer POST /api/upload/publish for file upload). Body (JSON): title*, slug*, videoUrl, thumbnailUrl, description, category, tags (string[]), visibility (public|private|unlisted), monetizationEnabled (bool), duration (int), publishedAt (ISO date) Response 200: Video object ### GET /api/videos/:slug Get a single video by URL slug (NOT uuid). Increments view count. Response 200: Video object Response 404: {message: "Video not found"} ### PATCH /api/videos/:id [AUTH REQUIRED — creator only] Partial update of a video by UUID. Only the owning creator can update. Body (JSON, all optional): title, description, visibility, category, tags, thumbnailUrl, status (ready|draft), publishedAt (ISO date) Response 200: Updated Video object Response 403: {message: "Forbidden"} ### DELETE /api/videos/:id [AUTH REQUIRED — creator only] Delete a video by UUID. Response 200: {message: "Video deleted"} Response 403: {message: "Forbidden"} --- ## Video Interactions ### GET /api/videos/:id/comments Get comments for a video (UUID). Response 200: Array of Comment objects [{id, content, userId, username, videoId, createdAt}] ### POST /api/videos/:id/comments [AUTH REQUIRED] Post a comment. Body (JSON): content* (string, max 500 chars) Response 200: Comment object ### DELETE /api/comments/:id [AUTH REQUIRED — author or video owner] Delete a comment by UUID. Response 200: {message: "Comment deleted"} ### POST /api/videos/:id/like [AUTH REQUIRED] Like a video. Toggles off if already liked. Removes dislike if one exists. Response 200: {likes: int, dislikes: int} ### POST /api/videos/:id/dislike [AUTH REQUIRED] Dislike a video. Toggles off if already disliked. Response 200: {likes: int, dislikes: int} ### GET /api/videos/:id/like-status Get current user's reaction. Returns {type: null} when unauthenticated. Response 200: {type: "like" | "dislike" | null} --- ## Upload ### POST /api/upload/publish [AUTH REQUIRED] **Recommended endpoint** — upload video + thumbnail + all metadata in one request. Format: multipart/form-data Fields: - video (File) — MP4/WebM/MOV/AVI/MKV, max 500 MB [required if no videoUrl] - videoUrl (string) — pre-uploaded video URL (alternative to file) - thumbnail (File) — JPG/PNG/WebP, max 5 MB - thumbnailUrl (string) — pre-uploaded thumbnail URL - title* (string, max 100 chars) - description (string) - category (string, e.g. "Gaming") - tags (string) — comma-separated, e.g. "gaming,fps,review" - visibility (string) — public|private|unlisted, default: public - monetizationEnabled (string) — "true"|"false", default: "true" - saveAsDraft (string) — "true" to save without publishing - scheduledAt (string) — ISO 8601 future date to schedule publish - duration (string) — duration in seconds as string Response 200: Full Video object {id, slug, title, videoUrl, thumbnailUrl, visibility, status ("ready"|"draft"), monetizationEnabled, publishedAt, duration, views, likes, dislikes, createdAt} ### POST /api/upload/video [AUTH REQUIRED] Upload a video file only. Returns a URL to use in other endpoints. Format: multipart/form-data, field: video (File) Response 200: {url: "/api/media/videos/uuid.mp4"} ### POST /api/upload/thumbnail [AUTH REQUIRED] Upload a thumbnail image only. Returns a URL. Format: multipart/form-data, field: thumbnail (File) Response 200: {url: "/api/media/thumbnails/uuid.jpg"} --- ## Channels ### GET /api/channels/:handle Get channel info by handle (e.g. "techreviewer"). Response 200: {id, handle, name, description, avatarUrl, bannerUrl, subscriberCount, userId, createdAt} Response 404: {message: "Channel not found"} ### GET /api/channels/:handle/videos Get public videos for a channel. Query params: limit (int, default 20), offset (int, default 0) Response 200: Array of Video objects ### POST /api/channels/:handle/subscribe [AUTH REQUIRED] Subscribe or unsubscribe (toggle). Cannot subscribe to own channel. Response 200: {message: "Subscribed"} or {message: "Unsubscribed"} ### GET /api/channels/:handle/subscribe-status Check if current user is subscribed. Returns {subscribed: false} when unauthenticated. Response 200: {subscribed: bool} --- ## Search & Discovery ### GET /api/search Search public videos by title and description. Query params: q* (string), limit (int, default 20), offset (int, default 0) Response 200: Array of Video objects ### GET /api/trending Get trending videos sorted by views descending. Query params: limit (int, default 20), offset (int, default 0) Response 200: Array of Video objects ### GET /api/categories Get all video categories. Response 200: [{id: int, name: string, slug: string}] --- ## Creator Studio [AUTH REQUIRED — creator role] ### GET /api/creator/videos Get all videos for the logged-in creator, including drafts and scheduled. Response 200: Array of Video objects (status may be "draft" or "ready") ### GET /api/creator/dashboard Get creator stats and wallet info. Response 200: { channel: Channel, stats: {totalViews: int, totalVideos: int, totalSubscribers: int}, wallet: {id, balance: string}, recentVideos: Video[] } --- ## Wallet [AUTH REQUIRED] ### GET /api/wallet Get wallet balance and transaction history. Response 200: {wallet: {id, balance: string}, transactions: Transaction[]} ### POST /api/wallet/withdraw Withdraw from wallet. Body (JSON): amount* (number) Response 200: Updated Wallet object Response 400: {message: "Insufficient balance"} ### POST /api/wallet/deposit Add funds to wallet. Body (JSON): amount* (number) Response 200: Updated Wallet object ### GET /api/wallet/transactions Get full transaction history. Response 200: [{id, type: "deposit"|"withdrawal"|"ad_revenue", amount, description, createdAt}] --- ## Advertiser [AUTH REQUIRED — advertiser role] ### GET /api/advertiser/campaigns List all ad campaigns for the logged-in advertiser. Response 200: Array of AdCampaign objects Response 403: {message: "Forbidden"} — not an advertiser ### POST /api/advertiser/campaigns Create an ad campaign. Body (JSON): name*, budget* (number), targetCategory, adVideoUrl, adTitle, adDescription, costPerView (number) Response 200: AdCampaign object ### GET /api/advertiser/campaigns/:id/stats Get views and spend for a campaign. Response 200: {views: int, spent: string, campaign: AdCampaign} --- ## Media Proxy ### GET /api/media/:key Stream a video or image file from DigitalOcean Spaces. Supports HTTP Range headers for video seeking (returns 206 Partial Content). The :key is the path after /api/media/, e.g. "videos/uuid.mp4" or "thumbnails/uuid.jpg". Use the videoUrl/thumbnailUrl fields returned by other endpoints directly. Response 200/206: Binary stream with Content-Type, Content-Length, Accept-Ranges headers --- ## Seed Accounts (password: password123) - techreviewer (creator) — Tech Reviewer channel - musicmaster (creator) — Music Master channel - gamingpro (creator) — Gaming Pro channel - adcompany (advertiser) — Ad Company --- ## Data Models ### Video {id: uuid, slug: string, title: string, description: string, videoUrl: string, thumbnailUrl: string, duration: int (seconds), views: int, likes: int, dislikes: int, visibility: "public"|"private"|"unlisted", status: "ready"|"draft", monetizationEnabled: bool, channelId: uuid, category: string, tags: string[], publishedAt: datetime|null, createdAt: datetime} ### Channel {id: uuid, handle: string, name: string, description: string, avatarUrl: string, bannerUrl: string, subscriberCount: int, userId: uuid, createdAt: datetime} ### Comment {id: uuid, content: string, userId: uuid, username: string, videoId: uuid, createdAt: datetime} ### Transaction {id: uuid, walletId: uuid, type: "deposit"|"withdrawal"|"ad_revenue", amount: string (decimal), description: string, createdAt: datetime} ### AdCampaign {id: uuid, name: string, budget: string, spent: string, status: "active"|"paused"|"completed", targetCategory: string, createdAt: datetime}