1020 lines
16 KiB
Markdown
1020 lines
16 KiB
Markdown
# 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**: Required for cutover; not yet enforced by the checked-in Express host
|
|
|
|
---
|
|
|
|
## Authentication
|
|
|
|
The React cutover requires a single Express authentication boundary covering
|
|
the dashboard assets, `/api`, both SSE streams, debug routes, and private media.
|
|
That boundary and its token/session configuration are not present in the
|
|
checked-in server yet. Until they land and their security tests pass, bind the
|
|
dashboard to a trusted network and do not expose port 3000 publicly.
|
|
|
|
The intended contract is an HttpOnly, SameSite=Strict dashboard session for
|
|
interactive users and bearer-token access for API clients. Treat those details
|
|
as a migration requirement, not as proof of current protection.
|
|
|
|
---
|
|
|
|
## 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 uses the same authentication boundary 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 compatibility tooling.
|
|
New clients use the typed `/api/events/stream` endpoint.
|
|
|
|
**Log Levels**:
|
|
|
|
| Level | Description |
|
|
|---|---|
|
|
| `info` | Normal operation |
|
|
| `warn` | Warning (recoverable issue) |
|
|
| `error` | Error (action failed) |
|
|
| `debug` | Debug information |
|