11 KiB
11 KiB
API Specification
Base URL
Development: http://localhost:3000/api
Production: https://api.carryyourlive.com/api
Authentication
All endpoints require authentication via Bearer token:
Authorization: Bearer <jwt_token>
Endpoints
Sync
Pull Changes
GET /sync
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| since | integer | Yes | Unix timestamp (ms) of last successful sync |
Response (200 OK)
{
"categories": [
{
"id": "cat_1",
"name": "Work",
"color": "#E53935",
"order": 0,
"createdAt": 1699000000000,
"updatedAt": 1699000000000
}
],
"tasks": [
{
"id": "task_1",
"title": "Finish project",
"description": "Complete the mobile app",
"categoryId": "cat_1",
"priority": "high",
"completed": false,
"dueDate": 1699100000000,
"dueTime": "17:00",
"createdAt": 1699000000000,
"updatedAt": 1699000000000
}
],
"subtasks": [
{
"id": "sub_1",
"taskId": "task_1",
"title": "Setup Expo project",
"completed": true,
"order": 0,
"createdAt": 1699000000000,
"updatedAt": 1699000000000
}
],
"timestamp": 1699150000000
}
Error Responses
| Status | Code | Message |
|---|---|---|
| 400 | INVALID_TIMESTAMP | Invalid or missing since parameter |
| 401 | UNAUTHORIZED | Missing or invalid token |
| 500 | SERVER_ERROR | Internal server error |
Push Changes
POST /sync/push
Request Body
{
"changes": {
"categories": [
{
"id": "cat_new",
"name": "New Category",
"color": "#1E88E5",
"order": 6,
"createdAt": 1699150000000,
"updatedAt": 1699150000000
}
],
"tasks": [
{
"id": "task_new",
"title": "New Task",
"description": "Task description",
"categoryId": "cat_1",
"priority": "medium",
"completed": false,
"dueDate": 1699200000000,
"dueTime": "10:00",
"createdAt": 1699150000000,
"updatedAt": 1699150000000
}
],
"subtasks": [
{
"id": "sub_new",
"taskId": "task_new",
"title": "Subtask 1",
"completed": false,
"order": 0,
"createdAt": 1699150000000,
"updatedAt": 1699150000000
}
]
},
"lastPulledAt": 1699100000000
}
Response (200 OK)
{
"success": true,
"timestamp": 1699150000000,
"conflicts": [
{
"entity": "tasks",
"id": "task_1",
"serverVersion": { ... },
"clientVersion": { ... },
"resolution": "server_wins"
}
]
}
Error Responses
| Status | Code | Message |
|---|---|---|
| 400 | INVALID_PAYLOAD | Malformed request body |
| 401 | UNAUTHORIZED | Missing or invalid token |
| 409 | CONFLICT | Unresolvable conflicts (if not using auto-resolution) |
| 500 | SERVER_ERROR | Internal server error |
Categories
List Categories
GET /categories
Response (200 OK)
{
"categories": [
{
"id": "cat_1",
"name": "Work",
"color": "#E53935",
"order": 0,
"createdAt": 1699000000000,
"updatedAt": 1699000000000
}
]
}
Create Category
POST /categories
Request Body
{
"name": "New Category",
"color": "#1E88E5",
"order": 6
}
Response (201 Created)
{
"id": "cat_new",
"name": "New Category",
"color": "#1E88E5",
"order": 6,
"createdAt": 1699150000000,
"updatedAt": 1699150000000
}
Update Category
PATCH /categories/{id}
Request Body
{
"name": "Updated Name",
"color": "#FB8C00",
"order": 1
}
Response (200 OK)
{
"id": "cat_1",
"name": "Updated Name",
"color": "#FB8C00",
"order": 1,
"createdAt": 1699000000000,
"updatedAt": 1699150000000
}
Delete Category
DELETE /categories/{id}
Response (204 No Content)
Tasks
List Tasks
GET /tasks
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| categoryId | string | Filter by category |
| completed | boolean | Filter by completion status |
| dueBefore | integer | Due date before timestamp (ms) |
| dueAfter | integer | Due date after timestamp (ms) |
| priority | string | Filter by priority |
| sortBy | string | Sort field: dueDate, priority, createdAt, title |
| sortOrder | string | asc or desc |
| limit | integer | Pagination limit (default 50) |
| offset | integer | Pagination offset |
Response (200 OK)
{
"tasks": [
{
"id": "task_1",
"title": "Finish project",
"description": "Complete the mobile app",
"categoryId": "cat_1",
"priority": "high",
"completed": false,
"dueDate": 1699100000000,
"dueTime": "17:00",
"createdAt": 1699000000000,
"updatedAt": 1699000000000,
"subtasks": [
{
"id": "sub_1",
"title": "Setup Expo project",
"completed": true,
"order": 0
}
]
}
],
"total": 1,
"limit": 50,
"offset": 0
}
Get Task
GET /tasks/{id}
Response (200 OK)
{
"id": "task_1",
"title": "Finish project",
"description": "Complete the mobile app",
"categoryId": "cat_1",
"priority": "high",
"completed": false,
"dueDate": 1699100000000,
"dueTime": "17:00",
"createdAt": 1699000000000,
"updatedAt": 1699000000000,
"subtasks": [...]
}
Create Task
POST /tasks
Request Body
{
"title": "New Task",
"description": "Task description",
"categoryId": "cat_1",
"priority": "medium",
"dueDate": 1699200000000,
"dueTime": "10:00",
"subtasks": [
{ "title": "Subtask 1" },
{ "title": "Subtask 2" }
]
}
Response (201 Created)
{
"id": "task_new",
"title": "New Task",
"description": "Task description",
"categoryId": "cat_1",
"priority": "medium",
"completed": false,
"dueDate": 1699200000000,
"dueTime": "10:00",
"createdAt": 1699150000000,
"updatedAt": 1699150000000,
"subtasks": [
{ "id": "sub_1", "title": "Subtask 1", "completed": false, "order": 0 },
{ "id": "sub_2", "title": "Subtask 2", "completed": false, "order": 1 }
]
}
Update Task
PATCH /tasks/{id}
Request Body (all fields optional)
{
"title": "Updated Title",
"description": "Updated description",
"categoryId": "cat_2",
"priority": "high",
"completed": true,
"dueDate": 1699300000000,
"dueTime": "12:00"
}
Response (200 OK)
{
"id": "task_1",
"title": "Updated Title",
"description": "Updated description",
"categoryId": "cat_2",
"priority": "high",
"completed": true,
"dueDate": 1699300000000,
"dueTime": "12:00",
"createdAt": 1699000000000,
"updatedAt": 1699150000000,
"subtasks": [...]
}
Delete Task
DELETE /tasks/{id}
Response (204 No Content)
Batch Update Tasks
POST /tasks/batch
Request Body
{
"operations": [
{ "type": "update", "id": "task_1", "data": { "completed": true } },
{ "type": "delete", "id": "task_2" },
{ "type": "create", "data": { "title": "New Task", "categoryId": "cat_1" } }
]
}
Response (200 OK)
{
"results": [
{ "id": "task_1", "success": true },
{ "id": "task_2", "success": true },
{ "id": "task_new", "success": true, "data": { ... } }
]
}
Subtasks
List Subtasks
GET /tasks/{taskId}/subtasks
Response (200 OK)
{
"subtasks": [
{
"id": "sub_1",
"taskId": "task_1",
"title": "Subtask 1",
"completed": false,
"order": 0,
"createdAt": 1699000000000,
"updatedAt": 1699000000000
}
]
}
Create Subtask
POST /tasks/{taskId}/subtasks
Request Body
{
"title": "New Subtask",
"order": 2
}
Response (201 Created)
{
"id": "sub_new",
"taskId": "task_1",
"title": "New Subtask",
"completed": false,
"order": 2,
"createdAt": 1699150000000,
"updatedAt": 1699150000000
}
Update Subtask
PATCH /subtasks/{id}
Request Body
{
"title": "Updated Subtask",
"completed": true,
"order": 1
}
Response (200 OK)
Delete Subtask
DELETE /subtasks/{id}
Response (204 No Content)
Users
Get Current User
GET /users/me
Response (200 OK)
{
"id": "user_1",
"email": "user@example.com",
"name": "John Doe",
"settings": {
"darkMode": false,
"notifications": true,
"reminderTime": "09:00",
"defaultCategory": "cat_1",
"sortBy": "dueDate",
"sortOrder": "asc"
},
"createdAt": 1699000000000
}
Update Settings
PATCH /users/me/settings
Request Body
{
"darkMode": true,
"notifications": false,
"reminderTime": "08:00",
"defaultCategory": "cat_2",
"sortBy": "priority",
"sortOrder": "desc"
}
Response (200 OK)
Data Types
Category
interface Category {
id: string;
name: string;
color: string; // HEX format
order: number;
createdAt: number; // Unix timestamp (ms)
updatedAt: number; // Unix timestamp (ms)
}
Task
interface Task {
id: string;
title: string;
description: string;
categoryId: string;
priority: 'none' | 'low' | 'medium' | 'high' | 'critical';
completed: boolean;
dueDate: number; // Unix timestamp (ms), 0 if not set
dueTime: string; // HH:MM format, empty if not set
createdAt: number;
updatedAt: number;
subtasks?: Subtask[];
}
Subtask
interface Subtask {
id: string;
taskId: string;
title: string;
completed: boolean;
order: number;
createdAt: number;
updatedAt: number;
}
UserSettings
interface UserSettings {
darkMode: boolean;
notifications: boolean;
reminderTime: string; // HH:MM
defaultCategory: string; // Category ID
sortBy: 'dueDate' | 'priority' | 'title' | 'createdAt';
sortOrder: 'asc' | 'desc';
}
Error Format
All error responses follow this format:
{
"error": {
"code": "ERROR_CODE",
"message": "Human readable message",
"details": {}
}
}
Common error codes:
INVALID_PAYLOAD- Request validation failedUNAUTHORIZED- Authentication requiredFORBIDDEN- Insufficient permissionsNOT_FOUND- Resource not foundCONFLICT- Version conflictRATE_LIMITED- Too many requestsSERVER_ERROR- Internal server error
Rate Limiting
- 100 requests/minute per user for sync endpoints
- 500 requests/minute per user for other endpoints
- Returns 429 with
Retry-Afterheader
Webhooks (Optional)
Server can notify clients of remote changes:
POST /webhooks/sync
Payload:
{
"event": "data_changed",
"entities": ["tasks", "categories"],
"timestamp": 1699150000000
}
Client should trigger sync on receipt.