diff --git a/.agent/logs/20260410_stats_api.md b/.agent/logs/20260410_stats_api.md new file mode 100644 index 00000000..0c679773 --- /dev/null +++ b/.agent/logs/20260410_stats_api.md @@ -0,0 +1,567 @@ +# User Statistics API Documentation + +## Overview + +The User Statistics API provides comprehensive analytics about user growth, engagement, activity, and content distribution. All statistics support flexible time intervals and are optimized for dashboard visualizations. + +**Base URL**: `/api/stats` + +**Authentication**: All endpoints require API key authentication via Bearer token. + +--- + +## Authentication + +Include your API key in the `Authorization` header: + +```bash +Authorization: Bearer YOUR_STATS_API_KEY +``` + +Set the API key via the `STATS_API_KEY` environment variable. + +--- + +## Base Endpoint + +``` +GET /api/stats +``` + +All statistics are accessed through this single endpoint using the `type` query parameter to specify the stat type. + +--- + +## Query Parameters + +| Parameter | Type | Required | Description | Valid Values | +| ------------------- | ------- | -------- | ------------------------------------------ | ----------------------------------------------- | +| `type` | string | Yes | Type of statistics to retrieve | `growth`, `engagement`, `activity`, `breakdown` | +| `interval` | string | No | Time interval for aggregation | `day`, `week`, `month` (default: `day`) | +| `limit` | number | No | Number of intervals to return | 1-365 (default: 30) | +| `includeTimeSeries` | boolean | No | Include time series data (engagement only) | `true`, `false` (default: `false`) | + +--- + +## Stat Types + +### 1. Growth Statistics + +**Description**: Track user sign-ups over time with cumulative totals and period-over-period growth. + +**Query Parameters**: + +- `type=growth` (required) +- `interval` (optional, default: `day`) +- `limit` (optional, default: 30) + +**Request Example**: + +```bash +curl -X GET "http://localhost:3000/api/stats?type=growth&interval=day&limit=30" \ + -H "Authorization: Bearer YOUR_STATS_API_KEY" +``` + +**Response Schema**: + +```typescript +{ + dataPoints: Array<{ + date: string; // ISO date string + totalUsers: number; // Cumulative total users up to this date + newUsers: number; // New users added in this period + }>; + currentTotal: number; // Total users as of now + periodStart: string; // ISO date string of first data point + periodEnd: string; // ISO date string of last data point +} +``` + +**Example Response**: + +```json +{ + "dataPoints": [ + { + "date": "2026-03-11T00:00:00.000Z", + "totalUsers": 100, + "newUsers": 15 + }, + { + "date": "2026-03-12T00:00:00.000Z", + "totalUsers": 112, + "newUsers": 12 + }, + { + "date": "2026-03-13T00:00:00.000Z", + "totalUsers": 125, + "newUsers": 13 + } + ], + "currentTotal": 125, + "periodStart": "2026-03-11T00:00:00.000Z", + "periodEnd": "2026-03-13T00:00:00.000Z" +} +``` + +--- + +### 2. Engagement Statistics + +**Description**: Track active vs inactive users with detailed content breakdown. Supports optional time series data. + +**Note**: Only counts URL cards (excludes NOTE and HIGHLIGHT cards). + +**Query Parameters**: + +- `type=engagement` (required) +- `interval` (optional, default: `day`, used if `includeTimeSeries=true`) +- `limit` (optional, default: 30, used if `includeTimeSeries=true`) +- `includeTimeSeries` (optional, default: `false`) + +**Request Example (Snapshot)**: + +```bash +curl -X GET "http://localhost:3000/api/stats?type=engagement" \ + -H "Authorization: Bearer YOUR_STATS_API_KEY" +``` + +**Request Example (With Time Series)**: + +```bash +curl -X GET "http://localhost:3000/api/stats?type=engagement&includeTimeSeries=true&interval=week&limit=12" \ + -H "Authorization: Bearer YOUR_STATS_API_KEY" +``` + +**Response Schema**: + +```typescript +{ + // Snapshot data (always included) + totalUsers: number; // Total registered users + activeUsers: number; // Users who created any content + inactiveUsers: number; // Users who signed in but created nothing + + // Activity breakdown + usersWithCards: number; // Users who created URL cards + usersWithCollections: number; // Users who created collections + usersWithConnections: number; // Users who created connections + usersWithFollows: number; // Users who followed targets + usersWithContributions: number; // Users who added cards to others' collections + + // Engagement metrics + activationRate: number; // activeUsers / totalUsers + avgActionsPerActiveUser: number; // Average actions per active user + + // Optional time series data (only if includeTimeSeries=true) + dataPoints?: Array<{ + date: string; // ISO date string + activeUsers: number; // Users activated in this period + newlyActivatedUsers: number; // Previously inactive users who became active + cumulativeActiveUsers: number; // Total active users up to this date + }>; +} +``` + +**Example Response (Snapshot)**: + +```json +{ + "totalUsers": 500, + "activeUsers": 320, + "inactiveUsers": 180, + "usersWithCards": 280, + "usersWithCollections": 150, + "usersWithConnections": 90, + "usersWithFollows": 200, + "usersWithContributions": 45, + "activationRate": 0.64, + "avgActionsPerActiveUser": 12.5 +} +``` + +**Example Response (With Time Series)**: + +```json +{ + "totalUsers": 500, + "activeUsers": 320, + "inactiveUsers": 180, + "usersWithCards": 280, + "usersWithCollections": 150, + "usersWithConnections": 90, + "usersWithFollows": 200, + "usersWithContributions": 45, + "activationRate": 0.64, + "avgActionsPerActiveUser": 12.5, + "dataPoints": [ + { + "date": "2026-03-01T00:00:00.000Z", + "activeUsers": 25, + "newlyActivatedUsers": 25, + "cumulativeActiveUsers": 275 + }, + { + "date": "2026-03-08T00:00:00.000Z", + "activeUsers": 30, + "newlyActivatedUsers": 30, + "cumulativeActiveUsers": 305 + }, + { + "date": "2026-03-15T00:00:00.000Z", + "activeUsers": 15, + "newlyActivatedUsers": 15, + "cumulativeActiveUsers": 320 + } + ] +} +``` + +--- + +### 3. Activity Statistics + +**Description**: Track daily content creation volume across all content types. + +**Note**: Only counts URL cards (excludes NOTE and HIGHLIGHT cards). + +**Query Parameters**: + +- `type=activity` (required) +- `interval` (optional, default: `day`) +- `limit` (optional, default: 30) + +**Request Example**: + +```bash +curl -X GET "http://localhost:3000/api/stats?type=activity&interval=day&limit=7" \ + -H "Authorization: Bearer YOUR_STATS_API_KEY" +``` + +**Response Schema**: + +```typescript +{ + dataPoints: Array<{ + date: string; // ISO date string + cardsCreated: number; // URL cards created in this period + collectionsCreated: number; // Collections created in this period + connectionsCreated: number; // Connections created in this period + followsCreated: number; // Follows created in this period + totalActions: number; // Sum of all above + }>; + totals: { + cardsCreated: number; // Total across all periods + collectionsCreated: number; // Total across all periods + connectionsCreated: number; // Total across all periods + followsCreated: number; // Total across all periods + totalActions: number; // Sum of all totals + } + periodStart: string; // ISO date string of first data point + periodEnd: string; // ISO date string of last data point +} +``` + +**Example Response**: + +```json +{ + "dataPoints": [ + { + "date": "2026-04-08T00:00:00.000Z", + "cardsCreated": 45, + "collectionsCreated": 12, + "connectionsCreated": 8, + "followsCreated": 23, + "totalActions": 88 + }, + { + "date": "2026-04-09T00:00:00.000Z", + "cardsCreated": 52, + "collectionsCreated": 15, + "connectionsCreated": 10, + "followsCreated": 28, + "totalActions": 105 + }, + { + "date": "2026-04-10T00:00:00.000Z", + "cardsCreated": 38, + "collectionsCreated": 9, + "connectionsCreated": 6, + "followsCreated": 19, + "totalActions": 72 + } + ], + "totals": { + "cardsCreated": 135, + "collectionsCreated": 36, + "connectionsCreated": 24, + "followsCreated": 70, + "totalActions": 265 + }, + "periodStart": "2026-04-08T00:00:00.000Z", + "periodEnd": "2026-04-10T00:00:00.000Z" +} +``` + +--- + +### 4. Breakdown Statistics + +**Description**: Track content distribution by subtypes over time. Shows cumulative growth of different content categories. + +**Breakdowns**: + +- URL cards by `urlType` (article, video, tool, etc.) +- Collections by `accessType` (OPEN, CLOSED) +- Connections by `connectionType` (SUPPORTS, OPPOSES, unspecified) + +**Note**: Only counts URL cards (excludes NOTE and HIGHLIGHT cards). + +**Query Parameters**: + +- `type=breakdown` (required) +- `interval` (optional, default: `day`) +- `limit` (optional, default: 30) + +**Request Example**: + +```bash +curl -X GET "http://localhost:3000/api/stats?type=breakdown&interval=week&limit=4" \ + -H "Authorization: Bearer YOUR_STATS_API_KEY" +``` + +**Response Schema**: + +```typescript +{ + dataPoints: Array<{ + date: string; // ISO date string + urlCards: { + total: number; // Total URL cards at this date + byType: Record; // Breakdown by urlType + }; + collections: { + total: number; // Total collections at this date + byAccessType: Record; // Breakdown by accessType + }; + connections: { + total: number; // Total connections at this date + byType: Record; // Breakdown by connectionType + }; + }>; + currentTotals: { + urlCards: { + total: number; // Current total URL cards + byType: Record; // Current breakdown by urlType + } + collections: { + total: number; // Current total collections + byAccessType: Record; // Current breakdown by accessType + } + connections: { + total: number; // Current total connections + byType: Record; // Current breakdown by connectionType + } + } + periodStart: string; // ISO date string of first data point + periodEnd: string; // ISO date string of last data point +} +``` + +**Example Response**: + +```json +{ + "dataPoints": [ + { + "date": "2026-03-17T00:00:00.000Z", + "urlCards": { + "total": 450, + "byType": { + "article": 180, + "video": 120, + "tool": 90, + "unspecified": 60 + } + }, + "collections": { + "total": 85, + "byAccessType": { + "OPEN": 60, + "CLOSED": 25 + } + }, + "connections": { + "total": 120, + "byType": { + "SUPPORTS": 70, + "OPPOSES": 30, + "unspecified": 20 + } + } + }, + { + "date": "2026-03-24T00:00:00.000Z", + "urlCards": { + "total": 520, + "byType": { + "article": 210, + "video": 140, + "tool": 105, + "unspecified": 65 + } + }, + "collections": { + "total": 98, + "byAccessType": { + "OPEN": 72, + "CLOSED": 26 + } + }, + "connections": { + "total": 145, + "byType": { + "SUPPORTS": 85, + "OPPOSES": 38, + "unspecified": 22 + } + } + } + ], + "currentTotals": { + "urlCards": { + "total": 520, + "byType": { + "article": 210, + "video": 140, + "tool": 105, + "unspecified": 65 + } + }, + "collections": { + "total": 98, + "byAccessType": { + "OPEN": 72, + "CLOSED": 26 + } + }, + "connections": { + "total": 145, + "byType": { + "SUPPORTS": 85, + "OPPOSES": 38, + "unspecified": 22 + } + } + }, + "periodStart": "2026-03-17T00:00:00.000Z", + "periodEnd": "2026-03-24T00:00:00.000Z" +} +``` + +--- + +## Error Responses + +All endpoints return standard error responses: + +**Validation Error** (400): + +```json +{ + "error": "Stat type is required" +} +``` + +**Authentication Error** (401): + +```json +{ + "error": "Unauthorized" +} +``` + +**Server Error** (500): + +```json +{ + "error": "Failed to retrieve user stats: " +} +``` + +--- + +## Implementation Notes + +### Card Counting + +- **Important**: All card counts refer exclusively to URL cards +- NOTE cards and HIGHLIGHT cards are excluded from all statistics +- This applies to: engagement stats, activity stats, and breakdown stats + +### Time Intervals + +- `day`: Data aggregated by calendar day +- `week`: Data aggregated by calendar week (starts Monday) +- `month`: Data aggregated by calendar month + +### Data Points Ordering + +- All time series data is returned in **chronological order** (oldest to newest) +- This makes it easy to render charts and track trends over time + +### Performance + +- Queries are optimized with indexes on relevant columns +- Large date ranges (limit > 100) may take longer to compute +- Breakdown stats execute three queries in parallel for better performance + +### Null Handling + +- Null values in `urlType` and `connectionType` are mapped to `"unspecified"` +- This ensures consistent data structure in breakdown responses + +--- + +## Common Use Cases + +### Dashboard Overview + +Get current engagement snapshot: + +```bash +GET /api/stats?type=engagement +``` + +### Growth Chart + +Get 90 days of user growth by week: + +```bash +GET /api/stats?type=growth&interval=week&limit=13 +``` + +### Activity Heatmap + +Get daily activity for the past month: + +```bash +GET /api/stats?type=activity&interval=day&limit=30 +``` + +### Content Distribution Trends + +Track content type growth over 6 months: + +```bash +GET /api/stats?type=breakdown&interval=month&limit=6 +``` + +### Activation Funnel + +Get engagement with time series to track activation trends: + +```bash +GET /api/stats?type=engagement&includeTimeSeries=true&interval=week&limit=12 +```