diff --git a/docs/API.md b/docs/API.md index b9df55d..8ba848b 100644 --- a/docs/API.md +++ b/docs/API.md @@ -205,7 +205,275 @@ Base path: `/api/users` ### 4.3 Expense Lists -_TODO_ +Base path: `/api/expenselist` + +All endpoints in this group require authentication. The authenticated caller is resolved from the JWT and used for ownership checks. + +--- + +#### `GET /api/expenselist/mine` — Get caller's expense lists + +Returns all expense lists where the caller is the owner **or** has been shared the list. + +**Auth required:** Yes + +**Request body:** None + +**Success response:** `200 OK` — array of [ExpenseList](#expenselist). + +```json +[ + { + "id": 1, + "name": "Holiday Trip", + "inviteCode": null, + "owner": { "id": 1, "username": "alice" }, + "sharedWith": null, + "expenses": [], + "customCategories": [] + } +] +``` + +--- + +#### `GET /api/expenselist/byId` — Get expense list by ID + +**Auth required:** Yes + +**Query params:** +| Param | Type | Required | Description | +|-------|------|----------|-------------| +| `id` | Long | Yes | Expense list database ID | + +The caller must be the owner or the shared user of the list. + +**Success response:** `200 OK` — returns [ExpenseList](#expenselist). + +**Error responses:** +| Status | Condition | +|--------|-----------| +| 403 | Caller is neither owner nor shared user | +| 404 | No list found for `id` | + +--- + +#### `POST /api/expenselist/create` — Create an expense list + +**Auth required:** Yes + +**Request body:** +```json +{ + "name": "Road Trip 2026" +} +``` + +| Field | Type | Constraints | +|-------|------|-------------| +| `name` | String | Required. Max 100 chars. | + +The authenticated caller becomes the `owner` of the new list. The list is initialised with the default Xpensely standard categories. + +**Success response:** `201 Created` — returns the created [ExpenseList](#expenselist). + +**Error responses:** +| Status | Condition | +|--------|-----------| +| 400 | Validation failure | + +--- + +#### `DELETE /api/expenselist/{id}` — Delete an expense list + +**Auth required:** Yes + +**Path params:** +| Param | Type | Description | +|-------|------|-------------| +| `id` | Long | Expense list database ID | + +Only the **owner** may delete a list. Deleting a list cascades to all its expenses and custom categories. + +**Success response:** `204 No Content` + +**Error responses:** +| Status | Condition | +|--------|-----------| +| 403 | Caller is not the owner | +| 404 | No list found for `id` | + +--- + +#### `POST /api/expenselist/{id}/add` — Add an expense to a list + +**Auth required:** Yes + +**Path params:** +| Param | Type | Description | +|-------|------|-------------| +| `id` | Long | Expense list database ID | + +**Request body:** +```json +{ + "title": "Dinner", + "owner": "alice", + "amount": 42.50, + "personalUseAmount": 21.25, + "otherPersonAmount": 21.25, + "date": "2026-05-09", + "category": "Food" +} +``` + +| Field | Type | Constraints | +|-------|------|-------------| +| `title` | String | Required. Max 100 chars. | +| `owner` | String | Required. Username of the person who paid. | +| `amount` | Double | Required. Min 0.01. | +| `personalUseAmount` | Double | Optional. Caller's share. | +| `otherPersonAmount` | Double | Optional. Other person's share. | +| `date` | String (ISO-8601) | Required. Format: `YYYY-MM-DD`. | +| `category` | String | Required. Non-blank category name. | + +**Success response:** `200 OK` — returns the created [Expense](#expense). + +**Error responses:** +| Status | Condition | +|--------|-----------| +| 400 | Validation failure | +| 403 | Caller is not a member (owner or sharedWith) of the list | +| 404 | List not found | + +--- + +#### `PUT /api/expenselist/{id}/update` — Update an expense + +**Auth required:** Yes + +**Path params:** +| Param | Type | Description | +|-------|------|-------------| +| `id` | Long | Expense list database ID | + +**Request body:** +```json +{ + "id": 7, + "title": "Dinner (updated)", + "ownerName": "alice", + "amount": 50.00, + "personalUseAmount": 25.00, + "otherPersonAmount": 25.00, + "date": "2026-05-09", + "category": "Food" +} +``` + +| Field | Type | Constraints | +|-------|------|-------------| +| `id` | Long | Required. ID of the expense to update. | +| `title` | String | Required. Max 100 chars. | +| `ownerName` | String | Required. Username of the payer. | +| `amount` | Double | Required. Min 0.01. | +| `personalUseAmount` | Double | Optional. | +| `otherPersonAmount` | Double | Optional. | +| `date` | String (ISO-8601) | Required. `YYYY-MM-DD`. | +| `category` | String | Required. | + +Caller must be a member of the list. Expense must belong to the specified list. + +**Success response:** `200 OK` — returns the updated [Expense](#expense). + +**Error responses:** +| Status | Condition | +|--------|-----------| +| 400 | Validation failure or expense does not belong to this list | +| 403 | Caller is not a member of the list | +| 404 | List or expense not found | + +--- + +#### `DELETE /api/expenselist/{id}/delete` — Remove an expense from a list + +**Auth required:** Yes + +**Path params:** +| Param | Type | Description | +|-------|------|-------------| +| `id` | Long | Expense list database ID | + +**Query params:** +| Param | Type | Required | Description | +|-------|------|----------|-------------| +| `expenseId` | Long | Yes | ID of the expense to remove | + +Caller must be a member of the list. + +**Success response:** `200 OK` — returns the deleted [Expense](#expense). + +**Error responses:** +| Status | Condition | +|--------|-----------| +| 403 | Caller is not a member of the list | +| 404 | List or expense not found | + +--- + +#### `POST /api/expenselist/{listId}/invite` — Generate an invite code + +Generates (or refreshes) a 6-character uppercase invite code for the list, valid for **1 week**. + +**Auth required:** Yes + +**Path params:** +| Param | Type | Description | +|-------|------|-------------| +| `listId` | Long | Expense list database ID | + +Caller must be the **owner** of the list. + +**Success response:** `200 OK` — returns the invite code as a plain string. + +``` +AB3X7Q +``` + +**Error responses:** +| Status | Condition | +|--------|-----------| +| 403 | Caller is not the owner | +| 404 | List not found | + +--- + +#### `POST /api/expenselist/accept-invite` — Accept an invite + +Joins the caller to a shared expense list using an invite code. + +**Auth required:** Yes + +**Request body:** +```json +{ + "inviteCode": "AB3X7Q" +} +``` + +| Field | Type | Constraints | +|-------|------|-------------| +| `inviteCode` | String | Required. Exactly 6 characters. | + +**Success response:** `200 OK` — returns the [ExpenseList](#expenselist) the caller joined. + +**Error responses:** +| Status | Condition | +|--------|-----------| +| 400 | Validation failure or invite code not found / expired | +| 403 | Caller is already the owner of this list | + +--- ## 5. Data Models