Files
ProjectE/docs/API.md
T
mbatchelder 8f55626e03 refactor: migrate to monorepo structure with Docker, PocketBase, and e2e tests
- Reorganize into apps/, packages/, docs/, e2e/, pocketbase/ directories
- Add Dockerfiles for web, worker, and PocketBase services
- Add docker-compose.yml for local orchestration
- Add turbo.json for monorepo task management
- Add Playwright e2e test infrastructure
- Add PocketBase backend with migrations
- Remove Vite/Next.js/ESLint/PostCSS config files
- Update package.json with workspace dependencies
- Add .env.example and .dockerignore
2026-07-16 06:19:58 -04:00

18 KiB

API Documentation

Project E exposes a REST API through Next.js API routes. All endpoints live under /api/ and communicate via JSON.

Table of Contents

Authentication

All API endpoints (except /api/health and /api/auth/login) require authentication.

Session-Based Auth

The web app uses cookie-based sessions. After login, the server sets an httpOnly cookie named pb_auth containing a PocketBase JWT token.

Login:

POST /api/auth/login
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "your-password"
}

Response:

{
  "user": {
    "id": "user_id",
    "email": "user@example.com",
    "name": "User Name"
  },
  "token": "eyJhbGciOi..."
}

The Set-Cookie header includes the pb_auth cookie. Include this cookie in subsequent requests.

Token Refresh

Tokens expire after 7 days. Refresh before expiry:

POST /api/auth/refresh
Cookie: pb_auth=eyJhbGciOi...

Current User

GET /api/auth/me
Cookie: pb_auth=eyJhbGciOi...

Response:

{
  "id": "user_id",
  "email": "user@example.com",
  "name": "User Name"
}

Logout

POST /api/auth/logout
Cookie: pb_auth=eyJhbGciOi...

Clears the pb_auth cookie.

Request Format

All write endpoints (POST, PATCH, PUT) expect JSON bodies with Content-Type: application/json.

Request bodies are validated with Zod schemas from @project-e/shared. Invalid input returns a 400 VALIDATION_ERROR.

Response Format

Successful responses return JSON with appropriate HTTP status codes:

Status Meaning
200 Success
201 Created
204 No Content (delete)
400 Bad Request (validation error)
401 Unauthorized
404 Not Found
500 Internal Server Error

List endpoints return paginated results:

{
  "items": [...],
  "totalItems": 42,
  "totalPages": 3,
  "page": 1,
  "perPage": 20
}

Error Handling

Errors follow a consistent structure:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input",
    "details": [
      {
        "code": "invalid_string",
        "message": "Title is required",
        "path": ["title"]
      }
    ]
  }
}

Error codes:

Code Status Description
UNAUTHORIZED 401 Missing or invalid auth token
VALIDATION_ERROR 400 Request body failed Zod validation
NOT_FOUND 404 Resource does not exist
FORBIDDEN 403 Insufficient permissions
INTERNAL_ERROR 500 Unexpected server error

Pagination

List endpoints accept query parameters for pagination:

Parameter Type Default Description
page number 1 Page number (1-indexed)
perPage number 50 Items per page (max 200)

Example:

GET /api/tasks?page=2&perPage=20

Filtering and Sorting

Filtering

Use the filter query parameter with PocketBase filter syntax:

GET /api/tasks?filter=status = "todo" && priority = "high"

Filter operators:

Operator Description
= Equal
!= Not equal
>, >=, <, <= Comparison
~ Contains (case-insensitive)
!~ Not contains
^ Starts with
@ Full-text search
&& AND
|| OR

Sorting

Use the sort query parameter. Prefix with - for descending order:

GET /api/tasks?sort=-created
GET /api/tasks?sort=priority,-due_date

Endpoints

Health

GET /api/health

Returns server health status. No authentication required.

Response:

{
  "status": "ok",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "version": "0.1.0"
}

Auth

POST /api/auth/login

Authenticate a user and create a session.

Request:

{
  "email": "user@example.com",
  "password": "your-password"
}

Response (200):

{
  "user": {
    "id": "user_id",
    "email": "user@example.com",
    "name": "User Name"
  },
  "token": "eyJhbGciOi..."
}

POST /api/auth/logout

End the current session. Requires authentication.

GET /api/auth/me

Get the current authenticated user. Requires authentication.

POST /api/auth/refresh

Refresh the auth token. Requires authentication.

Tasks

GET /api/tasks

List tasks with filtering, sorting, and pagination.

Query Parameters:

Parameter Type Description
page number Page number
perPage number Items per page
filter string PocketBase filter expression
sort string Sort field(s)

Example:

GET /api/tasks?filter=status = "todo"&sort=-priority

Response:

{
  "items": [
    {
      "id": "task_id",
      "title": "Complete documentation",
      "description": "Write API docs",
      "status": "todo",
      "priority": "high",
      "due_date": "2024-01-20T00:00:00.000Z",
      "project_id": "project_id",
      "tags": ["docs", "api"],
      "domain": "work",
      "subtasks": [],
      "attachments": [],
      "created": "2024-01-15T10:00:00.000Z",
      "updated": "2024-01-15T10:00:00.000Z"
    }
  ],
  "totalItems": 1,
  "totalPages": 1,
  "page": 1,
  "perPage": 50
}

POST /api/tasks

Create a new task.

Request:

{
  "title": "Complete documentation",
  "description": "Write API docs",
  "status": "todo",
  "priority": "high",
  "due_date": "2024-01-20T00:00:00.000Z",
  "project_id": "project_id",
  "tags": ["docs", "api"],
  "domain": "work"
}

Required fields: title, domain

Response (201): Returns the created task.

GET /api/tasks/[id]

Get a single task by ID.

Response: Returns the task object.

PATCH /api/tasks/[id]

Update a task. Send only the fields you want to change.

Request:

{
  "status": "in_progress",
  "priority": "urgent"
}

Response: Returns the updated task.

DELETE /api/tasks/[id]

Delete a task. Returns 204 No Content.

POST /api/tasks/bulk

Bulk operations on tasks.

Request (bulk update):

{
  "action": "update",
  "task_ids": ["id1", "id2", "id3"],
  "data": {
    "status": "done"
  }
}

Request (bulk delete):

{
  "action": "delete",
  "task_ids": ["id1", "id2", "id3"]
}

Habits

GET /api/habits

List habits with filtering and pagination.

Query Parameters: Same as tasks.

Response: Paginated list of habit objects.

POST /api/habits

Create a new habit.

Request:

{
  "title": "Morning meditation",
  "description": "10 minutes of mindfulness",
  "frequency": "daily",
  "domain": "health",
  "difficulty": "medium",
  "target_count": 1,
  "tags": ["wellness"]
}

Required fields: title, domain

Response (201): Returns the created habit.

GET /api/habits/[id]

Get a single habit by ID.

PATCH /api/habits/[id]

Update a habit.

DELETE /api/habits/[id]

Delete a habit.

GET /api/habit-logs

List habit completion logs.

Query Parameters:

Parameter Type Description
habit_id string Filter by habit
date string Filter by date

POST /api/habit-logs

Log a habit completion.

Request:

{
  "habit_id": "habit_id",
  "date": "2024-01-15",
  "mood": "good",
  "quantity": 1,
  "notes": "Felt great"
}

Projects

GET /api/projects

List projects.

Query Parameters:

Parameter Type Description
status string Filter by status (active, paused, archived)
domain string Filter by domain

POST /api/projects

Create a new project.

Request:

{
  "name": "Website Redesign",
  "description": "Complete overhaul of the company website",
  "domain": "work",
  "status": "active",
  "color": "#3b82f6",
  "tags": ["design", "frontend"]
}

Required fields: name, domain

GET /api/projects/[id]

Get a single project.

PATCH /api/projects/[id]

Update a project.

DELETE /api/projects/[id]

Delete a project.

Notes

GET /api/notes

List notes.

Query Parameters:

Parameter Type Description
domain string Filter by domain
is_pinned boolean Filter by pinned status
is_archived boolean Filter by archived status

POST /api/notes

Create a new note.

Request:

{
  "title": "Meeting Notes",
  "content": "# Meeting with design team\n\nDiscussed new layout...",
  "domain": "work",
  "tags": ["meetings"],
  "is_pinned": false
}

Required fields: title, domain

GET /api/notes/[id]

Get a single note.

PATCH /api/notes/[id]

Update a note.

DELETE /api/notes/[id]

Delete a note.

Reports

GET /api/reports

List reports.

Query Parameters:

Parameter Type Description
type string Filter by type (weekly, monthly, project, habit, custom)
domain string Filter by domain

POST /api/reports

Create a new report.

Request:

{
  "title": "Weekly Summary",
  "report_type": "weekly",
  "domain": "work",
  "date_range_start": "2024-01-08T00:00:00.000Z",
  "date_range_end": "2024-01-14T23:59:59.999Z",
  "content": "# Week of Jan 8-14\n\nCompleted 15 tasks..."
}

Required fields: title, report_type, domain

GET /api/reports/[id]

Get a single report.

PATCH /api/reports/[id]

Update a report.

DELETE /api/reports/[id]

Delete a report.

Milestones

GET /api/milestones

List milestones.

Query Parameters:

Parameter Type Description
project_id string Filter by project
status string Filter by status (planned, in_progress, complete)

POST /api/milestones

Create a new milestone.

Request:

{
  "title": "Design Phase",
  "description": "Complete all design mockups",
  "project_id": "project_id",
  "status": "in_progress",
  "due_date": "2024-02-01T00:00:00.000Z",
  "sort_order": 1
}

Required fields: title, project_id

GET /api/milestones/[id]

Get a single milestone.

PATCH /api/milestones/[id]

Update a milestone.

DELETE /api/milestones/[id]

Delete a milestone.

Domains

GET /api/domains

List all domains.

Response:

{
  "items": [
    {
      "id": "domain_id",
      "name": "Work",
      "color": "#3b82f6",
      "icon": "briefcase",
      "sort_order": 1
    }
  ],
  "totalItems": 3,
  "page": 1,
  "perPage": 50
}

POST /api/domains

Create a new domain.

Request:

{
  "name": "Work",
  "color": "#3b82f6",
  "icon": "briefcase",
  "sort_order": 1
}

Required fields: name

GET /api/domains/[id]

Get a single domain.

PATCH /api/domains/[id]

Update a domain.

DELETE /api/domains/[id]

Delete a domain.

Tags

GET /api/tags

List all tags.

POST /api/tags

Create a new tag.

Request:

{
  "name": "urgent",
  "color": "#ef4444"
}

Required fields: name

GET /api/tags/[id]

Get a single tag.

PATCH /api/tags/[id]

Update a tag.

DELETE /api/tags/[id]

Delete a tag.

Agents

GET /api/agents

List agents.

Query Parameters:

Parameter Type Description
status string Filter by status (active, disabled)

POST /api/agents

Create a new agent.

Request:

{
  "name": "Code Assistant",
  "description": "Helps with code reviews and suggestions",
  "permission_tier": "read_only",
  "webhook_url": "https://example.com/webhook",
  "status": "active"
}

Required fields: name

The server generates an api_key automatically.

GET /api/agents/[id]

Get a single agent.

PATCH /api/agents/[id]

Update an agent.

DELETE /api/agents/[id]

Delete an agent.

GET /api/agent-activity

List recent agent activity.

Query Parameters:

Parameter Type Description
agent_id string Filter by agent
limit number Max results

POST /api/agent-webhook

Webhook endpoint for agent callbacks. Agents POST results here.

Request:

{
  "task_id": "agent_task_id",
  "status": "completed",
  "result": {
    "summary": "Task completed successfully"
  }
}

Webhooks

GET /api/webhooks

List webhooks.

Query Parameters:

Parameter Type Description
active boolean Filter by active status

POST /api/webhooks

Create a new webhook.

Request:

{
  "name": "Task Notifications",
  "url": "https://example.com/webhook",
  "events": ["task.created", "task.updated", "task.completed"],
  "secret": "your-webhook-secret",
  "active": true
}

Required fields: name, url, events

GET /api/webhooks/[id]

Get a single webhook.

PATCH /api/webhooks/[id]

Update a webhook.

DELETE /api/webhooks/[id]

Delete a webhook.

GET /api/webhook-deliveries

List webhook delivery attempts.

Query Parameters:

Parameter Type Description
webhook_id string Filter by webhook
success boolean Filter by success status

Analytics

GET /api/analytics

Get analytics data.

Query Parameters:

Parameter Type Description
period_days number Number of days to analyze (default: 30)

Response:

{
  "taskCompletionRate": 75,
  "habitConsistency": 82,
  "totalTimeMinutes": 1240,
  "activeStreaks": 5,
  "bestStreak": 21,
  "period": 30
}

GET /api/time-summary

Get aggregated time tracking summary.

Query Parameters:

Parameter Type Description
start_date string Start date (ISO 8601)
end_date string End date (ISO 8601)

Response:

{
  "totalMinutes": 1240,
  "byDomain": {
    "work": 800,
    "personal": 440
  },
  "byProject": {
    "project_id_1": 600,
    "project_id_2": 640
  },
  "byTag": {
    "frontend": 300,
    "backend": 500
  }
}

Realtime

GET /api/realtime

Server-sent events endpoint for realtime data updates. Requires authentication.

Query Parameters:

Parameter Type Description
collections string Comma-separated list of collections to subscribe to

Example:

GET /api/realtime?collections=tasks,habits

If no collections specified, subscribes to: tasks, habits, projects, notes, reports, milestones, notifications.

Event format:

data: {"type":"connected","collections":["tasks","habits"]}

data: {"type":"create","collection":"tasks","record":{"id":"...","title":"..."}}

data: {"type":"update","collection":"tasks","record":{"id":"...","status":"done"}}

data: {"type":"delete","collection":"tasks","record":{"id":"..."}}

:ping

The server sends a :ping comment every 30 seconds to keep the connection alive.

MCP

GET /api/mcp

MCP server endpoint using Streamable HTTP transport. Requires API key authentication.

Authentication:

Authorization: Bearer your_agent_api_key

The server validates the API key against the agents collection.

Session management:

  1. Client sends GET /api/mcp to establish a session
  2. Server responds with mcp-session-id header
  3. Client includes mcp-session-id in subsequent POST and DELETE requests
  4. Client sends DELETE /api/mcp to end the session

See MCP Documentation for the full list of 61 available tools.

Rate Limiting

The API does not enforce rate limiting at the application level. Configure rate limiting at the reverse proxy or infrastructure layer.

Recommended limits:

  • Auth endpoints: 10 requests per minute per IP
  • Write endpoints: 100 requests per minute per user
  • Read endpoints: 500 requests per minute per user

SDK Usage

JavaScript/TypeScript

const API_URL = 'http://localhost:3000/api';

// Login
const loginResponse = await fetch(`${API_URL}/auth/login`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'user@example.com', password: 'password' }),
});
const { token } = await loginResponse.json();

// List tasks
const tasksResponse = await fetch(`${API_URL}/tasks?filter=status = "todo"`, {
  headers: { Cookie: `pb_auth=${token}` },
});
const { items: tasks } = await tasksResponse.json();

// Create a task
const createResponse = await fetch(`${API_URL}/tasks`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Cookie: `pb_auth=${token}`,
  },
  body: JSON.stringify({
    title: 'New task',
    domain: 'work',
    priority: 'high',
  }),
});
const task = await createResponse.json();

cURL

# Login
curl -X POST http://localhost:3000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"password"}' \
  -c cookies.txt

# List tasks
curl http://localhost:3000/api/tasks \
  -b cookies.txt

# Create a task
curl -X POST http://localhost:3000/api/tasks \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"title":"New task","domain":"work","priority":"high"}'