999 lines
19 KiB
Markdown
999 lines
19 KiB
Markdown
# Life Management System — API Design
|
|
|
|
## Overview
|
|
|
|
REST API at `http://localhost:3700/api/*` with Socket.IO WebSocket gateways on the same port. All responses follow consistent JSON format. Pagination via query params. Validation via `class-validator` DTOs.
|
|
|
|
**Base URL:** `http://localhost:3700/api`
|
|
**Swagger:** `http://localhost:3700/api-docs`
|
|
|
|
## Common Patterns
|
|
|
|
### Pagination
|
|
|
|
All list endpoints accept:
|
|
|
|
```
|
|
?page=1&limit=20&sort=createdAt&order=DESC
|
|
```
|
|
|
|
**Response envelope:**
|
|
```json
|
|
{
|
|
"data": [...],
|
|
"meta": {
|
|
"page": 1,
|
|
"limit": 20,
|
|
"total": 142,
|
|
"totalPages": 8
|
|
}
|
|
}
|
|
```
|
|
|
|
### Error Response
|
|
|
|
```json
|
|
{
|
|
"statusCode": 400,
|
|
"error": "Bad Request",
|
|
"message": "Validation failed",
|
|
"details": [
|
|
{ "field": "title", "message": "title should not be empty" }
|
|
]
|
|
}
|
|
```
|
|
|
|
### Filtering
|
|
|
|
List endpoints support filtering via query params specific to each entity. Common patterns:
|
|
- `?domainId=<uuid>` — Filter by domain
|
|
- `?status=todo,in_progress` — Comma-separated enum values
|
|
- `?startDate=2026-01-01&endDate=2026-01-31` — Date ranges
|
|
|
|
---
|
|
|
|
## Health
|
|
|
|
### `GET /health`
|
|
|
|
Health check endpoint (via `@lilith/nestjs-health`).
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"status": "ok",
|
|
"info": {
|
|
"database": { "status": "up" },
|
|
"redis": { "status": "up" }
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Domains Module
|
|
|
|
### `GET /api/domains`
|
|
|
|
List all domains.
|
|
|
|
**Query params:** `?isActive=true`
|
|
|
|
**Response:** `Domain[]`
|
|
```json
|
|
[
|
|
{
|
|
"id": "uuid",
|
|
"name": "Christine's Startup",
|
|
"type": "client_work",
|
|
"slug": "christine",
|
|
"color": "#4A90D9",
|
|
"icon": "briefcase",
|
|
"description": "Primary client contract",
|
|
"isActive": true,
|
|
"sortOrder": 0,
|
|
"config": {},
|
|
"createdAt": "2026-01-01T00:00:00.000Z",
|
|
"updatedAt": "2026-01-01T00:00:00.000Z"
|
|
}
|
|
]
|
|
```
|
|
|
|
### `GET /api/domains/:id`
|
|
|
|
Get domain by ID.
|
|
|
|
### `GET /api/domains/slug/:slug`
|
|
|
|
Get domain by slug (for URL-based lookups).
|
|
|
|
### `POST /api/domains`
|
|
|
|
Create domain.
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"name": "New Domain",
|
|
"type": "side_project",
|
|
"slug": "new-domain",
|
|
"color": "#FF5733",
|
|
"icon": "star",
|
|
"description": "Optional description",
|
|
"config": {}
|
|
}
|
|
```
|
|
|
|
### `PATCH /api/domains/:id`
|
|
|
|
Update domain (partial).
|
|
|
|
### `DELETE /api/domains/:id`
|
|
|
|
Delete domain (hard delete — only if no associated entities).
|
|
|
|
---
|
|
|
|
## Tasks Module
|
|
|
|
### `GET /api/tasks`
|
|
|
|
List tasks with filtering.
|
|
|
|
**Query params:**
|
|
- `?domainId=<uuid>` — Filter by domain
|
|
- `?goalId=<uuid>` — Filter by goal
|
|
- `?sprintId=<uuid>` — Filter by sprint
|
|
- `?parentId=<uuid>` — Filter subtasks of a parent
|
|
- `?status=todo,in_progress` — Status filter (comma-separated)
|
|
- `?priority=critical,high` — Priority filter
|
|
- `?energyLevel=low,medium` — Energy level filter
|
|
- `?isQuickWin=true` — Quick wins only
|
|
- `?overdue=true` — Overdue tasks only (due_date < today, status not done/cancelled)
|
|
- `?scheduledDate=2026-02-24` — Scheduled for specific date
|
|
- `?dueDateStart=&dueDateEnd=` — Due date range
|
|
- `?search=<text>` — Full-text search on title
|
|
- `?tags=urgent,client` — Tag filter (comma-separated, AND logic)
|
|
- `?includeSubtasks=true` — Include subtask tree
|
|
|
|
**Response:** Paginated `Task[]`
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"id": "uuid",
|
|
"domainId": "uuid",
|
|
"domain": { "id": "uuid", "name": "...", "slug": "...", "color": "..." },
|
|
"goalId": null,
|
|
"sprintId": null,
|
|
"parentId": null,
|
|
"title": "Design review for Christine",
|
|
"description": "Review the latest mockups",
|
|
"status": "todo",
|
|
"priority": "high",
|
|
"energyLevel": "high",
|
|
"estimatedMinutes": 60,
|
|
"actualMinutes": null,
|
|
"dueDate": "2026-02-24",
|
|
"scheduledDate": "2026-02-24",
|
|
"isQuickWin": false,
|
|
"recurrenceRule": null,
|
|
"tags": ["design", "client"],
|
|
"sortOrder": 0,
|
|
"subtaskCount": 3,
|
|
"subtaskCompletedCount": 1,
|
|
"createdAt": "...",
|
|
"updatedAt": "..."
|
|
}
|
|
],
|
|
"meta": { "page": 1, "limit": 20, "total": 45, "totalPages": 3 }
|
|
}
|
|
```
|
|
|
|
### `GET /api/tasks/:id`
|
|
|
|
Get task by ID with subtasks.
|
|
|
|
**Response:** `Task` with nested `subtasks: Task[]`
|
|
|
|
### `POST /api/tasks`
|
|
|
|
Create task.
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"domainId": "uuid",
|
|
"goalId": null,
|
|
"sprintId": null,
|
|
"parentId": null,
|
|
"title": "New task",
|
|
"description": "Description",
|
|
"status": "todo",
|
|
"priority": "medium",
|
|
"energyLevel": "medium",
|
|
"estimatedMinutes": 30,
|
|
"dueDate": "2026-03-01",
|
|
"scheduledDate": null,
|
|
"isQuickWin": false,
|
|
"recurrenceRule": null,
|
|
"tags": ["tag1"]
|
|
}
|
|
```
|
|
|
|
### `PATCH /api/tasks/:id`
|
|
|
|
Update task (partial).
|
|
|
|
### `PATCH /api/tasks/:id/status`
|
|
|
|
Quick status transition.
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"status": "done",
|
|
"actualMinutes": 45
|
|
}
|
|
```
|
|
|
|
### `DELETE /api/tasks/:id`
|
|
|
|
Soft delete (sets `deletedAt`).
|
|
|
|
### `POST /api/tasks/:id/reorder`
|
|
|
|
Reorder task within its list.
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"sortOrder": 3
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Goals Module
|
|
|
|
### `GET /api/goals`
|
|
|
|
List goals with filtering.
|
|
|
|
**Query params:**
|
|
- `?domainId=<uuid>` — Filter by domain
|
|
- `?level=yearly,quarterly` — Filter by level
|
|
- `?status=active` — Filter by status
|
|
- `?parentId=<uuid>` — Children of a specific goal
|
|
|
|
### `GET /api/goals/tree`
|
|
|
|
Get hierarchical goal tree.
|
|
|
|
**Query params:**
|
|
- `?domainId=<uuid>` — Optional domain filter
|
|
|
|
**Response:** Nested tree structure:
|
|
```json
|
|
[
|
|
{
|
|
"id": "uuid",
|
|
"title": "Ship Lilith Platform v1",
|
|
"level": "yearly",
|
|
"status": "active",
|
|
"progressPct": 35,
|
|
"children": [
|
|
{
|
|
"id": "uuid",
|
|
"title": "Complete core modules",
|
|
"level": "quarterly",
|
|
"progressPct": 60,
|
|
"children": [
|
|
{
|
|
"id": "uuid",
|
|
"title": "Finish auth system",
|
|
"level": "monthly",
|
|
"progressPct": 80,
|
|
"children": []
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
```
|
|
|
|
### `GET /api/goals/:id`
|
|
|
|
Get goal with children and linked tasks.
|
|
|
|
### `POST /api/goals`
|
|
|
|
Create goal.
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"domainId": "uuid",
|
|
"parentId": null,
|
|
"title": "Goal title",
|
|
"description": "Description",
|
|
"level": "quarterly",
|
|
"targetDate": "2026-06-30"
|
|
}
|
|
```
|
|
|
|
### `PATCH /api/goals/:id`
|
|
|
|
Update goal.
|
|
|
|
### `PATCH /api/goals/:id/progress`
|
|
|
|
Update progress percentage.
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"progressPct": 75
|
|
}
|
|
```
|
|
|
|
### `DELETE /api/goals/:id`
|
|
|
|
Delete goal (cascading to orphan children — re-parents to grandparent or null).
|
|
|
|
---
|
|
|
|
## Habits Module
|
|
|
|
### `GET /api/habits`
|
|
|
|
List habits.
|
|
|
|
**Query params:**
|
|
- `?domainId=<uuid>`
|
|
- `?isActive=true`
|
|
- `?frequency=daily`
|
|
- `?timeOfDay=morning`
|
|
|
|
**Response:** `Habit[]` with current streak info.
|
|
|
|
### `GET /api/habits/due-today`
|
|
|
|
Get habits due today based on frequency configuration and day of week.
|
|
|
|
**Response:** `Habit[]` with `checkedInToday: boolean` flag.
|
|
|
|
### `GET /api/habits/:id`
|
|
|
|
Get habit with recent check-in history (last 30 days).
|
|
|
|
### `POST /api/habits`
|
|
|
|
Create habit.
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"domainId": "uuid",
|
|
"goalId": null,
|
|
"name": "Morning meditation",
|
|
"description": "10 minutes mindfulness",
|
|
"frequency": "daily",
|
|
"frequencyConfig": {},
|
|
"timeOfDay": "morning",
|
|
"estimatedMinutes": 10
|
|
}
|
|
```
|
|
|
|
### `PATCH /api/habits/:id`
|
|
|
|
Update habit.
|
|
|
|
### `POST /api/habits/:id/check-in`
|
|
|
|
Record check-in for today.
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"status": "done",
|
|
"qualityRating": 4,
|
|
"notes": "Great session today"
|
|
}
|
|
```
|
|
|
|
**Response:** Updated `Habit` with new streak values.
|
|
|
|
### `GET /api/habits/:id/check-ins`
|
|
|
|
Get check-in history.
|
|
|
|
**Query params:**
|
|
- `?startDate=2026-01-01&endDate=2026-02-28`
|
|
|
|
**Response:** `HabitCheckIn[]`
|
|
|
|
### `GET /api/habits/:id/stats`
|
|
|
|
Get habit statistics.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"currentStreak": 14,
|
|
"bestStreak": 30,
|
|
"completionRate30d": 0.87,
|
|
"completionRate7d": 1.0,
|
|
"averageQuality": 3.8,
|
|
"totalCheckIns": 120,
|
|
"checkInsByStatus": { "done": 105, "skipped": 10, "partial": 5 }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Scheduling Module
|
|
|
|
### `GET /api/scheduling/time-blocks`
|
|
|
|
List time blocks.
|
|
|
|
**Query params:**
|
|
- `?date=2026-02-24` — Specific date
|
|
- `?startDate=&endDate=` — Date range
|
|
- `?domainId=<uuid>`
|
|
|
|
### `POST /api/scheduling/time-blocks`
|
|
|
|
Create time block.
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"domainId": "uuid",
|
|
"taskId": null,
|
|
"habitId": null,
|
|
"title": "Christine standup",
|
|
"date": "2026-02-24",
|
|
"startTime": "09:00",
|
|
"endTime": "10:30",
|
|
"blockType": "work",
|
|
"notes": ""
|
|
}
|
|
```
|
|
|
|
### `PATCH /api/scheduling/time-blocks/:id`
|
|
|
|
Update time block.
|
|
|
|
### `POST /api/scheduling/time-blocks/:id/reorder`
|
|
|
|
Reorder time block (update start/end times).
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"startTime": "10:00",
|
|
"endTime": "11:30"
|
|
}
|
|
```
|
|
|
|
### `DELETE /api/scheduling/time-blocks/:id`
|
|
|
|
Delete time block.
|
|
|
|
### `GET /api/scheduling/daily-plan/:date`
|
|
|
|
Get or initialize daily plan for a date.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"date": "2026-02-24",
|
|
"energyLevel": "medium",
|
|
"focusDomains": ["uuid1", "uuid2"],
|
|
"morningIntention": "Focus on Christine deliverables",
|
|
"eveningReflection": null,
|
|
"moodMorning": 3,
|
|
"moodEvening": null,
|
|
"timeBlocks": [...],
|
|
"createdAt": "...",
|
|
"updatedAt": "..."
|
|
}
|
|
```
|
|
|
|
### `PUT /api/scheduling/daily-plan/:date`
|
|
|
|
Create or update daily plan (upsert).
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"energyLevel": "medium",
|
|
"focusDomains": ["uuid1", "uuid2"],
|
|
"morningIntention": "Focus on Christine deliverables",
|
|
"moodMorning": 3
|
|
}
|
|
```
|
|
|
|
### `PATCH /api/scheduling/daily-plan/:date/reflection`
|
|
|
|
Update evening reflection (separate endpoint for end-of-day flow).
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"eveningReflection": "Good day, completed all priority tasks",
|
|
"moodEvening": 4
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Today Module (Aggregation)
|
|
|
|
### `GET /api/today`
|
|
|
|
Unified daily view — pulls from all modules for today.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"date": "2026-02-24",
|
|
"dailyPlan": {
|
|
"energyLevel": "medium",
|
|
"moodMorning": 3,
|
|
"morningIntention": "...",
|
|
"focusDomains": [...]
|
|
},
|
|
"timeBlocks": [...],
|
|
"priorityTasks": [...],
|
|
"quickWins": [...],
|
|
"habitsDueToday": [
|
|
{
|
|
"habit": { "id": "...", "name": "Morning meditation", ... },
|
|
"checkedIn": false,
|
|
"checkIn": null
|
|
}
|
|
],
|
|
"domainStats": [
|
|
{
|
|
"domain": { "id": "...", "name": "Christine's Startup", "color": "#4A90D9" },
|
|
"tasksDueToday": 2,
|
|
"tasksOverdue": 1,
|
|
"incomeThisWeek": "420.00",
|
|
"currentStreak": 7,
|
|
"sprintProgress": 60
|
|
}
|
|
],
|
|
"overdueTasks": [...],
|
|
"completedToday": 5,
|
|
"totalToday": 12
|
|
}
|
|
```
|
|
|
|
### `GET /api/today/next-action`
|
|
|
|
Get suggested next action based on energy, priority, time of day.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"task": {
|
|
"id": "uuid",
|
|
"title": "Design review for Christine",
|
|
"domain": { "name": "Christine's Startup", "color": "#4A90D9" },
|
|
"priority": "high",
|
|
"energyLevel": "high",
|
|
"estimatedMinutes": 60
|
|
},
|
|
"score": 65,
|
|
"reason": "This is overdue and matches your current energy level"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Income Module (Encrypted)
|
|
|
|
### `GET /api/income`
|
|
|
|
List income entries (decrypted).
|
|
|
|
**Query params:**
|
|
- `?domainId=<uuid>`
|
|
- `?startDate=&endDate=`
|
|
- `?sourceType=session,tip`
|
|
- `?currency=GBP`
|
|
|
|
### `GET /api/income/summary`
|
|
|
|
Income summary with aggregation.
|
|
|
|
**Query params:**
|
|
- `?period=month|week|year`
|
|
- `?domainId=<uuid>` — Optional domain filter
|
|
- `?startDate=&endDate=` — Custom range
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"period": "month",
|
|
"startDate": "2026-02-01",
|
|
"endDate": "2026-02-28",
|
|
"total": "3420.00",
|
|
"currency": "GBP",
|
|
"byDomain": [
|
|
{ "domain": { "name": "Escort", "color": "#FF6B6B" }, "total": "2100.00", "count": 8 },
|
|
{ "domain": { "name": "OnlyFans", "color": "#00AFF0" }, "total": "820.00", "count": 45 },
|
|
{ "domain": { "name": "Christine's Startup", "color": "#4A90D9" }, "total": "500.00", "count": 1 }
|
|
],
|
|
"bySourceType": [
|
|
{ "sourceType": "session", "total": "2100.00", "count": 8 },
|
|
{ "sourceType": "subscription", "total": "620.00", "count": 40 },
|
|
{ "sourceType": "tip", "total": "200.00", "count": 5 },
|
|
{ "sourceType": "invoice", "total": "500.00", "count": 1 }
|
|
]
|
|
}
|
|
```
|
|
|
|
### `POST /api/income`
|
|
|
|
Create income entry (encrypted on write).
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"domainId": "uuid",
|
|
"date": "2026-02-24",
|
|
"amount": "150.00",
|
|
"currency": "GBP",
|
|
"sourceType": "session",
|
|
"description": "Evening session",
|
|
"durationMinutes": 120,
|
|
"contactId": "uuid"
|
|
}
|
|
```
|
|
|
|
### `PATCH /api/income/:id`
|
|
|
|
Update income entry.
|
|
|
|
### `DELETE /api/income/:id`
|
|
|
|
Delete income entry.
|
|
|
|
### `GET /api/income/billable`
|
|
|
|
List billable entries.
|
|
|
|
**Query params:**
|
|
- `?domainId=<uuid>`
|
|
- `?isInvoiced=false`
|
|
- `?startDate=&endDate=`
|
|
|
|
### `POST /api/income/billable`
|
|
|
|
Create billable entry.
|
|
|
|
### `PATCH /api/income/billable/:id`
|
|
|
|
Update billable entry (mark as invoiced, etc.).
|
|
|
|
---
|
|
|
|
## Health Module (Encrypted)
|
|
|
|
### `GET /api/health/measurements`
|
|
|
|
List measurements.
|
|
|
|
**Query params:**
|
|
- `?domainId=<uuid>`
|
|
- `?type=weight,waist`
|
|
- `?startDate=&endDate=`
|
|
|
|
### `POST /api/health/measurements`
|
|
|
|
Create measurement.
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"domainId": "uuid",
|
|
"date": "2026-02-24",
|
|
"type": "weight",
|
|
"value": 62.5,
|
|
"unit": "kg",
|
|
"notes": "Morning, before breakfast"
|
|
}
|
|
```
|
|
|
|
### `GET /api/health/medical`
|
|
|
|
List medical entries (decrypted).
|
|
|
|
**Query params:**
|
|
- `?domainId=<uuid>`
|
|
- `?type=hrt_dose,appointment`
|
|
- `?startDate=&endDate=`
|
|
|
|
### `POST /api/health/medical`
|
|
|
|
Create medical entry (encrypted on write).
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"domainId": "uuid",
|
|
"date": "2026-02-24",
|
|
"type": "hrt_dose",
|
|
"title": "Estradiol injection",
|
|
"details": "0.5ml intramuscular, left thigh",
|
|
"provider": "Dr. Smith",
|
|
"nextDate": "2026-03-10"
|
|
}
|
|
```
|
|
|
|
### `PATCH /api/health/medical/:id`
|
|
|
|
Update medical entry.
|
|
|
|
---
|
|
|
|
## Contacts Module (Encrypted)
|
|
|
|
### `GET /api/contacts`
|
|
|
|
List contacts (decrypted).
|
|
|
|
**Query params:**
|
|
- `?domainId=<uuid>`
|
|
- `?contactType=client,agency`
|
|
- `?isActive=true`
|
|
- `?search=<name>`
|
|
|
|
### `GET /api/contacts/:id`
|
|
|
|
Get contact with interaction history.
|
|
|
|
### `POST /api/contacts`
|
|
|
|
Create contact (encrypted on write).
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"domainId": "uuid",
|
|
"name": "James",
|
|
"contactType": "client",
|
|
"phone": "+447700123456",
|
|
"email": "james@example.com",
|
|
"notes": "Regular, prefers weekday evenings",
|
|
"rating": 4,
|
|
"metadata": { "preferences": "..." }
|
|
}
|
|
```
|
|
|
|
### `PATCH /api/contacts/:id`
|
|
|
|
Update contact.
|
|
|
|
### `DELETE /api/contacts/:id`
|
|
|
|
Soft delete contact.
|
|
|
|
---
|
|
|
|
## Projects Module
|
|
|
|
### Sprints
|
|
|
|
#### `GET /api/projects/sprints`
|
|
|
|
**Query params:** `?domainId=<uuid>&status=active`
|
|
|
|
#### `GET /api/projects/sprints/:id`
|
|
|
|
Get sprint with associated tasks.
|
|
|
|
#### `POST /api/projects/sprints`
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"domainId": "uuid",
|
|
"name": "Sprint 3",
|
|
"startDate": "2026-02-24",
|
|
"endDate": "2026-03-07",
|
|
"goal": "Complete authentication module"
|
|
}
|
|
```
|
|
|
|
#### `PATCH /api/projects/sprints/:id`
|
|
|
|
Update sprint (including retrospective).
|
|
|
|
### Content Calendar
|
|
|
|
#### `GET /api/projects/content`
|
|
|
|
**Query params:**
|
|
- `?domainId=<uuid>`
|
|
- `?status=idea,planned`
|
|
- `?platform=onlyfans`
|
|
- `?startDate=&endDate=`
|
|
|
|
#### `POST /api/projects/content`
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"domainId": "uuid",
|
|
"title": "Lingerie photo set",
|
|
"contentType": "photo_set",
|
|
"scheduledDate": "2026-03-01",
|
|
"platform": "onlyfans",
|
|
"status": "planned",
|
|
"notes": "Black set, studio lighting"
|
|
}
|
|
```
|
|
|
|
#### `PATCH /api/projects/content/:id`
|
|
|
|
Update content calendar item.
|
|
|
|
---
|
|
|
|
## Analytics Module
|
|
|
|
### `GET /api/analytics/overview`
|
|
|
|
Cross-domain summary.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"period": "month",
|
|
"tasks": {
|
|
"completed": 45,
|
|
"created": 52,
|
|
"overdue": 3,
|
|
"byDomain": [...]
|
|
},
|
|
"habits": {
|
|
"adherenceRate": 0.82,
|
|
"activeStreaks": 5,
|
|
"longestStreak": 30,
|
|
"byDomain": [...]
|
|
},
|
|
"goals": {
|
|
"active": 12,
|
|
"completed": 3,
|
|
"averageProgress": 45
|
|
},
|
|
"income": {
|
|
"total": "3420.00",
|
|
"currency": "GBP",
|
|
"byDomain": [...]
|
|
}
|
|
}
|
|
```
|
|
|
|
### `GET /api/analytics/domain/:id`
|
|
|
|
Domain-specific analytics.
|
|
|
|
### `GET /api/analytics/trends`
|
|
|
|
Time-series data for charts.
|
|
|
|
**Query params:**
|
|
- `?metric=income|tasks_completed|habit_adherence|time_tracked`
|
|
- `?period=day|week|month`
|
|
- `?domainId=<uuid>` — Optional
|
|
- `?startDate=&endDate=`
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"metric": "income",
|
|
"period": "week",
|
|
"dataPoints": [
|
|
{ "date": "2026-02-03", "value": 820 },
|
|
{ "date": "2026-02-10", "value": 1050 },
|
|
{ "date": "2026-02-17", "value": 750 },
|
|
{ "date": "2026-02-24", "value": 800 }
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Chat Module
|
|
|
|
### `GET /api/chat/conversations`
|
|
|
|
List conversations (newest first).
|
|
|
|
**Query params:**
|
|
- `?archived=false`
|
|
- `?isVoice=false`
|
|
|
|
**Response:** `Conversation[]` with last message preview.
|
|
|
|
### `POST /api/chat/conversations`
|
|
|
|
Start new conversation.
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"title": "Planning tomorrow",
|
|
"model": "qwen3-8b"
|
|
}
|
|
```
|
|
|
|
### `GET /api/chat/conversations/:id`
|
|
|
|
Get conversation with recent messages.
|
|
|
|
### `GET /api/chat/conversations/:id/messages`
|
|
|
|
Get messages for a conversation (paginated, oldest first).
|
|
|
|
### `PATCH /api/chat/conversations/:id`
|
|
|
|
Update conversation (rename, archive).
|
|
|
|
### `DELETE /api/chat/conversations/:id`
|
|
|
|
Delete conversation and all messages.
|
|
|
|
---
|
|
|
|
## WebSocket Events
|
|
|
|
### Chat Gateway (namespace: `/chat`)
|
|
|
|
**Client → Server:**
|
|
|
|
| Event | Payload | Description |
|
|
|-------|---------|-------------|
|
|
| `conversation:join` | `{ conversationId: string }` | Join conversation room |
|
|
| `conversation:leave` | `{ conversationId: string }` | Leave conversation room |
|
|
| `send_message` | `{ conversationId: string, content: string, model?: string }` | Send user message |
|
|
|
|
**Server → Client:**
|
|
|
|
| Event | Payload | Description |
|
|
|-------|---------|-------------|
|
|
| `message_start` | `{ messageId: string, conversationId: string }` | New assistant message starting |
|
|
| `message_chunk` | `{ messageId: string, chunk: string }` | Streaming text chunk |
|
|
| `message_end` | `{ messageId: string, model: string, tokenCount: number, durationMs: number }` | Message complete |
|
|
| `message_error` | `{ messageId: string, error: string }` | Generation error |
|
|
| `typing` | `{ conversationId: string, isTyping: boolean }` | Typing indicator |
|
|
|
|
### Voice Gateway (namespace: `/voice`)
|
|
|
|
**Client → Server:**
|
|
|
|
| Event | Payload | Description |
|
|
|-------|---------|-------------|
|
|
| `voice:start` | `{ conversationId: string }` | Start voice session |
|
|
| `voice:audio` | `{ audio: string (base64), segmentId: number }` | Audio data from mic |
|
|
| `voice:interrupt` | `{}` | User speaking — interrupt TTS |
|
|
| `voice:stop` | `{}` | End voice session |
|
|
|
|
**Server → Client:**
|
|
|
|
| Event | Payload | Description |
|
|
|-------|---------|-------------|
|
|
| `voice:transcription` | `{ text: string, segmentId: number, confidence: number }` | STT result |
|
|
| `voice:assistant_text` | `{ text: string, messageId: string }` | Assistant response text |
|
|
| `voice:audio_chunk` | `Buffer` (binary) | TTS audio chunk (WAV) |
|
|
| `voice:audio_end` | `{ messageId: string }` | TTS playback complete |
|
|
| `voice:error` | `{ error: string }` | Voice processing error |
|
|
| `voice:status` | `{ state: 'listening' \| 'processing' \| 'speaking' \| 'idle' }` | Voice state change |
|