Overview

llms.md

+
LLM API Reference

TaskWorks API for LLMs

Comprehensive reference for integrating TaskWorks API with LLM applications.

# TaskWorks API for LLMs

This document provides a comprehensive reference for integrating TaskWorks API with LLM applications. All endpoints return user-specific data only (RLS-protected).

## Authentication

### ApiKey Authentication (Recommended for LLMs)
```
Authorization: ApiKey <token>
```
- Generate API key from Settings > API Key Management
- Token is shown only once, keep it secure
- Works for most endpoints

### Bearer Token Authentication
```
Authorization: Bearer <token>
```
- Used for service-to-service communication
- Required for some endpoints (e.g., /api/v1/blocks/windows)

### Cookie Authentication
- Standard session-based authentication
- Used by web client
- Required for cookie-only endpoints

## Common Response Format

All successful responses follow this structure:
```json
{
  "data": <response_data>,
  "error": null
}
```

All error responses follow this structure:
```json
{
  "error": "<error_type>",
  "message": "<human_readable_message>",
  "code": "<optional_error_code>",
  "context": <optional_additional_context>
}
```

## HTTP Status Codes

- 200: Success
- 201: Created
- 400: Bad Request (validation error)
- 401: Unauthorized (authentication required)
- 403: Forbidden (permission denied)
- 404: Not Found
- 409: Conflict (resource conflict)
- 500: Internal Server Error

## Pagination

Some endpoints support cursor-based pagination:
- cursor: Starting point for next page
- limit: Number of items to return (1-100, default varies)

Response format:
```json
{
  "data": {
    "items": [...],
    "nextCursor": "string | null",
    "hasMore": boolean
  },
  "error": null
}
```

## API Endpoints

### Auth

#### GET /api/v1/auth/me

Get current authenticated user information.

Authentication: ApiKey / Bearer / Cookie

Response Schema:
```json
{
  "data": {
    "id": "string",
    "userId": "string",
    "email": "string",
    "name": "string"
  },
  "error": null
}
```

Response Example:
```json
{
  "data": {
    "id": "user_123",
    "userId": "user_123",
    "email": "user@example.com",
    "name": "John Doe"
  },
  "error": null
}
```

Error Cases:
- 401: Authentication required

#### GET /api/v1/auth/api-keys

List user's API keys.

Authentication: Cookie only

Response Schema:
```json
{
  "data": [
    {
      "id": "string",
      "name": "string | null",
      "scope": "string",
      "revoked": boolean,
      "expiresAt": "string | null",
      "lastUsedAt": "string | null",
      "createdAt": "string"
    }
  ],
  "error": null
}
```

#### POST /api/v1/auth/api-keys

Create a new API key.

Authentication: Cookie only

Request Body:
```json
{
  "name": "string | null",
  "scope": "string",
  "expiresAt": "string | null"
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "key": "string",
    "name": "string | null",
    "scope": "string",
    "expiresAt": "string | null",
    "createdAt": "string"
  },
  "error": null
}
```

#### DELETE /api/v1/auth/api-keys

Revoke an API key.

Authentication: Cookie only

Query Parameters:
- id: string (required) - API key ID to revoke

#### POST /api/v1/auth/api-keys/verify

Verify if an API key is valid.

Authentication: Cookie only

Request Body:
```json
{
  "key": "string"
}
```

Response Schema:
```json
{
  "data": {
    "valid": boolean,
    "userId": "string | null"
  },
  "error": null
}
```

### Tasks

#### GET /api/v1/tasks

Get all tasks for authenticated user.

Authentication: ApiKey / Bearer / Cookie

Query Parameters:
- active: boolean (optional) - Filter by active status
- kind: string (optional) - Filter by task kind ("single" | "habit")

Response Schema:
```json
{
  "data": [
    {
      "id": "string",
      "title": "string",
      "description": "string | null",
      "kind": "single" | "habit",
      "active": boolean,
      "start_date": "string | null",
      "end_date": "string | null",
      "created_at": "string",
      "updated_at": "string",
      "period_rules": Array<PeriodRule>,
      "time_rules": Array<TimeRule>,
      "tags": Array<{ "id": string, "name": string }>
    }
  ],
  "error": null
}
```

#### POST /api/v1/tasks

Create a new task.

Authentication: ApiKey / Bearer / Cookie

Request Body:
```json
{
  "title": "string (required)",
  "description": "string | null",
  "kind": "single" | "habit",
  "active": boolean,
  "start_date": "string | null",
  "end_date": "string | null"
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "title": "string",
    "description": "string | null",
    "kind": "single" | "habit",
    "active": boolean,
    "start_date": "string | null",
    "end_date": "string | null",
    "created_at": "string",
    "updated_at": "string",
    "period_rules": Array<PeriodRule>,
    "time_rules": Array<TimeRule>
  },
  "error": null
}
```

Error Cases:
- 400: Title is required

#### GET /api/v1/tasks/today

Get today's tasks with time slots.

Authentication: ApiKey / Bearer / Cookie

Query Parameters:
- date: string (optional, ISO 8601 format, e.g., "2025-01-17")
- timezone: string (optional, default: "Asia/Tokyo")

Response Schema:
```json
{
  "data": {
    "date": "string",
    "timezone": "string",
    "tasks": [
      {
        "taskId": "string",
        "slotId": "string",
        "title": "string",
        "kind": "single" | "habit",
        "tags": Array<string>,
        "timeLabel": "string",
        "anytime": boolean,
        "startTime": "string | null",
        "endTime": "string | null",
        "target": number,
        "completed": number,
        "remaining": number,
        "status": "todo" | "done" | "skipped",
        "sortMinutes": number
      }
    ]
  },
  "error": null
}
```

Error Cases:
- 400: Invalid date format
- 401: Authentication required

#### GET /api/v1/tasks/overview

Get task overview with statistics.

Authentication: ApiKey / Bearer / Cookie

Query Parameters:
- date: string (optional, ISO 8601 format)
- timezone: string (optional, default: "Asia/Tokyo")

Response Schema:
```json
{
  "data": {
    "date": "string",
    "timezone": "string",
    "today": Array<TodayTaskRow>,
    "stats": {
      "totalTasks": number,
      "completedTasks": number,
      "remainingTasks": number,
      "completionRate": number
    }
  },
  "error": null
}
```

#### GET /api/v1/tasks/search

Search tasks by keyword.

Authentication: ApiKey / Bearer / Cookie

Query Parameters:
- q: string (required) - Search keyword
- limit: number (optional, 1-50, default: 8)

Response Schema:
```json
{
  "data": [
    {
      "id": "string",
      "title": "string",
      "description": "string | null",
      "kind": "single" | "habit"
    }
  ],
  "error": null
}
```

Error Cases:
- 400: Search keyword is required or limit is invalid

#### GET /api/v1/tasks/streak

Get current streak count.

Authentication: ApiKey / Bearer / Cookie

Response Schema:
```json
{
  "data": {
    "current": number,
    "longest": number,
    "breakDate": "string | null"
  },
  "error": null
}
```

Note: Returns 0 for current/longest if unauthenticated.

#### GET /api/v1/tasks/:id

Get specific task details.

Authentication: ApiKey / Bearer / Cookie

Path Parameters:
- id: string (required) - Task ID

Response Schema:
```json
{
  "data": {
    "id": "string",
    "title": "string",
    "description": "string | null",
    "kind": "single" | "habit",
    "active": boolean,
    "start_date": "string | null",
    "end_date": "string | null",
    "period_rules": Array<PeriodRule>,
    "time_rules": Array<TimeRule>,
    "tags": Array<{ "id": string, "name": string }>
  },
  "error": null
}
```

#### PUT /api/v1/tasks/:id

Update a specific task.

Authentication: ApiKey / Bearer / Cookie

Path Parameters:
- id: string (required) - Task ID

Request Body:
```json
{
  "title": "string",
  "description": "string | null",
  "kind": "single" | "habit",
  "active": boolean,
  "start_date": "string | null",
  "end_date": "string | null"
}
```

Response Schema: Same as GET /api/v1/tasks/:id

#### DELETE /api/v1/tasks/:id

Delete a specific task.

Authentication: ApiKey / Bearer / Cookie

Path Parameters:
- id: string (required) - Task ID

Response Schema:
```json
{
  "data": {
    "success": true
  },
  "error": null
}
```

#### POST /api/v1/tasks/:id/execute

Log task execution and award cat cans.

Authentication: ApiKey / Bearer / Cookie

Path Parameters:
- id: string (required) - Task ID

Request Body:
```json
{
  "qty": number (optional, default: 1)
}
```

Response Schema:
```json
{
  "data": {
    "success": boolean,
    "qty": number,
    "rewardTotal": number,
    "rewardErrors": Array<{ "source": string, "message": string }>,
    "streakDays": number
  },
  "error": null
}
```

#### POST /api/v1/tasks/:id/skip

Skip a task with reason.

Authentication: ApiKey / Bearer / Cookie

Path Parameters:
- id: string (required) - Task ID

Request Body:
```json
{
  "reason": "string",
  "scheduled_date": "string"
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "task_id": "string",
    "reason": "string",
    "scheduled_date": "string",
    "skipped_at": "string"
  },
  "error": null
}
```

#### DELETE /api/v1/tasks/:id/skip

Cancel a task skip.

Authentication: ApiKey / Bearer / Cookie

Path Parameters:
- id: string (required) - Task ID

Response Schema:
```json
{
  "data": {
    "success": true
  },
  "error": null
}
```

### Habits

#### GET /api/v1/habits/dashboard

Get habit dashboard data.

Authentication: ApiKey / Bearer / Cookie

Query Parameters:
- year: number (optional, 2020-2030, default: current year)
- month: number (optional, 1-12, default: current month)

Response Schema:
```json
{
  "data": {
    "summary": {
      "year": number,
      "month": number,
      "days": Array<{ "date": string, "done": boolean }>
    },
    "dayStats": Record<string, {
      "required": number,
      "completedTasks": number,
      "done": boolean
    }>,
    "completionRate": number,
    "totals": {
      "trackedDays": number,
      "doneDays": number,
      "remainingDays": number
    },
    "today": {
      "required": number,
      "completed": number,
      "percentage": number
    },
    "streak": {
      "current": number,
      "longest": number,
      "breakDate": "string | null"
    },
    "timezone": "string",
    "taskCount": number,
    "isCurrentMonth": boolean
  },
  "error": null
}
```

Error Cases:
- 400: Invalid year (must be 2020-2030) or month (must be 1-12)
- 401: Authentication required

### Evaluations (Pomodoro)

#### POST /api/v1/evaluations/submit

Submit pomodoro evaluation (concentration, mood).

Authentication: ApiKey / Bearer / Cookie

Request Body:
```json
{
  "task_id": "string (required)",
  "concentration_level": -2 | -1 | 0 | 1 | 2,
  "mood_level": -2 | -1 | 0 | 1 | 2,
  "has_comment": boolean,
  "comment_text": "string | null",
  "pomodoro_session_id": "string | null",
  "execute_task": boolean,
  "task_qty": number
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "user_id": "string",
    "task_id": "string | null",
    "pomodoro_session_id": "string | null",
    "happened_at": "string",
    "concentration_level": -2 | -1 | 0 | 1 | 2,
    "mood_level": -2 | -1 | 0 | 1 | 2,
    "has_comment": boolean,
    "comment_text": "string | null"
  },
  "task_execution": {
    "qty": number,
    "rewardTotal": number,
    "streakDays": number,
    "bonusAmount": number,
    "rewardErrors": Array<{ "source": string, "message": string }>,
    "hasRewardFailure": boolean
  },
  "error": null
}
```

Error Cases:
- 400: task_id is required, concentration_level or mood_level out of range (-2 to 2)
- 403: Permission denied for task execution

#### GET /api/v1/evaluations/last-task

Get last task for default selection.

Authentication: ApiKey / Bearer / Cookie

Response Schema:
```json
{
  "data": {
    "task_id": "string",
    "task_title": "string",
    "selected_at": "string"
  },
  "error": null
}
```

#### POST /api/v1/evaluations/unanswered

Record unanswered pomodoro evaluations.

Authentication: ApiKey / Bearer / Cookie

Request Body:
```json
{
  "events": Array<{
    "id": "string",
    "user_id": "string",
    "task_id": "string | null",
    "pomodoro_session_id": "string | null",
    "happened_at": "string",
    "reason": "ignored" | "closed" | "cancelled"
  }>
}
```

Response Schema:
```json
{
  "data": {
    "success": true,
    "count": number
  },
  "error": null
}
```

### Analytics

#### GET /api/v1/analytics/monthly-comparison

Compare monthly data for trends.

Authentication: ApiKey / Bearer / Cookie

Query Parameters:
- months: number (optional, default: 3)

Response Schema:
```json
{
  "data": {
    "months": [
      {
        "year": number,
        "month": number,
        "completionRate": number,
        "avgConcentration": number | null,
        "avgMood": number | null,
        "streak": { "current": number, "longest": number }
      }
    ],
    "trends": {
      "completionRate": "improving" | "declining" | "stable",
      "concentration": "improving" | "declining" | "stable",
      "mood": "improving" | "declining" | "stable"
    },
    "insights": Array<string>
  },
  "error": null
}
```

#### GET /api/v1/analytics/daily-averages

Get daily concentration/mood averages.

Authentication: ApiKey / Bearer / Cookie

Query Parameters:
- days: number (optional)
- timezone: string (optional, default: "Asia/Tokyo")
- includeTasks: boolean (optional)

Response Schema:
```json
{
  "data": {
    "overall": Array<{
      "date": "string",
      "avg_concentration": number | null,
      "avg_mood": number | null,
      "answer_count": number,
      "unanswered_count": number
    }>,
    "tasks": Record<string, {
      "taskTitle": "string",
      "data": Array<DailyAverage>
    }> | null
  },
  "error": null
}
```

#### GET /api/v1/analytics/task-completion-grid

Get task completion heatmap data.

Authentication: ApiKey / Bearer / Cookie

Query Parameters:
- days: number (optional)
- timezone: string (optional, default: "Asia/Tokyo")

Response Schema:
```json
{
  "data": Array<{
    "date": "string",
    "task_id": "string",
    "task_title": "string",
    "status": "not_scheduled" | "completed" | "not_completed",
    "target_count": number,
    "completed_count": number,
    "time_slots": Array<TimeSlotSummary> | null
  }>,
  "dates": Array<string>,
  "tasks": Array<{ "id": string, "title": string }>,
  "error": null
}
```

#### GET /api/v1/analytics/task-day-detail

Get detailed task data for specific day.

Authentication: ApiKey / Bearer / Cookie

Query Parameters:
- date: string (required, ISO 8601 format)
- timezone: string (optional, default: "Asia/Tokyo")

Response Schema:
```json
{
  "data": {
    "date": "string",
    "tasks": Array<{
      "task_id": "string",
      "task_title": "string",
      "status": "not_scheduled" | "completed" | "not_completed",
      "target_count": number,
      "completed_count": number,
      "answer_events": Array<{
        "id": "string",
        "happened_at": "string",
        "concentration_level": -2 | -1 | 0 | 1 | 2,
        "mood_level": -2 | -1 | 0 | 1 | 2,
        "comment_text": "string | null"
      }>,
      "comments": Array<string>,
      "ai_prediction": {
        "broad_category": "string",
        "specific_reason": "string",
        "suggestion": "string | null",
        "confidence": number | null
      } | null
    }>
  },
  "error": null
}
```

#### GET /api/v1/analytics/daily-detail

Get daily detail analysis via RPC.

Authentication: ApiKey / Bearer / Cookie

Query Parameters:
- date: string (required, ISO 8601 format)
- timezone: string (optional, default: "Asia/Tokyo")

Response Schema:
```json
{
  "data": {
    "date": "string",
    "avg_concentration": number | null,
    "avg_mood": number | null,
    "answer_events": Array<{
      "id": "string",
      "happened_at": "string",
      "task_id": "string | null",
      "task_title": "string | null",
      "concentration_level": -2 | -1 | 0 | 1 | 2,
      "mood_level": -2 | -1 | 0 | 1 | 2,
      "comment_text": "string | null"
    }>,
    "comments": Array<{
      "id": "string",
      "happened_at": "string",
      "task_title": "string | null",
      "text": "string"
    }>,
    "unanswered_count": number,
    "unanswered_events": Array<{
      "id": "string",
      "happened_at": "string",
      "reason": "ignored" | "closed" | "cancelled",
      "task_title": "string | null"
    }>
  },
  "error": null
}
```

### Pomodoro

#### GET /api/v1/pomodoro

Get active pomodoro session.

Authentication: ApiKey / Bearer / Cookie

Response Schema:
```json
{
  "data": {
    "id": "string",
    "user_id": "string",
    "task_id": "string | null",
    "session_type": "work" | "short_break" | "long_break",
    "duration_minutes": number,
    "started_at": "string",
    "scheduled_end_at": "string",
    "actual_end_at": "string | null",
    "status": "active" | "completed" | "cancelled",
    "notification_sent": boolean,
    "evaluation_submitted": boolean,
    "created_at": "string",
    "updated_at": "string"
  } | null,
  "error": null
}
```

#### POST /api/v1/pomodoro

Start new pomodoro session.

Authentication: ApiKey / Bearer / Cookie

Request Body:
```json
{
  "task_id": "string | null",
  "session_type": "work" | "short_break" | "long_break",
  "duration_minutes": number (required, positive)
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "user_id": "string",
    "task_id": "string | null",
    "session_type": "work" | "short_break" | "long_break",
    "duration_minutes": number,
    "started_at": "string",
    "scheduled_end_at": "string",
    "status": "active",
    "notification_sent": false,
    "evaluation_submitted": false,
    "created_at": "string",
    "updated_at": "string"
  },
  "error": null
}
```

Error Cases:
- 400: duration_minutes is required and must be positive

#### POST /api/v1/pomodoro/:id/complete

Complete a pomodoro session.

Authentication: ApiKey / Bearer / Cookie

Path Parameters:
- id: string (required) - Session ID

Response Schema:
```json
{
  "data": {
    "id": "string",
    "actual_end_at": "string",
    "status": "completed"
  },
  "error": null
}
```

#### DELETE /api/v1/pomodoro/:id/cancel

Cancel a pomodoro session.

Authentication: ApiKey / Bearer / Cookie

Path Parameters:
- id: string (required) - Session ID

Response Schema:
```json
{
  "data": {
    "id": "string",
    "status": "cancelled"
  },
  "error": null
}
```

### AI

#### POST /api/v1/ai/chat

Chat with AI assistant (LLM).

Authentication: ApiKey / Bearer / Cookie

Request Body:
```json
{
  "question": "string (required)",
  "context": {
    "year": number,
    "month": number,
    "completionRate": number,
    "streak": number,
    "taskCount": number,
    "habitDashboard": Object,
    "monthlyComparison": Object,
    "concentrationMoodData": Array<Object>,
    "habitsMatrix": Object
  } | {},
  "messages": Array<{
    "role": "user" | "assistant",
    "content": "string",
    "timestamp": "string"
  }>
}
```

Response Schema:
```json
{
  "data": {
    "answer": "string",
    "messages": Array<{
      "role": "user" | "assistant",
      "content": "string",
      "timestamp": "string"
    }>
  },
  "error": null
}
```

Error Cases:
- 400: Question is required or empty
- 400: No available LLM provider configuration
- 503: LLM configuration table not created

#### GET /api/v1/ai/report

Get saved AI habit report.

Authentication: ApiKey / Bearer / Cookie

Query Parameters:
- providerId: string (optional)

Response Schema:
```json
{
  "data": {
    "id": "string",
    "year": number,
    "month": number,
    "content": "string",
    "providerName": "string",
    "providerKind": "openai" | "anthropic" | "azure-openai" | "openrouter" | "custom-openai",
    "generatedAt": "string",
    "createdAt": "string",
    "updatedAt": "string"
  },
  "error": null
}
```

#### POST /api/v1/ai/report

Generate AI habit analysis report.

Authentication: ApiKey / Bearer / Cookie

Request Body:
```json
{
  "providerId": "string | null",
  "useOfficial": boolean,
  "year": number,
  "month": number
}
```

Response Schema:
```json
{
  "data": {
    "content": "string",
    "generatedAt": "string",
    "providerName": "string",
    "providerKind": "openai" | "anthropic" | "azure-openai" | "openrouter" | "custom-openai"
  },
  "error": null
}
```

#### GET /api/v1/ai/providers

List LLM provider configurations.

Authentication: ApiKey / Bearer / Cookie

Response Schema:
```json
{
  "data": [
    {
      "id": "string",
      "name": "string",
      "kind": "openai" | "anthropic" | "azure-openai" | "openrouter" | "custom-openai",
      "apiKey": "string",
      "isDefault": boolean,
      "model": "string | null",
      "maxTokens": number | null,
      "temperature": number | null
    }
  ],
  "error": null
}
```

#### POST /api/v1/ai/providers

Create/update LLM provider configuration.

Authentication: ApiKey / Bearer / Cookie

Request Body:
```json
{
  "id": "string | null",
  "name": "string",
  "kind": "openai" | "anthropic" | "azure-openai" | "openrouter" | "custom-openai",
  "apiKey": "string",
  "isDefault": boolean,
  "model": "string | null",
  "maxTokens": number | null,
  "temperature": number | null
}
```

Response Schema: Same as GET /api/v1/ai/providers (single item in array)

#### DELETE /api/v1/ai/providers

Delete LLM provider configuration.

Authentication: ApiKey / Bearer / Cookie

Request Body:
```json
{
  "providerId": "string (required)"
}
```

Response Schema:
```json
{
  "data": {
    "success": true
  },
  "error": null
}
```

#### POST /api/v1/ai/daily-reflection

Generate daily reflection with AI.

Authentication: ApiKey / Bearer / Cookie

Request Body:
```json
{
  "question": "string",
  "context": Object,
  "providerId": "string | null"
}
```

Response Schema:
```json
{
  "data": {
    "reflection": "string",
    "generatedAt": "string"
  },
  "error": null
}
```

### Gacha

#### POST /api/v1/gacha/draw

Draw gacha (single or ten pull).

Authentication: ApiKey / Bearer / Cookie

Request Body:
```json
{
  "gacha": {
    "id": "string (required)",
    "name": "string (required)",
    "cost": {
      "single": number (required, non-negative),
      "ten": number (required, non-negative)
    }
  },
  "drawType": "single" | "ten"
}
```

Response Schema:
```json
{
  "data": {
    "result": {
      "id": "string",
      "gachaId": "string",
      "gachaName": "string",
      "drawType": "single" | "ten",
      "cards": [
        {
          "image": "string",
          "rarity": "normal" | "rare" | "ultra" | null,
          "clip": "stage" | "supporter" | "trainer" | null
        }
      ],
      "featuredIndex": number,
      "cost": number,
      "recordedAt": "string"
    },
    "catCanBalance": number,
    "catCanDelta": number,
    "ledgerId": "string"
  },
  "error": null
}
```

#### GET /api/v1/gacha/draw

Get latest draw result and balance.

Authentication: ApiKey / Bearer / Cookie

Response Schema:
```json
{
  "data": {
    "result": Object | null,
    "catCanBalance": number
  },
  "error": null
}
```

#### GET /api/v1/gacha/inventory

Get gacha inventory (owned items).

Authentication: ApiKey / Bearer / Cookie

Query Parameters:
- limit: number (optional)

Response Schema:
```json
{
  "data": {
    "cards": [
      {
        "image": "string",
        "rarity": "normal" | "rare" | "ultra" | null,
        "clip": "stage" | "supporter" | "trainer" | null,
        "count": number,
        "latestRecordedAt": "string",
        "latestGachaName": "string",
        "accentFrom": "string | null"
      }
    ],
    "totalUnique": number,
    "totalCount": number
  },
  "error": null
}
```

#### GET /api/v1/gacha/history

Get gacha draw history (paginated).

Authentication: ApiKey / Bearer / Cookie

Query Parameters:
- limit: number (optional)
- cursor: string (optional)

Response Schema:
```json
{
  "data": {
    "items": [
      {
        "id": "string",
        "gachaId": "string",
        "gachaName": "string",
        "drawType": "single" | "ten",
        "cards": Array<Object>,
        "featuredIndex": number,
        "cost": number,
        "recordedAt": "string"
      }
    ],
    "nextCursor": "string | null",
    "hasMore": boolean
  },
  "error": null
}
```

### Notifications

#### POST /api/v1/notifications/send

Send test notification via FCM.

Authentication: Cookie only

Request Body:
```json
{
  "title": "string",
  "body": "string",
  "data": Object
}
```

Response Schema:
```json
{
  "data": {
    "success": boolean,
    "messageId": "string | null"
  },
  "error": null
}
```

#### GET /api/v1/notifications/token

Get active FCM tokens for user.

Authentication: Cookie only

Response Schema:
```json
{
  "data": [
    {
      "id": "string",
      "token": "string",
      "isActive": boolean,
      "deviceInfo": Object | null,
      "lastSeenAt": "string | null",
      "createdAt": "string"
    }
  ],
  "error": null
}
```

#### POST /api/v1/notifications/token

Register/update FCM push token.

Authentication: Cookie only

Request Body:
```json
{
  "token": "string (required)",
  "deviceInfo": Object | null
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "token": "string",
    "isActive": boolean,
    "createdAt": "string"
  },
  "error": null
}
```

#### DELETE /api/v1/notifications/token

Deactivate FCM push token.

Authentication: Cookie only

Request Body:
```json
{
  "token": "string (required)"
}
```

Response Schema:
```json
{
  "data": {
    "success": true
  },
  "error": null
}
```

### Communities

#### GET /api/v1/communities

List all communities (public).

Authentication: None (public)

Response Schema:
```json
{
  "data": [
    {
      "id": "string",
      "name": "string",
      "description": "string",
      "icon_url": "string | null",
      "background_url": "string | null",
      "url": "string | null",
      "member_count": number | null,
      "help_count": number | null,
      "registration_status": "string | null",
      "language": "string | null",
      "category": "string | null",
      "created_at": "string | null",
      "updated_at": "string | null"
    }
  ],
  "error": null
}
```

#### GET /api/v1/communities?communityId=:id

Get specific community with posts (public).

Authentication: None (public)

Query Parameters:
- communityId: string (required) - Community ID

Response Schema:
```json
{
  "data": {
    "community": Object,
    "posts": Array<CommunityPost>
  },
  "error": null
}
```

#### POST /api/v1/communities

Create new community.

Authentication: Cookie only

Request Body:
```json
{
  "name": "string (required)",
  "description": "string",
  "icon_url": "string | null",
  "background_url": "string | null",
  "category": "string | null",
  "language": "string | null"
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "name": "string",
    "description": "string",
    "createdAt": "string"
  },
  "error": null
}
```

#### DELETE /api/v1/communities/:id

Delete community.

Authentication: Cookie + Admin privileges

Path Parameters:
- id: string (required) - Community ID

Response Schema:
```json
{
  "data": {
    "success": true
  },
  "error": null
}
```

Error Cases:
- 403: Forbidden (admin privileges required)

#### GET /api/v1/communities/me/avatar

Get user's community profile.

Authentication: Cookie only

Response Schema:
```json
{
  "data": {
    "id": "string",
    "userId": "string",
    "name": "string",
    "handle": "string",
    "avatarUrl": "string | null",
    "bio": "string | null"
  },
  "error": null
}
```

#### PUT /api/v1/communities/me/avatar

Update user's community profile.

Authentication: Cookie only

Request Body:
```json
{
  "name": "string",
  "handle": "string",
  "avatarUrl": "string | null",
  "bio": "string | null"
}
```

Response Schema: Same as GET /api/v1/communities/me/avatar

#### POST /api/v1/communities/:id/join

Join community.

Authentication: Cookie only

Path Parameters:
- id: string (required) - Community ID

Response Schema:
```json
{
  "data": {
    "id": "string",
    "communityId": "string",
    "userId": "string",
    "joinedAt": "string"
  },
  "error": null
}
```

#### DELETE /api/v1/communities/:id/join

Leave community.

Authentication: Cookie only

Path Parameters:
- id: string (required) - Community ID

Response Schema:
```json
{
  "data": {
    "success": true
  },
  "error": null
}
```

#### POST /api/v1/communities/:id/follow

Follow user within community.

Authentication: Cookie only

Path Parameters:
- id: string (required) - Community ID

Request Body:
```json
{
  "followingId": "string (required)"
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "communityId": "string",
    "followerId": "string",
    "followingId": "string",
    "createdAt": "string"
  },
  "error": null
}
```

#### DELETE /api/v1/communities/:id/follow

Unfollow user within community.

Authentication: Cookie only

Path Parameters:
- id: string (required) - Community ID

Request Body:
```json
{
  "followingId": "string (required)"
}
```

Response Schema:
```json
{
  "data": {
    "success": true
  },
  "error": null
}
```

#### POST /api/v1/communities/:id/posts

Create new post.

Authentication: Cookie only

Path Parameters:
- id: string (required) - Community ID

Request Body:
```json
{
  "content": "string (required)",
  "replyTo": "string | null",
  "images": Array<Object> | null
}
```

Response Schema:
```json
{
  "data": {
    "post": {
      "id": "string",
      "author": {
        "id": "string",
        "name": "string",
        "handle": "string",
        "avatarUrl": "string | null"
      },
      "content": "string",
      "createdAt": "string",
      "updatedAt": "string | null",
      "likesCount": number,
      "repliesCount": number,
      "repostsCount": number,
      "isLiked": boolean,
      "isReposted": boolean,
      "communityId": "string",
      "images": Array<Object> | null,
      "replyTo": "string | null"
    }
  },
  "error": null
}
```

#### GET /api/v1/communities/:id/posts/:postId

Get specific post with replies (public).

Authentication: None (public)

Path Parameters:
- id: string (required) - Community ID
- postId: string (required) - Post ID

Response Schema:
```json
{
  "data": {
    "post": CommunityPost,
    "replies": Array<CommunityPost>
  },
  "error": null
}
```

#### DELETE /api/v1/communities/:id/posts/:postId

Delete post.

Authentication: Cookie + Owner or Admin privileges

Path Parameters:
- id: string (required) - Community ID
- postId: string (required) - Post ID

Response Schema:
```json
{
  "data": {
    "success": true
  },
  "error": null
}
```

Error Cases:
- 403: Forbidden (must be post owner or admin)

#### POST /api/v1/communities/:id/posts/:postId/reactions

Add like/repost reaction.

Authentication: Cookie only

Path Parameters:
- id: string (required) - Community ID
- postId: string (required) - Post ID

Request Body:
```json
{
  "reactionType": "like" | "repost (required)"
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "postId": "string",
    "userId": "string",
    "reactionType": "like" | "repost",
    "createdAt": "string"
  },
  "error": null
}
```

#### DELETE /api/v1/communities/:id/posts/:postId/reactions

Remove like/repost reaction.

Authentication: Cookie only

Path Parameters:
- id: string (required) - Community ID
- postId: string (required) - Post ID

Request Body:
```json
{
  "reactionType": "like" | "repost (required)"
}
```

Response Schema:
```json
{
  "data": {
    "success": true
  },
  "error": null
}
```

#### GET /api/v1/communities/:id/reports

List community reports (admin only).

Authentication: Cookie + Admin privileges

Path Parameters:
- id: string (required) - Community ID

Response Schema:
```json
{
  "data": [
    {
      "id": "string",
      "communityId": "string",
      "postId": "string",
      "reportedBy": "string",
      "reason": "string",
      "status": "pending" | "resolved" | "dismissed",
      "createdAt": "string",
      "updatedAt": "string | null"
    }
  ],
  "error": null
}
```

#### POST /api/v1/communities/:id/reports

Report a post.

Authentication: Cookie only

Path Parameters:
- id: string (required) - Community ID

Request Body:
```json
{
  "postId": "string (required)",
  "reason": "string (required)"
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "communityId": "string",
    "postId": "string",
    "reportedBy": "string",
    "reason": "string",
    "status": "pending",
    "createdAt": "string"
  },
  "error": null
}
```

#### PATCH /api/v1/communities/:id/reports/:reportId

Update report status (admin only).

Authentication: Cookie + Admin privileges

Path Parameters:
- id: string (required) - Community ID
- reportId: string (required) - Report ID

Request Body:
```json
{
  "status": "resolved" | "dismissed (required)"
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "status": "resolved" | "dismissed",
    "updatedAt": "string"
  },
  "error": null
}
```

### Direct Messages (DM)

#### GET /api/v1/dm/threads

List DM threads.

Authentication: Cookie only

Response Schema:
```json
{
  "data": [
    {
      "id": "string",
      "userId": "string",
      "friendId": "string",
      "lastMessageAt": "string | null",
      "unreadCount": number,
      "friend": {
        "id": "string",
        "name": "string",
        "handle": "string",
        "avatarUrl": "string | null"
      }
    }
  ],
  "error": null
}
```

#### POST /api/v1/dm/threads

Create new DM thread with friend.

Authentication: Cookie only

Request Body:
```json
{
  "friendId": "string (required)"
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "userId": "string",
    "friendId": "string",
    "createdAt": "string",
    "friend": Object
  },
  "error": null
}
```

#### GET /api/v1/dm/threads/:threadId

Get thread messages.

Authentication: Cookie only

Path Parameters:
- threadId: string (required) - Thread ID

Response Schema:
```json
{
  "data": {
    "thread": Object,
    "messages": [
      {
        "id": "string",
        "threadId": "string",
        "senderId": "string",
        "content": "string",
        "createdAt": "string",
        "readAt": "string | null"
      }
    ]
  },
  "error": null
}
```

#### POST /api/v1/dm/threads/:threadId/messages

Send message to thread.

Authentication: Cookie only

Path Parameters:
- threadId: string (required) - Thread ID

Request Body:
```json
{
  "content": "string (required)"
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "threadId": "string",
    "senderId": "string",
    "content": "string",
    "createdAt": "string"
  },
  "error": null
}
```

#### POST /api/v1/dm/threads/:threadId/read

Mark thread as read.

Authentication: Cookie only

Path Parameters:
- threadId: string (required) - Thread ID

Response Schema:
```json
{
  "data": {
    "success": true
  },
  "error": null
}
```

#### GET /api/v1/dm/messages/:messageId

Get message details (for deletion).

Authentication: Cookie only

Path Parameters:
- messageId: string (required) - Message ID

Response Schema:
```json
{
  "data": {
    "id": "string",
    "threadId": "string",
    "senderId": "string",
    "content": "string",
    "createdAt": "string"
  },
  "error": null
}
```

### Friends

#### GET /api/v1/friends

List friends and friend requests.

Authentication: Cookie only

Response Schema:
```json
{
  "data": {
    "friends": Array<{
      "id": "string",
      "name": "string",
      "handle": "string",
      "avatarUrl": "string | null",
      "friendsSince": "string"
    }>,
    "pendingRequests": Array<{
      "id": "string",
      "fromUser": Object,
      "createdAt": "string"
    }>,
    "sentRequests": Array<{
      "id": "string",
      "toUser": Object,
      "status": "pending" | "accepted" | "rejected",
      "createdAt": "string"
    }>
  },
  "error": null
}
```

#### POST /api/v1/friends/requests

Send friend request.

Authentication: Cookie only

Request Body:
```json
{
  "recipientId": "string (required)"
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "fromUserId": "string",
    "toUserId": "string",
    "status": "pending",
    "createdAt": "string"
  },
  "error": null
}
```

#### PATCH /api/v1/friends/requests/:requestId

Accept/reject friend request.

Authentication: Cookie only

Path Parameters:
- requestId: string (required) - Request ID

Request Body:
```json
{
  "action": "accept" | "reject (required)"
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "status": "accepted" | "rejected",
    "updatedAt": "string"
  },
  "error": null
}
```

### Realtime (WebRTC Video Rooms)

#### POST /api/v1/realtime/rooms

Create new voice/video room (uses WebRTC offer).

Authentication: None (uses WebRTC offer)

Request Body:
```json
{
  "offer": Object (WebRTC offer)
}
```

Response Schema:
```json
{
  "data": {
    "roomId": "string",
    "answer": Object (WebRTC answer)
  },
  "error": null
}
```

#### POST /api/v1/realtime/rooms/:roomId/session

Join existing room (uses WebRTC offer).

Authentication: None (uses WebRTC offer)

Path Parameters:
- roomId: string (required) - Room ID

Request Body:
```json
{
  "offer": Object (WebRTC offer)
}
```

Response Schema:
```json
{
  "data": {
    "roomId": "string",
    "answer": Object (WebRTC answer),
    "participantId": "string"
  },
  "error": null
}
```

#### GET /api/v1/realtime/rooms/:roomId/state

Get room state.

Authentication: Cookie only

Path Parameters:
- roomId: string (required) - Room ID

Response Schema:
```json
{
  "data": {
    "roomId": "string",
    "participants": Array<{
      "id": "string",
      "userId": "string",
      "joinedAt": "string"
    }>,
    "activeTracks": Array<Object>,
    "createdAt": "string",
    "updatedAt": "string"
  },
  "error": null
}
```

#### POST /api/v1/realtime/rooms/:roomId/tracks/publish

Publish media track.

Authentication: Cookie only

Path Parameters:
- roomId: string (required) - Room ID

Request Body:
```json
{
  "track": Object (WebRTC track data)
}
```

Response Schema:
```json
{
  "data": {
    "trackId": "string",
    "publishedAt": "string"
  },
  "error": null
}
```

#### POST /api/v1/realtime/rooms/:roomId/tracks/subscribe

Subscribe to media track.

Authentication: Cookie only

Path Parameters:
- roomId: string (required) - Room ID

Request Body:
```json
{
  "trackId": "string (required)"
}
```

Response Schema:
```json
{
  "data": {
    "subscribed": true,
    "subscribedAt": "string"
  },
  "error": null
}
```

#### POST /api/v1/realtime/rooms/:roomId/renegotiate

Renegotiate WebRTC connection.

Authentication: Cookie only

Path Parameters:
- roomId: string (required) - Room ID

Request Body:
```json
{
  "offer": Object (WebRTC offer)
}
```

Response Schema:
```json
{
  "data": {
    "answer": Object (WebRTC answer)
  },
  "error": null
}
```

#### POST /api/v1/realtime/communities/:communityId/spaces

List community spaces.

Authentication: Cookie only

Path Parameters:
- communityId: string (required) - Community ID

Response Schema:
```json
{
  "data": [
    {
      "id": "string",
      "communityId": "string",
      "name": "string",
      "roomId": "string | null",
      "createdAt": "string"
    }
  ],
  "error": null
}
```

### Room Viewer (3D Room Customization)

#### GET /api/v1/room-viewer/cats

Get user's 3D room cats.

Authentication: Cookie only

Response Schema:
```json
{
  "data": [
    {
      "id": "string",
      "userId": "string",
      "catId": "string",
      "position": { "x": number, "y": number, "z": number },
      "rotation": { "x": number, "y": number, "z": number },
      "scale": { "x": number, "y": number, "z": number },
      "createdAt": "string",
      "updatedAt": "string"
    }
  ],
  "error": null
}
```

#### POST /api/v1/room-viewer/cats

Add cat to 3D room.

Authentication: Cookie only

Request Body:
```json
{
  "catId": "string (required)",
  "position": { "x": number, "y": number, "z": number },
  "rotation": { "x": number, "y": number, "z": number },
  "scale": { "x": number, "y": number, "z": number }
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "userId": "string",
    "catId": "string",
    "position": Object,
    "rotation": Object,
    "scale": Object,
    "createdAt": "string"
  },
  "error": null
}
```

#### PUT /api/v1/room-viewer/cats/:catId

Update cat position/rotation/scale.

Authentication: Cookie only

Path Parameters:
- catId: string (required) - Cat ID

Request Body:
```json
{
  "position": { "x": number, "y": number, "z": number },
  "rotation": { "x": number, "y": number, "z": number },
  "scale": { "x": number, "y": number, "z": number }
}
```

Response Schema: Same as POST /api/v1/room-viewer/cats

#### DELETE /api/v1/room-viewer/cats/:catId

Remove cat from 3D room.

Authentication: Cookie only

Path Parameters:
- catId: string (required) - Cat ID

Response Schema:
```json
{
  "data": {
    "success": true
  },
  "error": null
}
```

#### GET /api/v1/room-viewer/screenshots

Get user's room screenshots.

Authentication: Cookie only

Response Schema:
```json
{
  "data": [
    {
      "id": "string",
      "userId": "string",
      "imageUrl": "string",
      "thumbnailUrl": "string",
      "capturedAt": "string"
    }
  ],
  "error": null
}
```

#### POST /api/v1/room-viewer/screenshots

Save room screenshot.

Authentication: Cookie only

Request Body:
```json
{
  "imageData": "string (required, base64)",
  "thumbnailData": "string (optional, base64)"
}
```

Response Schema:
```json
{
  "data": {
    "id": "string",
    "userId": "string",
    "imageUrl": "string",
    "thumbnailUrl": "string",
    "capturedAt": "string"
  },
  "error": null
}
```

#### DELETE /api/v1/room-viewer/screenshots/:id

Delete screenshot.

Authentication: Cookie only

Path Parameters:
- id: string (required) - Screenshot ID

Response Schema:
```json
{
  "data": {
    "success": true
  },
  "error": null
}
```

#### GET /api/v1/room-viewer/settings

Get room viewer settings.

Authentication: Cookie only

Response Schema:
```json
{
  "data": {
    "id": "string",
    "userId": "string",
    "backgroundColor": "string | null",
    "floorColor": "string | null",
    "wallColor": "string | null",
    "lightingIntensity": number,
    "createdAt": "string",
    "updatedAt": "string"
  },
  "error": null
}
```

#### PUT /api/v1/room-viewer/settings

Update room viewer settings.

Authentication: Cookie only

Request Body:
```json
{
  "backgroundColor": "string | null",
  "floorColor": "string | null",
  "wallColor": "string | null",
  "lightingIntensity": number
}
```

Response Schema: Same as GET /api/v1/room-viewer/settings

### Blocks

#### GET /api/v1/blocks/windows

Get task blocking windows for TaskWorks.

Authentication: Bearer / Session only (ApiKey NOT supported)

Query Parameters:
- date: string (optional, ISO 8601 format)
- timezone: string (optional, default: "UTC")
- focus_only: boolean (optional, default: false)
- merge: boolean (optional, default: false)
- debug: boolean (optional, default: false)
- pre_grace_min: number (optional, default: 0)
- post_grace_min: number (optional, default: 0)
- duration_default_min: number (optional, default: 60)

Response Schema (debug: false):
```json
{
  "data": [
    {
      "taskId": "string",
      "taskTitle": "string",
      "startTime": "string",
      "endTime": "string",
      "redirectUrl": "string"
    }
  ],
  "error": null
}
```

Response Schema (debug: true):
```json
{
  "data": [
    /* Same window objects */
  ],
  "meta": {
    "dateIso": "string",
    "timeZone": "string",
    "focusOnly": boolean,
    "mergeOverlaps": boolean,
    "taskCount": number,
    "completedTaskCount": number,
    "completedCounts": Record<string, number>,
    "windowCount": number
  },
  "error": null
}
```

Error Cases:
- 401: Unauthorized (ApiKey not supported)
- 401: User not found

## Data Schemas

### Period Rule
```json
{
  "id": "string",
  "cadence": "daily" | "weekly" | "monthly" | "interval",
  "times_per_period": number | null,
  "period": "string",
  "days_of_week": Array<number> | null,
  "timezone": "string"
}
```

### Time Rule
```json
{
  "id": "string",
  "start_time": "string | null",
  "end_time": "string | null",
  "anytime": boolean
}
```

### Community Post
```json
{
  "id": "string",
  "author": {
    "id": "string",
    "name": "string",
    "handle": "string",
    "avatarUrl": "string | null",
    "bio": "string | null",
    "followersCount": number,
    "followingCount": number,
    "postsCount": number,
    "joinedAt": "string",
    "isFollowing": boolean,
    "isFriend": boolean
  },
  "content": "string",
  "createdAt": "string",
  "updatedAt": "string | null",
  "likesCount": number,
  "repliesCount": number,
  "repostsCount": number,
  "isLiked": boolean,
  "isReposted": boolean,
  "communityId": "string",
  "images": Array<{
    "id": "string",
    "url": "string",
    "alt": "string",
    "width": number,
    "height": number
  }> | null,
  "replyTo": "string | null",
  "replies": Array<CommunityPost> | null
}
```

## Use Cases

### Creating and Completing a Task Flow
1. Create a task: POST /api/v1/tasks
2. Get today's tasks: GET /api/v1/tasks/today
3. Execute task: POST /api/v1/tasks/:id/execute
4. Submit evaluation: POST /api/v1/evaluations/submit

### Analyzing Task Completion Patterns
1. Get daily averages: GET /api/v1/analytics/daily-averages
2. Get task completion grid: GET /api/v1/analytics/task-completion-grid
3. Get monthly comparison: GET /api/v1/analytics/monthly-comparison

### Working with Habits
1. Get habit dashboard: GET /api/v1/habits/dashboard?year=2025&month=1
2. Get daily detail: GET /api/v1/analytics/daily-detail

### Pomodoro Workflow
1. Start session: POST /api/v1/pomodoro
2. Get active session: GET /api/v1/pomodoro
3. Complete session: POST /api/v1/pomodoro/:id/complete
4. Submit evaluation: POST /api/v1/evaluations/submit

### Gacha Operations
1. Get balance and latest result: GET /api/v1/gacha/draw
2. Draw single or ten: POST /api/v1/gacha/draw
3. View inventory: GET /api/v1/gacha/inventory
4. View history: GET /api/v1/gacha/history

## Fewshot Examples

### Python: Get Tomorrow's Tasks

```python
#!/usr/bin/env python3
"""
Get tomorrow's tasks from TaskWorks API.

Setup:
  export TASKWORKS_API_KEY="YOUR_TOKEN"
  pip install requests
"""

import os
import requests
from datetime import datetime, timedelta

DEFAULT_BASE_URL = "https://my-app.yinyoo2904.workers.dev"
DEFAULT_TIMEZONE = "Asia/Tokyo"


def tomorrow_iso_date(timezone: str = DEFAULT_TIMEZONE) -> str:
    """Return tomorrow's date in YYYY-MM-DD format."""
    from zoneinfo import ZoneInfo
    tz = ZoneInfo(timezone)
    now = datetime.now(tz=tz)
    return (now.date() + timedelta(days=1)).isoformat()


def get_tasks_today(api_key: str, target_date: str, timezone: str = DEFAULT_TIMEZONE):
    """Get tasks for a specific date."""
    url = f"{DEFAULT_BASE_URL}/api/v1/tasks/today"
    headers = {
        "Authorization": f"ApiKey {api_key}",
        "Accept": "application/json",
    }
    params = {
        "date": target_date,
        "timezone": timezone
    }
    
    response = requests.get(url, headers=headers, params=params, timeout=20)
    response.raise_for_status()
    
    body = response.json()
    if body.get("error") is not None:
        raise RuntimeError(f"API error: {body.get('error')}: {body.get('message')}")
    
    return body["data"]


def main():
    api_key = os.environ.get("TASKWORKS_API_KEY", "").strip()
    if not api_key:
        raise ValueError("TASKWORKS_API_KEY environment variable is not set")
    
    target_date = tomorrow_iso_date()
    data = get_tasks_today(api_key, target_date)
    
    print(f"date: {data.get('date')}  timezone: {data.get('timezone')}")
    
    tasks = data.get("tasks", [])
    if not tasks:
        print("No tasks for tomorrow.")
        return
    
    for task in tasks:
        time_part = task.get("timeLabel") or ("Anytime" if task.get("anytime") else "")
        tags_part = f" [{', '.join(task.get('tags', []))}]" if task.get("tags") else ""
        progress = f"{task.get('completed', 0):g}/{task.get('target', 0):g}"
        status = task.get("status", "")
        
        print(f"- {time_part:>10} | {task.get('title')}{tags_part} | {status} | {progress}")


if __name__ == "__main__":
    main()
```

### JavaScript/TypeScript: Create and Complete Task

```typescript
/**
 * TaskWorks API Client for TypeScript/JavaScript
 */

interface TaskWorksClientConfig {
  apiKey: string;
  baseUrl?: string;
}

class TaskWorksClient {
  private apiKey: string;
  private baseUrl: string;

  constructor(config: TaskWorksClientConfig) {
    this.apiKey = config.apiKey;
    this.baseUrl = config.baseUrl || "https://my-app.yinyoo2904.workers.dev";
  }

  private headers(): HeadersInit {
    return {
      "Authorization": `ApiKey ${this.apiKey}`,
      "Accept": "application/json",
      "Content-Type": "application/json",
    };
  }

  async getTasksToday(date?: string, timezone: string = "Asia/Tokyo") {
    const url = new URL(`${this.baseUrl}/api/v1/tasks/today`);
    if (date) url.searchParams.set("date", date);
    url.searchParams.set("timezone", timezone);

    const response = await fetch(url.toString(), {
      headers: this.headers(),
    });

    if (!response.ok) {
      const body = await response.json();
      throw new Error(`API error: ${body.error}: ${body.message}`);
    }

    return response.json();
  }

  async createTask(task: {
    title: string;
    description?: string;
    kind?: "single" | "habit";
  }) {
    const response = await fetch(`${this.baseUrl}/api/v1/tasks`, {
      method: "POST",
      headers: this.headers(),
      body: JSON.stringify(task),
    });

    if (!response.ok) {
      const body = await response.json();
      throw new Error(`API error: ${body.error}: ${body.message}`);
    }

    return response.json();
  }

  async executeTask(taskId: string, qty: number = 1) {
    const response = await fetch(`${this.baseUrl}/api/v1/tasks/${taskId}/execute`, {
      method: "POST",
      headers: this.headers(),
      body: JSON.stringify({ qty }),
    });

    if (!response.ok) {
      const body = await response.json();
      throw new Error(`API error: ${body.error}: ${body.message}`);
    }

    return response.json();
  }

  async submitEvaluation(evaluation: {
    taskId: string;
    concentrationLevel: -2 | -1 | 0 | 1 | 2;
    moodLevel: -2 | -1 | 0 | 1 | 2;
    hasComment: boolean;
    commentText?: string;
  }) {
    const response = await fetch(`${this.baseUrl}/api/v1/evaluations/submit`, {
      method: "POST",
      headers: this.headers(),
      body: JSON.stringify(evaluation),
    });

    if (!response.ok) {
      const body = await response.json();
      throw new Error(`API error: ${body.error}: ${body.message}`);
    }

    return response.json();
  }
}

async function main() {
  const apiKey = process.env.TASKWORKS_API_KEY;
  if (!apiKey) {
    throw new Error("TASKWORKS_API_KEY environment variable is not set");
  }

  const client = new TaskWorksClient({ apiKey });

  try {
    // Get today's tasks
    const todayResponse = await client.getTasksToday();
    console.log("Today's tasks:", todayResponse.data.tasks);

    if (todayResponse.data.tasks.length > 0) {
      const firstTask = todayResponse.data.tasks[0];
      console.log(`
First task: ${firstTask.title}`);

      // Execute the task
      const execResponse = await client.executeTask(firstTask.taskId, 1);
      console.log("Task executed:", execResponse.data);

      if (execResponse.data.success) {
        console.log(`
Reward received: ${execResponse.data.rewardTotal} cat cans`);

        // Submit evaluation
        const evalResponse = await client.submitEvaluation({
          taskId: firstTask.taskId,
          concentrationLevel: 1,
          moodLevel: 1,
          hasComment: false,
        });
        console.log("Evaluation submitted:", evalResponse.data);
      }
    }

  } catch (error) {
    console.error("Error:", error instanceof Error ? error.message : error);
    process.exit(1);
  }
}

if (require.main === module) {
  main();
}
```

### Node.js: Get Habit Dashboard

```javascript
/**
 * Get habit dashboard for a specific month
 */

const https = require('https');

class TaskWorksClient {
  constructor(apiKey, baseUrl = 'https://my-app.yinyoo2904.workers.dev') {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
  }

  getHeaders() {
    return {
      'Authorization': `ApiKey ${this.apiKey}`,
      'Accept': 'application/json',
    };
  }

  async getHabitDashboard(year, month) {
    const url = new URL(`${this.baseUrl}/api/v1/habits/dashboard`);
    url.searchParams.set('year', year);
    url.searchParams.set('month', month);

    return this.request(url.toString());
  }

  async request(url) {
    return new Promise((resolve, reject) => {
      const urlObj = new URL(url);
      const options = {
        hostname: urlObj.hostname,
        path: urlObj.pathname + urlObj.search,
        method: 'GET',
        headers: this.getHeaders(),
      };

      const req = https.request(options, (res) => {
        let data = '';

        res.on('data', (chunk) => {
          data += chunk;
        });

        res.on('end', () => {
          try {
            const body = JSON.parse(data);
            if (res.statusCode !== 200) {
              reject(new Error(`API error: ${res.statusCode} ${body.error}: ${body.message}`));
            }
            resolve(body);
          } catch (e) {
            reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
          }
        });
      });
        res.on('end', () => {
          if (res.statusCode >= 200 && res.statusCode < 300) {
            try {
              resolve(JSON.parse(data));
            } catch (e) {
              reject(new Error('Invalid JSON response'));
            }
          } else {
            try {
              const body = JSON.parse(data);
              reject(new Error(`API error: ${res.statusCode} ${body.error}: ${body.message}`));
            } catch (e) {
              reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
            }
          }
        });
      });

      req.on('error', (e) => {
        reject(e);
      });

      req.end();
    });
  }
}

async function main() {
  const apiKey = process.env.TASKWORKS_API_KEY;
  if (!apiKey) {
    console.error('TASKWORKS_API_KEY environment variable is not set');
    process.exit(1);
  }

  const client = new TaskWorksClient(apiKey);

  try {
    const now = new Date();
    const year = now.getFullYear();
    const month = now.getMonth() + 1;

    const response = await client.getHabitDashboard(year, month);
    const dashboard = response.data;

    console.log(`Habit Dashboard for ${year}-${month.toString().padStart(2, '0')}`);
    console.log(`Completion rate: ${dashboard.completionRate.toFixed(1)}%`);
    console.log(`Streak: ${dashboard.streak.current} days (longest: ${dashboard.streak.longest} days)`);
    console.log(`Tasks: ${dashboard.taskCount} habits`);
    console.log(`
Today: ${dashboard.today.completed}/${dashboard.today.required} completed (${dashboard.today.percentage}%)`);
    console.log(`Month: ${dashboard.totals.doneDays}/${dashboard.totals.trackedDays} days done, ${dashboard.totals.remainingDays} remaining`);

  } catch (error) {
    console.error('Error:', error.message);
    process.exit(1);
  }
}

if (require.main === module) {
  main();
}
```

### cURL: Search Tasks

```bash
#!/bin/bash
# Search tasks by keyword

API_KEY="${TASKWORKS_API_KEY:-YOUR_TOKEN}"
BASE_URL="${BASE_URL:-https://my-app.yinyoo2904.workers.dev}"
KEYWORD="${1:-task}"
LIMIT="${2:-10}"

echo "Searching for: $KEYWORD (limit: $LIMIT)"

curl -X GET "${BASE_URL}/api/v1/tasks/search?q=${KEYWORD}&limit=${LIMIT}" \
  -H "Authorization: ApiKey ${API_KEY}" \
  -H "Accept: application/json" \
  -s | jq '.data'
```

### Python: Get Daily Averages

```python
#!/usr/bin/env python3
"""
Get daily concentration and mood averages
"""

import os
import requests
from datetime import datetime, timedelta

DEFAULT_BASE_URL = "https://my-app.yinyoo2904.workers.dev"


def get_daily_averages(api_key: str, days: int = 30, timezone: str = "Asia/Tokyo"):
    """Get daily concentration and mood averages."""
    url = f"{DEFAULT_BASE_URL}/api/v1/analytics/daily-averages"
    headers = {
        "Authorization": f"ApiKey {api_key}",
        "Accept": "application/json",
    }
    params = {
        "days": days,
        "timezone": timezone,
        "includeTasks": "true"
    }
    
    response = requests.get(url, headers=headers, params=params, timeout=20)
    response.raise_for_status()
    
    body = response.json()
    if body.get("error") is not None:
        raise RuntimeError(f"API error: {body.get('error')}: {body.get('message')}")
    
    return body["data"]


def main():
    api_key = os.environ.get("TASKWORKS_API_KEY", "").strip()
    if not api_key:
        raise ValueError("TASKWORKS_API_KEY environment variable is not set")
    
    data = get_daily_averages(api_key, days=30)
    
    print("Daily Averages (last 30 days)")
    print("=" * 60)
    
    overall = data.get("overall", [])
    for entry in overall:
        date = entry.get("date", "")
        conc = entry.get("avg_concentration")
        mood = entry.get("avg_mood")
        answers = entry.get("answer_count", 0)
        missed = entry.get("unanswered_count", 0)
        
        conc_str = f"{conc:.2f}" if conc is not None else "N/A"
        mood_str = f"{mood:.2f}" if mood is not None else "N/A"
        
        print(f"{date} | Conc: {conc_str:>6} | Mood: {mood_str:>6} | Answers: {answers:2d} | Missed: {missed:2d}")


if __name__ == "__main__":
    main()
```

### JavaScript: Pomodoro Workflow

```javascript
/**
 * Complete Pomodoro workflow: start session, complete it, submit evaluation
 */

class PomodoroClient extends TaskWorksClient {
  async startPomodoro({ taskId, sessionType, durationMinutes }) {
    const response = await fetch(`${this.baseUrl}/api/v1/pomodoro`, {
      method: "POST",
      headers: this.headers(),
      body: JSON.stringify({
        task_id: taskId,
        session_type: sessionType,
        duration_minutes: durationMinutes,
      }),
    });

    if (!response.ok) {
      const body = await response.json();
      throw new Error(`API error: ${body.error}: ${body.message}`);
    }

    return response.json();
  }

  async completePomodoro(sessionId) {
    const response = await fetch(`${this.baseUrl}/api/v1/pomodoro/${sessionId}/complete`, {
      method: "POST",
      headers: this.headers(),
    });

    if (!response.ok) {
      const body = await response.json();
      throw new Error(`API error: ${body.error}: ${body.message}`);
    }

    return response.json();
  }

  async getActivePomodoro() {
    const response = await fetch(`${this.baseUrl}/api/v1/pomodoro`, {
      method: "GET",
      headers: this.headers(),
    });

    if (!response.ok) {
      const body = await response.json();
      throw new Error(`API error: ${body.error}: ${body.message}`);
    }

    return response.json();
  }
}

async function main() {
  const apiKey = process.env.TASKWORKS_API_KEY;
  if (!apiKey) {
    throw new Error("TASKWORKS_API_KEY environment variable is not set");
  }

  const client = new PomodoroClient({ apiKey });

  try {
    // Check if there's an active session
    const activeResponse = await client.getActivePomodoro();
    if (activeResponse.data) {
      console.log("Active session found:", activeResponse.data.id);
      return;
    }

    // Start a new 25-minute work session
    console.log("Starting 25-minute work session...");
    const startResponse = await client.startPomodoro({
      taskId: null,
      sessionType: "work",
      durationMinutes: 25,
    });

    const session = startResponse.data;
    console.log("Session started:", session.id);
    console.log("Scheduled to end at:", session.scheduled_end_at);

    // Wait for user to complete the session
    console.log("
Press Enter when the session is complete...");
    await new Promise((resolve) => process.stdin.once('data', resolve));

    // Complete the session
    console.log("Completing session...");
    const completeResponse = await client.completePomodoro(session.id);
    console.log("Session completed:", completeResponse.data);

  } catch (error) {
    console.error("Error:", error instanceof Error ? error.message : error);
    process.exit(1);
  }
}

if (require.main === module) {
  main();
}
```

### Python: Gacha Draw

```python
#!/usr/bin/env python3
"""
Draw gacha cards
"""

import os
import requests
from dataclasses import dataclass
from typing import List, Optional

DEFAULT_BASE_URL = "https://my-app.yinyoo2904.workers.dev"


@dataclass
class GachaConfig:
    id: str
    name: str
    cost: dict


@dataclass
class GachaCard:
    image: str
    rarity: Optional[str]
    clip: Optional[str]


class GachaClient:
    def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL):
        self.api_key = api_key
        self.base_url = base_url

    def get_headers(self):
        return {
            "Authorization": f"ApiKey {self.api_key}",
            "Accept": "application/json",
            "Content-Type": "application/json",
        }

    def draw_gacha(self, gacha: GachaConfig, draw_type: str = "single"):
        """Draw gacha cards."""
        url = f"{self.base_url}/api/v1/gacha/draw"
        body = {
            "gacha": {
                "id": gacha.id,
                "name": gacha.name,
                "cost": gacha.cost
            },
            "drawType": draw_type
        }

        response = requests.post(url, headers=self.get_headers(), json=body, timeout=30)
        response.raise_for_status()

        data = response.json()
        if data.get("error") is not None:
            raise RuntimeError(f"API error: {data.get('error')}: {data.get('message')}")

        return data["data"]

    def get_balance(self):
        """Get current cat can balance."""
        url = f"{self.base_url}/api/v1/gacha/draw"
        response = requests.get(url, headers=self.get_headers(), timeout=20)
        response.raise_for_status()

        data = response.json()
        if data.get("error") is not None:
            raise RuntimeError(f"API error: {data.get('error')}: {data.get('message')}")

        return data["data"]["catCanBalance"]

    def get_inventory(self, limit: int = 100):
        """Get gacha inventory."""
        url = f"{self.base_url}/api/v1/gacha/inventory"
        params = {"limit": limit}
        response = requests.get(url, headers=self.get_headers(), params=params, timeout=20)
        response.raise_for_status()

        data = response.json()
        if data.get("error") is not None:
            raise RuntimeError(f"API error: {data.get('error')}: {data.get('message')}")

        return data["data"]


def main():
    api_key = os.environ.get("TASKWORKS_API_KEY", "").strip()
    if not api_key:
        raise ValueError("TASKWORKS_API_KEY environment variable is not set")

    client = GachaClient(api_key)

    try:
        # Get current balance
        balance = client.get_balance()
        print(f"Current balance: {balance} cat cans")

        # Define gacha configuration (example values)
        gacha_config = GachaConfig(
            id="example_gacha_1",
            name="Example Gacha",
            cost={"single": 100, "ten": 1000}
        )

        # Draw single card
        print("
Drawing single card (cost: 100)...")
        result = client.draw_gacha(gacha_config, "single")

        print(f"Result ID: {result['result']['id']}")
        print(f"Gacha: {result['result']['gachaName']}")
        print(f"Cost: {result['cost']} cat cans")
        print(f"Balance after: {result['catCanBalance']} cat cans")

        # Display drawn cards
        cards = result["result"]["cards"]
        print(f"
Drawn cards ({len(cards)}):")
        for i, card in enumerate(cards, 1):
            rarity = card.get("rarity", "normal")
            print(f"  {i}. {card['image']} (rarity: {rarity})")

        # Get inventory
        print("
Inventory:")
        inventory = client.get_inventory()
        print(f"  Total unique: {inventory['totalUnique']}")
        print(f"  Total count: {inventory['totalCount']}")

        for item in inventory["cards"][:5]:
            print(f"  - {item['image']} x{item['count']}")

        if len(inventory["cards"]) > 5:
            print(f"  ... and {len(inventory['cards']) - 5} more")

    except Exception as error:
        print(f"Error: {error}")
        import sys
        sys.exit(1)


if __name__ == "__main__":
    main()
```

## Quick Start Example

### cURL

### JavaScript (fetch)

### Python (requests)

## Important Notes

- RLS Protection: All endpoints return user-specific data only
- ApiKey Limitations: /api/v1/blocks/windows does not support ApiKey authentication (use Bearer or Session)
- Admin Endpoints: Some community endpoints require administrator permissions
- Rate Limiting: High-frequency clients should set expiresAt for periodic key rotation
- Security: API keys are shown only once after generation. Store them securely.