TidyQuest is a self-hosted web application that gamifies household chores using RPG mechanics. Complete tasks, earn coins, unlock achievements, and compete with your family on the leaderboard. Features: - 🎯 Health-based task tracking with visual decay over time - 💰 Coin system with customizable rewards - 🔥 Daily/weekly streak tracking - 🏆 Family leaderboard (week/month/quarter/year) - 🎖️ Automatic achievement unlocking - 📅 Calendar view for upcoming tasks - 🌍 Multilingual support (EN/FR/DE/ES) - 📱 Optional Telegram notifications - 🏖️ Vacation mode (pause task decay) - 🐳 Docker deployment (single container) Tech Stack: - Frontend: React 19 + Vite + TypeScript - Backend: Node.js + Express + TypeScript - Database: SQLite3 with WAL mode - Auth: JWT + bcrypt - Deployment: Docker Compose Includes: - 8 room types with 60+ predefined tasks - 10 preset rewards - 12 achievement types - Complete API documentation - Security best practices guide - Comprehensive README with examples License: GNU Affero General Public License v3.0 (AGPL-3.0) This ensures TidyQuest remains free and open-source forever, preventing proprietary SaaS forks. Any modified hosted version must share source code.
1053 lines
16 KiB
Markdown
1053 lines
16 KiB
Markdown
# 🌐 TidyQuest API Documentation
|
|
|
|
Complete reference for all TidyQuest REST API endpoints.
|
|
|
|
**Base URL**: `http://localhost:3020/api`
|
|
|
|
**Authentication**: All endpoints (except `/auth/*`) require a JWT token in the `Authorization` header:
|
|
|
|
```http
|
|
Authorization: Bearer <your-jwt-token>
|
|
```
|
|
|
|
---
|
|
|
|
## 📑 Table of Contents
|
|
|
|
- [Authentication](#authentication)
|
|
- [Rooms](#rooms)
|
|
- [Tasks](#tasks)
|
|
- [Dashboard](#dashboard)
|
|
- [Leaderboard](#leaderboard)
|
|
- [History](#history)
|
|
- [Users](#users)
|
|
- [Rewards](#rewards)
|
|
- [Achievements](#achievements)
|
|
- [Data Export/Import](#data-exportimport)
|
|
- [Error Responses](#error-responses)
|
|
|
|
---
|
|
|
|
## 🔐 Authentication
|
|
|
|
### Register
|
|
|
|
Create a new user account.
|
|
|
|
**Endpoint**: `POST /api/auth/register`
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"username": "john",
|
|
"password": "securepass123",
|
|
"displayName": "John Doe",
|
|
"avatarColor": "#F97316",
|
|
"language": "en"
|
|
}
|
|
```
|
|
|
|
**Response** (201):
|
|
```json
|
|
{
|
|
"user": {
|
|
"id": 1,
|
|
"username": "john",
|
|
"displayName": "John Doe",
|
|
"role": "admin",
|
|
"avatarColor": "#F97316",
|
|
"avatarType": "letter",
|
|
"coins": 0,
|
|
"currentStreak": 0,
|
|
"language": "en",
|
|
"createdAt": "2026-02-17T10:30:00.000Z"
|
|
},
|
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
}
|
|
```
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X POST http://localhost:3020/api/auth/register \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"username": "john",
|
|
"password": "securepass123",
|
|
"displayName": "John Doe",
|
|
"avatarColor": "#F97316",
|
|
"language": "en"
|
|
}'
|
|
```
|
|
|
|
---
|
|
|
|
### Login
|
|
|
|
Authenticate and receive a JWT token.
|
|
|
|
**Endpoint**: `POST /api/auth/login`
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"username": "john",
|
|
"password": "securepass123"
|
|
}
|
|
```
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"user": { ... },
|
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
}
|
|
```
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X POST http://localhost:3020/api/auth/login \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"username": "john", "password": "securepass123"}'
|
|
```
|
|
|
|
---
|
|
|
|
### Get Current User
|
|
|
|
Get authenticated user's profile.
|
|
|
|
**Endpoint**: `GET /api/auth/me`
|
|
|
|
**Headers**: `Authorization: Bearer <token>`
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"username": "john",
|
|
"displayName": "John Doe",
|
|
"role": "admin",
|
|
"avatarColor": "#F97316",
|
|
"avatarType": "letter",
|
|
"avatarPreset": null,
|
|
"avatarPhotoUrl": null,
|
|
"coins": 150,
|
|
"currentStreak": 7,
|
|
"goalCoins": 500,
|
|
"goalStartAt": "2026-02-01T00:00:00.000Z",
|
|
"goalEndAt": "2026-02-28T23:59:59.000Z",
|
|
"isVacationMode": false,
|
|
"language": "en",
|
|
"createdAt": "2026-01-15T08:00:00.000Z"
|
|
}
|
|
```
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X GET http://localhost:3020/api/auth/me \
|
|
-H "Authorization: Bearer <your-token>"
|
|
```
|
|
|
|
---
|
|
|
|
## 🏠 Rooms
|
|
|
|
### List All Rooms
|
|
|
|
**Endpoint**: `GET /api/rooms`
|
|
|
|
**Response** (200):
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"name": "Kitchen",
|
|
"roomType": "kitchen",
|
|
"color": "#FFE4C4",
|
|
"accentColor": "#FFA500",
|
|
"photoUrl": null,
|
|
"sortOrder": 1,
|
|
"health": 75,
|
|
"createdAt": "2026-02-10T12:00:00.000Z"
|
|
}
|
|
]
|
|
```
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X GET http://localhost:3020/api/rooms \
|
|
-H "Authorization: Bearer <your-token>"
|
|
```
|
|
|
|
---
|
|
|
|
### Get Room by ID
|
|
|
|
**Endpoint**: `GET /api/rooms/:id`
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"name": "Kitchen",
|
|
"roomType": "kitchen",
|
|
"color": "#FFE4C4",
|
|
"accentColor": "#FFA500",
|
|
"photoUrl": null,
|
|
"sortOrder": 1,
|
|
"createdAt": "2026-02-10T12:00:00.000Z"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Create Room
|
|
|
|
**Endpoint**: `POST /api/rooms` (Admin only)
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"name": "Master Bedroom",
|
|
"roomType": "bedroom",
|
|
"color": "#E6F3FF",
|
|
"accentColor": "#4A90E2"
|
|
}
|
|
```
|
|
|
|
**Response** (201):
|
|
```json
|
|
{
|
|
"id": 5,
|
|
"name": "Master Bedroom",
|
|
"roomType": "bedroom",
|
|
...
|
|
}
|
|
```
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X POST http://localhost:3020/api/rooms \
|
|
-H "Authorization: Bearer <admin-token>" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"name": "Master Bedroom",
|
|
"roomType": "bedroom",
|
|
"color": "#E6F3FF",
|
|
"accentColor": "#4A90E2"
|
|
}'
|
|
```
|
|
|
|
---
|
|
|
|
### Update Room
|
|
|
|
**Endpoint**: `PUT /api/rooms/:id`
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"name": "Main Kitchen",
|
|
"sortOrder": 2
|
|
}
|
|
```
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"name": "Main Kitchen",
|
|
"sortOrder": 2,
|
|
...
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Delete Room
|
|
|
|
**Endpoint**: `DELETE /api/rooms/:id` (Admin only)
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"success": true
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Get Default Tasks for Room Type
|
|
|
|
**Endpoint**: `GET /api/rooms/defaults/:roomType`
|
|
|
|
Available room types: `kitchen`, `bedroom`, `bathroom`, `living`, `office`, `garage`, `laundry`, `garden`
|
|
|
|
**Response** (200):
|
|
```json
|
|
[
|
|
{
|
|
"name": "Wash Dishes",
|
|
"frequencyDays": 1,
|
|
"effort": 2,
|
|
"translationKey": "kitchen.wash_dishes",
|
|
"iconKey": "wash_dishes"
|
|
},
|
|
{
|
|
"name": "Clean Fridge",
|
|
"frequencyDays": 14,
|
|
"effort": 3,
|
|
"translationKey": "kitchen.clean_fridge",
|
|
"iconKey": "fridge"
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ Tasks
|
|
|
|
### List Tasks for a Room
|
|
|
|
**Endpoint**: `GET /api/rooms/:roomId/tasks`
|
|
|
|
**Response** (200):
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"roomId": 1,
|
|
"name": "Wash Dishes",
|
|
"notes": "Use dishwasher for big loads",
|
|
"frequencyDays": 1,
|
|
"effort": 2,
|
|
"health": 85,
|
|
"lastCompletedAt": "2026-02-17T08:00:00.000Z",
|
|
"translationKey": "kitchen.wash_dishes",
|
|
"iconKey": "wash_dishes",
|
|
"createdAt": "2026-02-10T12:00:00.000Z"
|
|
}
|
|
]
|
|
```
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X GET http://localhost:3020/api/rooms/1/tasks \
|
|
-H "Authorization: Bearer <your-token>"
|
|
```
|
|
|
|
---
|
|
|
|
### Create Task
|
|
|
|
**Endpoint**: `POST /api/rooms/:roomId/tasks` (Admin only)
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"name": "Deep Clean Oven",
|
|
"notes": "Use oven cleaner spray",
|
|
"frequencyDays": 30,
|
|
"effort": 5,
|
|
"translationKey": "kitchen.deep_clean_oven",
|
|
"iconKey": "oven"
|
|
}
|
|
```
|
|
|
|
**Response** (201):
|
|
```json
|
|
{
|
|
"id": 15,
|
|
"roomId": 1,
|
|
"name": "Deep Clean Oven",
|
|
...
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Update Task
|
|
|
|
**Endpoint**: `PUT /api/tasks/:id` (Admin only)
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"frequencyDays": 7,
|
|
"effort": 3
|
|
}
|
|
```
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"id": 15,
|
|
"frequencyDays": 7,
|
|
"effort": 3,
|
|
...
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Delete Task
|
|
|
|
**Endpoint**: `DELETE /api/tasks/:id` (Admin only)
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"success": true
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Complete Task
|
|
|
|
Mark a task as completed, earn coins, update streak.
|
|
|
|
**Endpoint**: `POST /api/tasks/:id/complete`
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"completion": {
|
|
"id": 123,
|
|
"taskId": 1,
|
|
"userId": 1,
|
|
"completedAt": "2026-02-17T14:30:00.000Z",
|
|
"coinsEarned": 10
|
|
},
|
|
"user": {
|
|
"id": 1,
|
|
"coins": 160,
|
|
"currentStreak": 8,
|
|
"lastActiveDate": "2026-02-17",
|
|
...
|
|
},
|
|
"newAchievements": [
|
|
{
|
|
"id": "streak_7",
|
|
"name": "On Fire",
|
|
"description": "Complete tasks for 7 days in a row",
|
|
"icon": "🔥"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X POST http://localhost:3020/api/tasks/1/complete \
|
|
-H "Authorization: Bearer <your-token>"
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Dashboard
|
|
|
|
Get aggregated view: house health, today's quests, upcoming tasks, recent activity, goals, pending rewards.
|
|
|
|
**Endpoint**: `GET /api/dashboard`
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"houseHealth": 78,
|
|
"todayQuests": {
|
|
"completed": 5,
|
|
"total": 12
|
|
},
|
|
"nextTasks": [
|
|
{
|
|
"id": 3,
|
|
"name": "Vacuum Living Room",
|
|
"roomName": "Living Room",
|
|
"health": 45,
|
|
"effort": 3,
|
|
"dueIn": "2 hours"
|
|
}
|
|
],
|
|
"recentActivity": [
|
|
{
|
|
"id": 120,
|
|
"taskName": "Wash Dishes",
|
|
"userName": "John Doe",
|
|
"completedAt": "2026-02-17T14:00:00.000Z",
|
|
"coinsEarned": 10
|
|
}
|
|
],
|
|
"myGoals": [
|
|
{
|
|
"id": 1,
|
|
"title": "February Goal",
|
|
"goalCoins": 500,
|
|
"currentCoins": 160,
|
|
"progress": 32,
|
|
"startAt": "2026-02-01T00:00:00.000Z",
|
|
"endAt": "2026-02-28T23:59:59.000Z"
|
|
}
|
|
],
|
|
"pendingRewardRequests": 2
|
|
}
|
|
```
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X GET http://localhost:3020/api/dashboard \
|
|
-H "Authorization: Bearer <your-token>"
|
|
```
|
|
|
|
---
|
|
|
|
## 🏆 Leaderboard
|
|
|
|
Get family rankings by period.
|
|
|
|
**Endpoint**: `GET /api/leaderboard?period=<period>`
|
|
|
|
**Query Params**:
|
|
- `period`: `week`, `month`, `quarter`, or `year`
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"period": "week",
|
|
"leaderboard": [
|
|
{
|
|
"userId": 1,
|
|
"displayName": "John Doe",
|
|
"avatarColor": "#F97316",
|
|
"avatarType": "letter",
|
|
"avatarPreset": null,
|
|
"avatarPhotoUrl": null,
|
|
"coinsEarned": 150,
|
|
"tasksCompleted": 25,
|
|
"rank": 1
|
|
},
|
|
{
|
|
"userId": 2,
|
|
"displayName": "Jane Doe",
|
|
"coinsEarned": 120,
|
|
"tasksCompleted": 20,
|
|
"rank": 2
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X GET "http://localhost:3020/api/leaderboard?period=week" \
|
|
-H "Authorization: Bearer <your-token>"
|
|
```
|
|
|
|
---
|
|
|
|
## 📜 History
|
|
|
|
Get paginated activity log of all task completions.
|
|
|
|
**Endpoint**: `GET /api/history?limit=<limit>&offset=<offset>`
|
|
|
|
**Query Params**:
|
|
- `limit`: Number of records (default: 20)
|
|
- `offset`: Skip N records (default: 0)
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"history": [
|
|
{
|
|
"id": 120,
|
|
"taskName": "Wash Dishes",
|
|
"roomName": "Kitchen",
|
|
"userName": "John Doe",
|
|
"completedAt": "2026-02-17T14:00:00.000Z",
|
|
"coinsEarned": 10
|
|
}
|
|
],
|
|
"total": 500,
|
|
"limit": 20,
|
|
"offset": 0
|
|
}
|
|
```
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X GET "http://localhost:3020/api/history?limit=50&offset=100" \
|
|
-H "Authorization: Bearer <your-token>"
|
|
```
|
|
|
|
---
|
|
|
|
## 👥 Users
|
|
|
|
### List All Users
|
|
|
|
**Endpoint**: `GET /api/users` (Admin only)
|
|
|
|
**Response** (200):
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"username": "john",
|
|
"displayName": "John Doe",
|
|
"role": "admin",
|
|
"coins": 160,
|
|
"currentStreak": 8,
|
|
...
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### Create User
|
|
|
|
**Endpoint**: `POST /api/users` (Admin only)
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"username": "alice",
|
|
"password": "alicepass",
|
|
"displayName": "Alice",
|
|
"role": "child",
|
|
"avatarColor": "#FFB6C1",
|
|
"language": "en"
|
|
}
|
|
```
|
|
|
|
**Response** (201):
|
|
```json
|
|
{
|
|
"id": 3,
|
|
"username": "alice",
|
|
"displayName": "Alice",
|
|
"role": "child",
|
|
...
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Update User Profile
|
|
|
|
**Endpoint**: `PUT /api/users/:id/profile`
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"displayName": "John Smith",
|
|
"avatarType": "character",
|
|
"avatarPreset": "cat",
|
|
"language": "fr"
|
|
}
|
|
```
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"displayName": "John Smith",
|
|
"avatarType": "character",
|
|
"avatarPreset": "cat",
|
|
...
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Upload Avatar Photo
|
|
|
|
**Endpoint**: `POST /api/users/:id/avatar-upload`
|
|
|
|
**Headers**:
|
|
- `Content-Type: multipart/form-data`
|
|
|
|
**Request Body**:
|
|
```
|
|
FormData:
|
|
- avatar: <file> (image/*, max 2MB)
|
|
```
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"avatarType": "photo",
|
|
"avatarPhotoUrl": "/api/avatars/user-1-1708176000000.jpg",
|
|
...
|
|
}
|
|
```
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X POST http://localhost:3020/api/users/1/avatar-upload \
|
|
-H "Authorization: Bearer <your-token>" \
|
|
-F "avatar=@/path/to/photo.jpg"
|
|
```
|
|
|
|
---
|
|
|
|
### Update User Settings
|
|
|
|
**Endpoint**: `PUT /api/users/:id/settings` (Admin only, self only)
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"isVacationMode": true,
|
|
"language": "es"
|
|
}
|
|
```
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"isVacationMode": true,
|
|
"vacationStartDate": "2026-02-17T15:00:00.000Z",
|
|
"language": "es",
|
|
...
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Get Coins Configuration
|
|
|
|
**Endpoint**: `GET /api/users/coins-config`
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"coinsByEffort": {
|
|
"1": 5,
|
|
"2": 10,
|
|
"3": 15,
|
|
"4": 20,
|
|
"5": 25
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Update Coins Configuration
|
|
|
|
**Endpoint**: `PUT /api/users/coins-config` (Admin only)
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"coinsByEffort": {
|
|
"1": 10,
|
|
"2": 20,
|
|
"3": 30,
|
|
"4": 40,
|
|
"5": 50
|
|
}
|
|
}
|
|
```
|
|
|
|
Or reset to defaults:
|
|
```json
|
|
{
|
|
"useDefault": true
|
|
}
|
|
```
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"coinsByEffort": { ... }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🎁 Rewards
|
|
|
|
### List Rewards
|
|
|
|
**Endpoint**: `GET /api/rewards`
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"rewards": [
|
|
{
|
|
"id": 1,
|
|
"title": "Movie Night Pick",
|
|
"description": "Choose the family movie",
|
|
"costCoins": 40,
|
|
"isActive": true,
|
|
"isPreset": true,
|
|
"createdBy": null,
|
|
"createdAt": "2026-02-10T00:00:00.000Z"
|
|
}
|
|
],
|
|
"myRedemptions": [
|
|
{
|
|
"id": 10,
|
|
"rewardId": 1,
|
|
"rewardTitle": "Movie Night Pick",
|
|
"costCoins": 40,
|
|
"status": "requested",
|
|
"redeemedAt": "2026-02-17T18:00:00.000Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Create Reward
|
|
|
|
**Endpoint**: `POST /api/rewards` (Admin only)
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"title": "Extra Dessert",
|
|
"description": "Second serving at dinner",
|
|
"costCoins": 25
|
|
}
|
|
```
|
|
|
|
**Response** (201):
|
|
```json
|
|
{
|
|
"id": 8,
|
|
"title": "Extra Dessert",
|
|
"costCoins": 25,
|
|
"isActive": true,
|
|
"createdBy": 1,
|
|
...
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Redeem Reward
|
|
|
|
**Endpoint**: `POST /api/rewards/:id/redeem`
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"redemption": {
|
|
"id": 12,
|
|
"rewardId": 1,
|
|
"userId": 2,
|
|
"costCoins": 40,
|
|
"status": "requested",
|
|
"redeemedAt": "2026-02-17T19:00:00.000Z"
|
|
},
|
|
"newCoinsBalance": 110
|
|
}
|
|
```
|
|
|
|
**Errors**:
|
|
- `400`: Insufficient coins
|
|
- `404`: Reward not found or inactive
|
|
|
|
---
|
|
|
|
### Approve/Reject Redemption
|
|
|
|
**Endpoint**: `PUT /api/rewards/redemptions/:id` (Admin only)
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"status": "approved"
|
|
}
|
|
```
|
|
|
|
Status options: `approved`, `rejected`
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"id": 12,
|
|
"status": "approved",
|
|
...
|
|
}
|
|
```
|
|
|
|
**Note**: If rejected, coins are refunded to user.
|
|
|
|
---
|
|
|
|
### Cancel Redemption
|
|
|
|
**Endpoint**: `POST /api/rewards/redemptions/:id/cancel` (Self only)
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"id": 12,
|
|
"status": "cancelled",
|
|
...
|
|
}
|
|
```
|
|
|
|
**Note**: Coins are refunded.
|
|
|
|
---
|
|
|
|
## 🎖️ Achievements
|
|
|
|
### Get Achievements
|
|
|
|
**Endpoint**: `GET /api/achievements`
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"myAchievements": [
|
|
{
|
|
"id": "tasks_10",
|
|
"name": "Cleaner",
|
|
"description": "Complete 10 tasks",
|
|
"icon": "✨",
|
|
"unlockedAt": "2026-02-15T10:00:00.000Z"
|
|
}
|
|
],
|
|
"allAchievements": [
|
|
{
|
|
"id": "tasks_1",
|
|
"name": "Starter",
|
|
"description": "Complete your first task",
|
|
"icon": "🌟",
|
|
"unlocked": true
|
|
},
|
|
{
|
|
"id": "streak_30",
|
|
"name": "Legend",
|
|
"description": "Maintain a 30-day streak",
|
|
"icon": "👑",
|
|
"unlocked": false
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Note**: `allAchievements` only returned for admins.
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X GET http://localhost:3020/api/achievements \
|
|
-H "Authorization: Bearer <your-token>"
|
|
```
|
|
|
|
---
|
|
|
|
## 📤 Data Export/Import
|
|
|
|
### Export Data
|
|
|
|
**Endpoint**: `GET /api/data/export` (Admin only)
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"version": "1.0",
|
|
"exportedAt": "2026-02-17T20:00:00.000Z",
|
|
"users": [...],
|
|
"rooms": [...],
|
|
"tasks": [...],
|
|
"taskCompletions": [...],
|
|
"rewards": [...],
|
|
"rewardRedemptions": [...],
|
|
"appSettings": [...]
|
|
}
|
|
```
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X GET http://localhost:3020/api/data/export \
|
|
-H "Authorization: Bearer <admin-token>" \
|
|
-o backup.json
|
|
```
|
|
|
|
---
|
|
|
|
### Import Data
|
|
|
|
**Endpoint**: `POST /api/data/import` (Admin only)
|
|
|
|
**Request Body**: Full JSON export (same structure as export)
|
|
|
|
**Response** (200):
|
|
```json
|
|
{
|
|
"success": true,
|
|
"imported": {
|
|
"users": 3,
|
|
"rooms": 5,
|
|
"tasks": 42,
|
|
"taskCompletions": 250,
|
|
"rewards": 10,
|
|
"rewardRedemptions": 15
|
|
}
|
|
}
|
|
```
|
|
|
|
**Warning**: This will CLEAR and REPLACE all data.
|
|
|
|
**cURL Example**:
|
|
```bash
|
|
curl -X POST http://localhost:3020/api/data/import \
|
|
-H "Authorization: Bearer <admin-token>" \
|
|
-H "Content-Type: application/json" \
|
|
-d @backup.json
|
|
```
|
|
|
|
---
|
|
|
|
## ❌ Error Responses
|
|
|
|
All errors follow this format:
|
|
|
|
```json
|
|
{
|
|
"error": "Descriptive error message"
|
|
}
|
|
```
|
|
|
|
**Common HTTP Status Codes**:
|
|
|
|
| Code | Meaning | Example |
|
|
|------|---------|---------|
|
|
| 400 | Bad Request | Missing required field |
|
|
| 401 | Unauthorized | Invalid or missing JWT token |
|
|
| 403 | Forbidden | Not admin or not owner |
|
|
| 404 | Not Found | Resource doesn't exist |
|
|
| 409 | Conflict | Username already exists |
|
|
| 500 | Server Error | Database or internal error |
|
|
|
|
---
|
|
|
|
## 🔔 Rate Limiting
|
|
|
|
Currently **no rate limiting** is implemented. For production deployments, consider using a reverse proxy (Nginx, Caddy) with rate limiting configured.
|
|
|
|
---
|
|
|
|
## 📝 Notes
|
|
|
|
- All timestamps are in ISO 8601 format (UTC)
|
|
- JWT tokens expire after **30 days**
|
|
- Pagination uses `limit` and `offset` query parameters
|
|
- File uploads (avatars) limited to **2MB**
|
|
- Accepted image types: `image/*` (MIME type check)
|
|
|
|
---
|
|
|
|
**Need help?** Open an issue on [GitHub Issues](https://github.com/mellow-fox/TidyQuest/issues)
|