# API Reference Complete reference for the RedditBot web dashboard API. --- ## Table of Contents 1. [Overview](#overview) 2. [Authentication](#authentication) 3. [Base URL](#base-url) 4. [Bot Status & Control](#bot-status--control) 5. [Activity & Messages](#activity--messages) 6. [Chat & Conversations](#chat--conversations) 7. [Media](#media) 8. [Analytics](#analytics) 9. [Configuration](#configuration) 10. [Logs & Debugging](#logs--debugging) 11. [Error Responses](#error-responses) 12. [SSE Events](#sse-events) --- ## Overview The RedditBot web dashboard exposes a REST API for monitoring and controlling the bot. All endpoints are served by the Express server. **Content-Type**: `application/json` (except SSE) **Response Format**: JSON **Authentication**: Dashboard session cookie or bearer token --- ## Authentication The dashboard, API, SSE streams, and private media require the token configured through `WEB_DASHBOARD_TOKEN` (preferred) or `web.authToken`. Interactive users exchange the token for an HttpOnly, SameSite=Strict session cookie at `POST /auth/session`. API clients may instead send `Authorization: Bearer `. `GET /auth/login` and `POST /auth/session` are the only unauthenticated dashboard routes. A rejected API request returns HTTP 401 with `{"error":"Dashboard authentication required"}`. Debug endpoints are absent unless `web.debugEnabled` is explicitly enabled. --- ## Base URL ``` http://localhost:3000/api ``` The port is configured via `config.web.port` (default: 3000). --- ## Bot Status & Control ### Get Bot Statistics Retrieve current bot statistics including running state, counts, and response rate. ``` GET /api/stats ``` **Response**: ```json { "running": true, "processed": 1250, "sent": 980, "responded": 312, "responseRate": 0.318, "hourlyCount": 15, "timeUntilSlot": 45 } ``` **Fields**: | Field | Type | Description | |---|---|---| | `running` | boolean | Whether the bot is currently running | | `processed` | number | Total posts processed | | `sent` | number | Total messages sent | | `responded` | number | Total messages that received responses | | `responseRate` | number | Response rate (0.0 to 1.0) | | `hourlyCount` | number | Messages sent this hour | | `timeUntilSlot` | number | Seconds until next message slot | --- ### Start Bot Start the bot's main processing loop. ``` POST /api/start ``` **Response**: ```json { "success": true, "message": "Bot started" } ``` **Errors**: ```json { "error": "Bot is already running" } ``` --- ### Stop Bot Stop the bot's main processing loop gracefully. ``` POST /api/stop ``` **Response**: ```json { "success": true, "message": "Bot stopping..." } ``` --- ## Activity & Messages ### Get Activity Log Retrieve recent activity entries from the bot's log. ``` GET /api/activity ``` **Query Parameters**: | Parameter | Type | Default | Description | |---|---|---|---| | `limit` | number | 50 | Number of entries to return | **Response**: ```json [ { "level": "info", "message": "Processed post abc123 from r/gonewild", "timestamp": "2024-01-15T10:30:00.000Z" }, { "level": "warn", "message": "AI model refused to analyze post def456", "timestamp": "2024-01-15T10:28:00.000Z" }, { "level": "error", "message": "Failed to send DM to user789: timeout", "timestamp": "2024-01-15T10:25:00.000Z" } ] ``` --- ### Get Posts by Author Look up processed posts for a specific Reddit user. ``` GET /api/posts/by-author/:username ``` **Path Parameters**: | Parameter | Type | Description | |---|---|---| | `username` | string | Reddit username | **Response**: ```json [ { "id": "abc123", "subreddit": "gonewild", "author": "user123", "title": "Summer vibes", "image_url": "https://...", "image_analysis": "{\"gender\":\"F\",\"description\":\"...\"}", "generated_message": "Hey, you look amazing...", "processed_at": 1716234567, "message_sent": 1, "error": null } ] ``` --- ### Get Sent Messages Retrieve recent sent messages. ``` GET /api/messages ``` **Query Parameters**: | Parameter | Type | Default | Description | |---|---|---|---| | `limit` | number | 50 | Number of messages to return | **Response**: ```json [ { "id": 1, "post_id": "abc123", "recipient": "user123", "subject": "Stunning", "message": "Hey, you look amazing...", "sent_at": "2024-01-15T10:30:00.000Z", "strategy_id": "compliment", "got_response": 1, "response_checked_at": "2024-01-15T11:00:00.000Z" } ] ``` --- ### Get Successful Messages Retrieve successful message/response pairs used for few-shot learning. ``` GET /api/successful ``` **Query Parameters**: | Parameter | Type | Default | Description | |---|---|---|---| | `limit` | number | 50 | Number of entries to return | **Response**: ```json [ { "id": 1, "sent_message_id": 1, "original_message": "Hey, you look amazing...", "image_analysis": "{\"gender\":\"F\",\"description\":\"...\"}", "post_title": "Summer vibes", "response_snippet": "Thank you so much! 😊", "subreddit": "gonewild", "created_at": "2024-01-15T10:30:00.000Z" } ] ``` --- ## Chat & Conversations ### Get Active Chats Retrieve cached active conversations. ``` GET /api/chats ``` **Response**: ```json [ { "chatId": "chat_abc123", "participant": "user456", "lastMessage": "Thanks for the compliment!", "timestamp": "2024-01-15T10:30:00.000Z", "hasNewMessages": false } ] ``` --- ### Fetch All Chats Force-fetch all chats from Reddit (refreshes cache). ``` POST /api/chats/fetch-all ``` **Response**: ```json { "success": true, "count": 15 } ``` --- ### Get Conversations Retrieve all cached conversations. ``` GET /api/conversations ``` **Response**: ```json [ { "chatId": "chat_abc123", "participants": ["user456", "bot"], "messageCount": 24, "lastUpdated": "2024-01-15T10:30:00.000Z", "hasNewMessages": false, "pendingReply": false, "isUpsell": false } ] ``` --- ### Get Conversation Detail Retrieve full details of a specific conversation. ``` GET /api/conversations/:id ``` **Path Parameters**: | Parameter | Type | Description | |---|---|---| | `id` | string | Chat ID | **Response**: ```json { "chatId": "chat_abc123", "participants": ["user456", "bot"], "messages": [ { "author": "user456", "text": "Hey there!", "timestamp": "2024-01-15T10:00:00.000Z" }, { "author": "bot", "text": "Hi! Thanks for the compliment!", "timestamp": "2024-01-15T10:01:00.000Z" }, { "author": "user456", "text": "Thanks for the compliment!", "timestamp": "2024-01-15T10:02:00.000Z" } ], "lastUpdated": "2024-01-15T10:30:00.000Z", "hasNewMessages": false, "pendingReply": false, "isUpsell": false } ``` --- ### Suggest Reply Generate an AI reply suggestion for a conversation. ``` POST /api/conversations/:id/suggest-reply ``` **Path Parameters**: | Parameter | Type | Description | |---|---|---| | `id` | string | Chat ID | **Response**: ```json { "success": true, "reply": "Thanks for reaching out! 😊 How's your day going?" } ``` --- ### Send Reply Send a manual reply in a conversation. ``` POST /api/conversations/:id/send-reply ``` **Path Parameters**: | Parameter | Type | Description | |---|---|---| | `id` | string | Chat ID | **Request Body**: ```json { "text": "Thanks for reaching out! 😊" } ``` **Response**: ```json { "success": true, "delivered": true } ``` --- ### Approve Pending Reply Approve an auto-reply that is pending manual approval. ``` POST /api/conversations/:id/approve-reply ``` **Path Parameters**: | Parameter | Type | Description | |---|---|---| | `id` | string | Chat ID | **Response**: ```json { "success": true, "message": "Reply sent" } ``` --- ### Reject Pending Reply Reject an auto-reply that is pending manual approval. ``` POST /api/conversations/:id/reject-reply ``` **Path Parameters**: | Parameter | Type | Description | |---|---|---| | `id` | string | Chat ID | **Response**: ```json { "success": true, "message": "Reply rejected" } ``` --- ### Dismiss Upsell Flag Clear the upsell/spam flag on a conversation. ``` POST /api/conversations/:id/dismiss-upsell ``` **Path Parameters**: | Parameter | Type | Description | |---|---|---| | `id` | string | Chat ID | **Response**: ```json { "success": true, "message": "Upsell flag dismissed" } ``` --- ## Media ### Get Media Gallery Retrieve downloaded media files. ``` GET /api/media ``` **Query Parameters**: | Parameter | Type | Default | Description | |---|---|---|---| | `limit` | number | 100 | Number of files to return | **Response**: ```json [ { "hash": "abc123def456", "filename": "abc123def456.jpg", "size": 245678, "conversationId": "chat_abc123", "downloadedAt": "2024-01-15T10:30:00.000Z" } ] ``` --- ### Get Media Sync Status Retrieve current media sync status. ``` GET /api/media/sync ``` **Response**: ```json { "running": false, "mode": null, "progress": 0, "total": 0, "completed": 0, "lastSync": "2024-01-15T09:00:00.000Z" } ``` --- ### Trigger Incremental Sync Start an incremental media sync (top 10 conversations). ``` POST /api/media/sync ``` **Response**: ```json { "success": true, "message": "Incremental sync started" } ``` --- ### Trigger Full Sync Start a full media sync (all conversations). ``` POST /api/media/sync/full ``` **Response**: ```json { "success": true, "message": "Full sync started" } ``` --- ## Analytics ### Response Rate by Subreddit Get response rates broken down by subreddit. ``` GET /api/analytics/by-subreddit ``` **Response**: ```json [ { "subreddit": "gonewild", "sent": 500, "responded": 175, "rate": 0.35 }, { "subreddit": "ratemyboobs", "sent": 300, "responded": 90, "rate": 0.30 }, { "subreddit": "HomemadeNsfw", "sent": 180, "responded": 72, "rate": 0.40 } ] ``` --- ### Response Rate by Strategy Get response rates broken down by A/B test variant. ``` GET /api/analytics/by-strategy ``` **Response**: ```json [ { "strategy_id": "compliment", "sent": 400, "responded": 140, "rate": 0.35 }, { "strategy_id": "question", "sent": 350, "responded": 147, "rate": 0.42 }, { "strategy_id": "observation", "sent": 230, "responded": 69, "rate": 0.30 } ] ``` --- ### Daily Response Trend Get daily response rates over time. ``` GET /api/analytics/trend ``` **Response**: ```json [ { "date": "2024-01-01", "sent": 18, "responded": 6, "rate": 0.33 }, { "date": "2024-01-02", "sent": 22, "responded": 8, "rate": 0.36 }, { "date": "2024-01-03", "sent": 15, "responded": 4, "rate": 0.27 } ] ``` --- ## Configuration ### Get Runtime Configuration Get current runtime configuration. ``` GET /api/config ``` **Response**: ```json { "reddit": { "subreddits": ["gonewild", "ratemyboobs"], "pollInterval": 15, "targetGender": "F", "sellerKeywords": ["seller", "onlyfans"] }, "openrouter": { "model": "meta-llama/llama-3.2-90b-vision-preview", "fallbackModels": ["google/gemini-2.0-flash-001"] }, "rateLimit": { "minDelayMs": 30000, "maxMessagesPerHour": 20, "jitterMs": 15000 }, "browser": { "headless": true, "userDataDir": "./data/browser" }, "database": { "path": "./data/reddit-bot.db" }, "mediaSync": { "enabled": true, "intervalMinutes": 30 }, "autoReply": { "enabled": true, "autoSend": false, "checkInterval": 10 }, "strategyVariants": [ { "name": "compliment", "enabled": true } ], "web": { "port": 3000 }, "dryRun": false, "fewShotExamples": 3, "minStrategySamples": 10, "explorationRate": 0.15 } ``` --- ### Update Runtime Configuration Update runtime configuration values without restarting. ``` PUT /api/config ``` **Request Body** (partial updates supported): ```json { "rateLimit": { "minDelayMs": 45000, "maxMessagesPerHour": 15 }, "dryRun": true, "reddit": { "pollInterval": 20 } } ``` **Response**: ```json { "success": true, "config": { ... } } ``` **Errors**: ```json { "error": "Invalid config: rateLimit.minDelayMs must be a positive integer" } ``` --- ## Logs & Debugging ### SSE Log Stream Connect to the realtime log stream via Server-Sent Events. ``` GET /api/logs/stream ``` **Headers**: | Header | Value | |---|---| | Accept | `text/event-stream` | **Response** (streaming): ``` event: log data: {"level":"info","message":"Bot started","timestamp":"2024-01-15T10:30:00.000Z"} event: log data: {"level":"info","message":"Processed post abc123","timestamp":"2024-01-15T10:30:05.000Z"} event: log data: {"level":"warn","message":"AI refusal detected","timestamp":"2024-01-15T10:30:10.000Z"} ``` **Client usage (JavaScript)**: ```javascript const eventSource = new EventSource('/api/logs/stream'); eventSource.onmessage = (event) => { const log = JSON.parse(event.data); console.log(log.level, log.message); }; // Close connection eventSource.close(); ``` --- ### Get Recent Logs Retrieve recent log entries via HTTP (non-streaming). ``` GET /api/logs ``` **Query Parameters**: | Parameter | Type | Default | Description | |---|---|---|---| | `limit` | number | 100 | Number of entries to return | | `level` | string | all | Filter by level: `info`, `warn`, `error` | **Response**: Same format as `/api/activity`. --- ### Get Rate Limiter Timers Get current rate limiter status. ``` GET /api/timers ``` **Response**: ```json { "timeUntilSlot": 45, "hourlyCount": 15, "maxMessagesPerHour": 20, "isThrottled": false } ``` --- ### Debug: Inspect DOM Inspect the current chat DOM structure (for debugging shadow DOM issues). ``` POST /api/debug/inspect-dom ``` **Response**: ```json { "success": true, "domStructure": { ... } } ``` --- ### Debug: Manual Response Check Trigger a manual response check (bypasses scheduled interval). ``` POST /api/debug/check-responses ``` **Response**: ```json { "success": true, "responsesFound": 3, "updated": 2 } ``` --- ## Error Responses All error responses follow this format: ```json { "error": "Error message describing what went wrong" } ``` ### Common Error Codes | HTTP Status | Description | Example | |---|---|---| | 400 | Bad Request | Invalid query parameter | | 404 | Not Found | Resource not found | | 500 | Internal Server Error | Unexpected error | --- ## SSE Events `GET /api/events/stream` is the typed resumable event stream used by the React dashboard. Each SSE frame contains `id`, `event`, and `data` fields. The data is the complete validated event envelope: ```json { "id": "1786201200000-000000", "occurredAt": "2026-08-08T15:00:00.000Z", "type": "log.created", "payload": { "level": "info", "message": "Dashboard started" } } ``` Clients may reconnect with `Last-Event-ID`; the server replays retained events after that ID in order. The stream sends heartbeats, bounds buffered events and slow-client queues, and requires the same dashboard authentication as the API. Current event families include bot status, message sent or failed, conversation updated, reply received, sync lifecycle and progress, rate-limit state, Reddit session state, and logs. `GET /api/logs/stream` remains temporarily available for the legacy dashboard. Its untyped `connected`, `log`, and `sync-progress` frames should not be used by new clients. **Log Levels**: | Level | Description | |---|---| | `info` | Normal operation | | `warn` | Warning (recoverable issue) | | `error` | Error (action failed) | | `debug` | Debug information |