Emotion App - API Documentation
Base URL: http://localhost:3000/api
Version: 1.0.0
Authentication: Bearer Token (JWT)
Table of Contents
Authentication
All endpoints except /auth/register and /auth/login require Bearer token authentication.
Header Format:
Authorization: Bearer <your-jwt-token>POST /auth/register
Register a new user account.
Endpoint: /auth/register
Method: POST
Auth Required: No
Request Body
{ "email": "user@example.com", "password": "Password123", "confirmPassword": "Password123", "name": "John Doe"}Validation Rules
| Field | Type | Required | Validation |
|---|---|---|---|
| string | ✅ Yes | Valid email format, max 255 chars | |
| password | string | ✅ Yes | Min 8 chars, max 128 chars, must contain: uppercase, lowercase, number |
| confirmPassword | string | ✅ Yes | Must match password |
| name | string | ❌ No | Min 1 char, max 100 chars |
Success Response (201 Created)
{ "status": "success", "message": "User registered successfully", "data": { "userId": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "name": "John Doe", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }}Error Response (400 Bad Request)
{ "status": "failed", "message": "Validation failed", "errors": [ { "field": "email", "message": "Email already exists", "value": "user@example.com" } ]}POST /auth/login
Authenticate user and get JWT token.
Endpoint: /auth/login
Method: POST
Auth Required: No
Request Body
{ "email": "user@example.com", "password": "Password123"}Validation Rules
| Field | Type | Required | Validation |
|---|---|---|---|
| string | ✅ Yes | Valid email format | |
| password | string | ✅ Yes | Required |
Success Response (200 OK)
{ "status": "success", "data": { "userId": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "name": "John Doe", "role": "USER", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }}Error Response (401 Unauthorized)
{ "status": "failed", "message": "Invalid email or password"}GET /auth/profile
Get authenticated user’s profile.
Endpoint: /auth/profile
Method: GET
Auth Required: ✅ Yes
Success Response (200 OK)
{ "status": "success", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "role": "USER", "createdAt": "2024-12-01T10:00:00.000Z", "profile": { "name": "John Doe", "reminderSettings": { "enabled": true, "time": "09:00", "frequency": "daily" }, "pushNotificationToken": "ExponentPushToken[...]" } }}PUT /auth/update-profile
Update user profile information.
Endpoint: /auth/update-profile
Method: PUT
Auth Required: ✅ Yes
Request Body
{ "email": "newemail@example.com", "name": "John Smith", "reminderSettings": { "enabled": true, "time": "09:00", "frequency": "daily", "days": ["monday", "wednesday", "friday"] }, "pushNotificationToken": "ExponentPushToken[...]"}Validation Rules
| Field | Type | Required | Validation |
|---|---|---|---|
| string | ❌ No | Valid email, max 255 chars | |
| role | string | ❌ No | Enum: USER, ADMIN |
| name | string | ❌ No | Min 1 char, max 100 chars |
| reminderSettings | object | ❌ No | Valid JSON with allowed keys |
| reminderSettings.time | string | ❌ No | Format: HH:MM (24-hour) |
| reminderSettings.frequency | string | ❌ No | Enum: daily, weekly, monthly |
| reminderSettings.days | array | ❌ No | Day names (lowercase) |
| pushNotificationToken | string | ❌ No | Min 1 char, max 255 chars |
Note: At least one field must be provided.
Success Response (200 OK)
{ "status": "success", "message": "Profile updated successfully", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "newemail@example.com", "profile": { "name": "John Smith", "reminderSettings": { "enabled": true, "time": "09:00", "frequency": "daily", "days": ["monday", "wednesday", "friday"] } } }}Moods
Manage moods (global defaults + custom user moods).
Mood Rank System: 1-7
- Very Sad 😢
- Sad 😔
- Somewhat Sad 😟
- Neutral 😐 (default)
- Somewhat Happy 🙂
- Happy 😊
- Very Happy 😄
GET /moods
Get all available moods (global + user’s custom moods).
Endpoint: /moods
Method: GET
Auth Required: ✅ Yes
Success Response (200 OK)
{ "status": "success", "data": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "userId": null, "name": "Very Happy", "rank": 7, "icon": "😄", "createdAt": "2024-12-01T10:00:00.000Z" }, { "id": "550e8400-e29b-41d4-a716-446655440001", "userId": "user-uuid", "name": "Excited", "rank": 6, "icon": "🤩", "createdAt": "2024-12-10T15:30:00.000Z" } ], "count": 2}Note: userId: null = global mood, userId: <uuid> = custom mood
POST /moods
Create a custom mood.
Endpoint: /moods
Method: POST
Auth Required: ✅ Yes
Request Body
{ "name": "Excited", "rank": 6, "icon": "🤩"}Validation Rules
| Field | Type | Required | Validation |
|---|---|---|---|
| name | string | ✅ Yes | Min 1 char, max 50 chars, unique per user |
| rank | integer | ❌ No | 1-7, defaults to 4 |
| icon | string | ❌ No | Max 100 chars (emoji) |
Success Response (201 Created)
{ "status": "success", "message": "Custom mood created successfully", "data": { "id": "550e8400-e29b-41d4-a716-446655440002", "userId": "user-uuid", "name": "Excited", "rank": 6, "icon": "🤩", "createdAt": "2024-12-19T10:00:00.000Z" }}PUT /moods/:id
Update a custom mood (cannot update global moods).
Endpoint: /moods/:id
Method: PUT
Auth Required: ✅ Yes
Request Body
{ "name": "Super Excited", "rank": 7, "icon": "🎉"}Validation Rules
Same as POST, all fields optional.
Success Response (200 OK)
{ "status": "success", "message": "Mood updated successfully", "data": { "id": "550e8400-e29b-41d4-a716-446655440002", "name": "Super Excited", "rank": 7, "icon": "🎉" }}Error Response (404 Not Found)
{ "status": "failed", "message": "Custom mood not found or you don't have permission"}DELETE /moods/:id
Delete a custom mood (cannot delete if used in entries).
Endpoint: /moods/:id
Method: DELETE
Auth Required: ✅ Yes
Success Response (200 OK)
{ "status": "success", "message": "Mood deleted successfully"}Error Response (400 Bad Request)
{ "status": "failed", "message": "Cannot delete mood that is used in entries"}Activities
Manage activities (global defaults + custom user activities).
70 Default Activities across 8 categories:
- Work & Career
- Health & Fitness
- Social & Relationships
- Entertainment & Hobbies
- Personal Care
- Relaxation & Leisure
- Food & Nutrition
- Learning & Development
GET /activities
Get all activities with optional filtering.
Endpoint: /activities
Method: GET
Auth Required: ✅ Yes
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| archived | string | ❌ No | Filter by archived status: “true” or “false” |
| activityGroup | string | ❌ No | Filter by group name (e.g., “health”) |
Success Response (200 OK)
{ "status": "success", "data": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "userId": null, "name": "Exercise", "icon": "🏃", "activityGroup": "health", "archived": false, "createdAt": "2024-12-01T10:00:00.000Z" }, { "id": "550e8400-e29b-41d4-a716-446655440001", "userId": "user-uuid", "name": "Morning Run", "icon": "🌅", "activityGroup": "health", "archived": false, "createdAt": "2024-12-15T08:00:00.000Z" } ], "count": 2}POST /activities
Create a custom activity.
Endpoint: /activities
Method: POST
Auth Required: ✅ Yes
Request Body
{ "name": "Morning Run", "icon": "🌅", "activityGroup": "health"}Validation Rules
| Field | Type | Required | Validation |
|---|---|---|---|
| name | string | ✅ Yes | Min 1 char, max 100 chars |
| icon | string | ❌ No | Max 100 chars (emoji) |
| activityGroup | string | ❌ No | Max 100 chars |
Success Response (201 Created)
{ "status": "success", "message": "Custom activity created successfully", "data": { "id": "550e8400-e29b-41d4-a716-446655440001", "userId": "user-uuid", "name": "Morning Run", "icon": "🌅", "activityGroup": "health", "archived": false }}PUT /activities/:id
Update a custom activity.
Endpoint: /activities/:id
Method: PUT
Auth Required: ✅ Yes
Request Body
{ "name": "Evening Run", "icon": "🌆"}Validation Rules
Same as POST, all fields optional.
Success Response (200 OK)
{ "status": "success", "message": "Activity updated successfully", "data": { "id": "550e8400-e29b-41d4-a716-446655440001", "name": "Evening Run", "icon": "🌆" }}DELETE /activities/:id
Archive a custom activity (soft delete).
Endpoint: /activities/:id
Method: DELETE
Auth Required: ✅ Yes
Success Response (200 OK)
{ "status": "success", "message": "Activity archived successfully"}Error Response (400 Bad Request)
{ "status": "failed", "message": "Cannot delete activity that is used in entries"}Entries
Manage daily journal entries with mood tracking and activity logging.
GET /entries
Get user’s entries with advanced filtering and pagination.
Endpoint: /entries
Method: GET
Auth Required: ✅ Yes
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| month | integer | ❌ No | Filter by month (1-12), requires year |
| year | integer | ❌ No | Filter by year (e.g., 2024), requires month |
| dateFrom | string | ❌ No | Start date (YYYY-MM-DD) |
| dateTo | string | ❌ No | End date (YYYY-MM-DD) |
| moodRank | integer | ❌ No | Filter by mood rank (1-7) |
| limit | integer | ❌ No | Results per page (default: 50) |
| offset | integer | ❌ No | Skip N entries (default: 0) |
| includeActivities | string | ❌ No | Populate activity details: “true” or “false” |
Example Queries
GET /entries?month=12&year=2024GET /entries?dateFrom=2024-12-01&dateTo=2024-12-31GET /entries?moodRank=7&limit=10GET /entries?includeActivities=trueSuccess Response (200 OK)
Without includeActivities:
{ "status": "success", "data": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "userId": "user-uuid", "moodId": "mood-uuid", "entryDate": "2024-12-19T10:00:00.000Z", "note": "Had a great day at work!", "activityIds": [ "activity-uuid-1", "activity-uuid-2" ], "media": [ { "type": "image", "url": "https://example.com/photo.jpg" } ], "mood": { "id": "mood-uuid", "name": "Very Happy", "rank": 7, "icon": "😄" }, "createdAt": "2024-12-19T10:00:00.000Z", "updatedAt": "2024-12-19T10:00:00.000Z" } ], "count": 1}With includeActivities=true:
{ "status": "success", "data": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "activityIds": ["activity-uuid-1", "activity-uuid-2"], "activities": [ { "id": "activity-uuid-1", "name": "Work", "icon": "💼", "activityGroup": "work" }, { "id": "activity-uuid-2", "name": "Exercise", "icon": "🏃", "activityGroup": "health" } ], "mood": { "id": "mood-uuid", "name": "Very Happy", "rank": 7, "icon": "😄" }, "note": "Had a great day!", "entryDate": "2024-12-19T10:00:00.000Z" } ], "count": 1}GET /entries/:id
Get a single entry by ID.
Endpoint: /entries/:id
Method: GET
Auth Required: ✅ Yes
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| includeActivities | string | Populate activity details: “true” or “false” |
Success Response (200 OK)
Same structure as GET /entries
Error Response (404 Not Found)
{ "status": "failed", "message": "Entry not found"}POST /entries
Create a new journal entry.
Endpoint: /entries
Method: POST
Auth Required: ✅ Yes
Request Body
{ "moodId": "550e8400-e29b-41d4-a716-446655440000", "entryDate": "2024-12-19T10:00:00Z", "note": "Had a great day at work!", "activityIds": [ "550e8400-e29b-41d4-a716-446655440001", "550e8400-e29b-41d4-a716-446655440002" ], "media": [ { "type": "image", "url": "https://example.com/photo.jpg" } ]}Validation Rules
| Field | Type | Required | Validation |
|---|---|---|---|
| moodId | string (UUID) | ✅ Yes | Must be valid mood UUID |
| entryDate | string | ✅ Yes | ISO 8601 date format |
| note | string | ❌ No | Max 1000 chars |
| activityIds | array | ❌ No | Array of valid activity UUIDs |
| media | array | ❌ No | Array of objects with type and url |
Activity IDs Validation:
- Each element must be a valid UUID format
- Each UUID must exist in database
- Cannot use archived activities
Media Validation:
- Each item must be an object
- Required properties:
type(string),url(string)
Success Response (201 Created)
{ "status": "success", "message": "Entry created successfully", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "userId": "user-uuid", "moodId": "mood-uuid", "entryDate": "2024-12-19T10:00:00.000Z", "note": "Had a great day at work!", "activityIds": ["activity-uuid-1", "activity-uuid-2"], "media": [ { "type": "image", "url": "https://example.com/photo.jpg" } ], "createdAt": "2024-12-19T10:00:00.000Z" }}Error Response (400 Bad Request)
Invalid Activity IDs:
{ "status": "failed", "message": "Validation failed", "errors": [ { "field": "activityIds", "message": "All activity IDs must be valid UUIDs. Invalid: not-a-uuid, 123", "value": ["not-a-uuid", "123"] } ]}Media Structure Error:
{ "status": "failed", "message": "Validation failed", "errors": [ { "field": "media", "message": "Each media item must be an object with \"type\" and \"url\" properties" } ]}PUT /entries/:id
Update an existing entry.
Endpoint: /entries/:id
Method: PUT
Auth Required: ✅ Yes
Request Body
All fields are optional (partial update):
{ "moodId": "mood-uuid-new", "note": "Updated note text", "activityIds": ["activity-uuid-3"], "media": []}Validation Rules
Same as POST, all fields optional.
Success Response (200 OK)
{ "status": "success", "message": "Entry updated successfully", "data": { "id": "entry-uuid", "moodId": "mood-uuid-new", "note": "Updated note text", "activityIds": ["activity-uuid-3"], "updatedAt": "2024-12-19T11:00:00.000Z" }}DELETE /entries/:id
Delete an entry (hard delete).
Endpoint: /entries/:id
Method: DELETE
Auth Required: ✅ Yes
Success Response (200 OK)
{ "status": "success", "message": "Entry deleted successfully"}Error Response (404 Not Found)
{ "status": "failed", "message": "Entry not found"}Response Format
All API responses follow a consistent structure:
Success Response
{ "status": "success", "message": "Optional success message", "data": {}, // or [] "count": 10 // For array responses}Error Response
{ "status": "failed", "message": "Error description", "errors": [] // Optional, for validation errors}Error Handling
HTTP Status Codes
| Code | Meaning | When Used |
|---|---|---|
| 200 | OK | Successful GET, PUT, DELETE |
| 201 | Created | Successful POST (resource created) |
| 400 | Bad Request | Validation errors, invalid data |
| 401 | Unauthorized | Missing/invalid token, wrong credentials |
| 403 | Forbidden | Valid token but no permission |
| 404 | Not Found | Resource doesn’t exist |
| 500 | Internal Server Error | Server-side errors |
Common Error Types
1. Validation Errors (400)
{ "status": "failed", "message": "Validation failed", "errors": [ { "field": "email", "message": "Please provide a valid email", "value": "invalid-email" }, { "field": "password", "message": "Password must be between 8-128 characters long", "value": "short" } ]}2. Authentication Errors (401)
{ "status": "failed", "message": "Invalid token"}{ "status": "failed", "message": "Token has expired"}3. Database Errors (400/500)
Unique Constraint:
{ "status": "failed", "message": "email already exists"}Record Not Found:
{ "status": "failed", "message": "Record not found"}4. Unexpected Fields (400)
{ "status": "failed", "message": "Validation failed", "errors": [ { "message": "Unexpected fields: archived, userId. Only name, icon, and activityGroup are allowed." } ]}Notes
-
Authentication Token:
- Expires after 2 hours
- Include in
Authorizationheader:Bearer <token> - Obtain from
/auth/loginor/auth/register
-
Date Formats:
- Requests: ISO 8601 (e.g.,
2024-12-19T10:00:00Z) - Responses: ISO 8601 with timezone
- Requests: ISO 8601 (e.g.,
-
UUIDs:
- All IDs are UUID v4 format
- Example:
550e8400-e29b-41d4-a716-446655440000
-
Pagination:
- Default limit: 50
- Maximum limit: 100
- Use
limitandoffsetfor pagination
-
Global vs Custom Resources:
- Global:
userId = null(available to all) - Custom:
userId = <user-uuid>(user-specific) - Users cannot modify/delete global resources
- Global:
-
Soft Delete:
- Activities: Soft deleted (archived = true)
- Entries: Hard deleted
-
Activity Relationship:
- Entries store activity IDs as array
- Use
?includeActivities=trueto get full details - Prevents deletion of used activities