API Reference
Hierarch runs a small set of edge functions on Supabase using the Hono framework (Deno runtime). These handle operations that require admin-level database access or server-side processing. All other data operations go directly through the Supabase client library from the browser.
Base URL
https://[project-ref].supabase.co/functions/v1/server
All edge function endpoints are prefixed with /functions/v1/server. The Hono router handles path matching within that base.
Authentication
Protected endpoints require a Supabase access token in the Authorization header:
Authorization: Bearer [supabase_access_token]
The edge function validates the token using supabaseAdmin.auth.getUser(token). Invalid or missing tokens return a 401 Unauthorized response.
Endpoints
POST /signup
Creates a new user account via the Supabase admin API. This endpoint is unauthenticated.
POST /functions/v1/server/signup
Content-Type: application/json
{
"email": "user@example.com",
"password": "securepassword"
}
Response: 200
{
"user": { "id": "uuid", "email": "user@example.com" }
}POST /upload-avatar
Uploads a user avatar to the Supabase Storage avatars bucket. Returns a signed URL for the uploaded file. Requires authentication.
POST /functions/v1/server/upload-avatar
Authorization: Bearer [token]
Content-Type: multipart/form-data
Form data:
file: [binary image data]
Response: 200
{
"url": "https://[project-ref].supabase.co/storage/v1/object/sign/avatars/[path]"
}GET /time-entries
Lists all focus timer entries for the authenticated user. Entries are stored in the Supabase KV store (separate from Postgres).
GET /functions/v1/server/time-entries
Authorization: Bearer [token]
Response: 200
[
{
"id": "entry-uuid",
"taskId": "task-uuid",
"duration": 1500,
"startedAt": "2026-03-16T10:00:00Z",
"completedAt": "2026-03-16T10:25:00Z"
}
]POST /time-entries
Creates a new focus timer entry.
POST /functions/v1/server/time-entries
Authorization: Bearer [token]
Content-Type: application/json
{
"taskId": "task-uuid",
"duration": 1500,
"startedAt": "2026-03-16T10:00:00Z",
"completedAt": "2026-03-16T10:25:00Z"
}
Response: 200
{ "id": "entry-uuid", ... }DELETE /time-entries
Deletes a time entry by ID (passed as a query parameter).
DELETE /functions/v1/server/time-entries?id=entry-uuid
Authorization: Bearer [token]
Response: 200
{ "success": true }DELETE /delete-account
Permanently deletes the authenticated user's account and all associated data (KV entries, stored files, database records).
DELETE /functions/v1/server/delete-account
Authorization: Bearer [token]
Response: 200
{ "success": true }KV Store
Time entries use Supabase's KV store rather than Postgres. Each entry is keyed as a tuple: ["time_entry", user_id, entry_id]. The edge function provides helper methods for KV operations:
| kvSet(key, value) | Store a value at the given key tuple |
| kvGet(key) | Retrieve a value by key tuple |
| kvList(prefix) | List all entries matching a key prefix |
| kvDelete(key) | Delete an entry by key tuple |
Client Data Layer
Most data operations bypass edge functions entirely and use the Supabase JavaScript client directly from the browser. The data access layer lives at src/app/api/data.ts and provides these core functions:
Projects
| getProjects() | Fetch all projects for the current user, ordered by created_at |
| createProject(name, metadata, startDate?, endDate?, description?) | Create a new project |
| updateProject(id, updates) | Update project fields (name, description, metadata, dates) |
| deleteProject(id) | Delete a project and cascade to tasks and resources |
Tasks
| getTasks(projectId?) | Fetch tasks. null = unassigned only, undefined = all tasks |
| createTask(task, projectId?) | Create a task with serialized description JSON |
| updateTask(id, updates) | Merge updates into existing task (re-serializes description) |
| deleteTask(id) | Delete a task (linked resources get task_id set to null) |
Resources
| getResources() | Fetch all resources for the current user |
| createResource(resource) | Create a resource with serialized content JSON |
| updateResource(id, updates) | Update resource fields |
| deleteResource(id) | Delete a resource |
Data Serialization
Tasks pack multiple fields into the description column as JSON:
{
"content": "Rich text content (string)",
"phaseHistory": [
{
"id": "string",
"fromPhase": "string",
"toPhase": "string",
"timestamp": "ISO 8601",
"reviewer": "string (optional)",
"notes": "string (optional)"
}
]
}The parseTaskDescription(raw) function unpacks this JSON when reading from the database, and createTask / updateTask re-serialize it when writing.
Error Handling
Edge function errors return standard HTTP status codes:
| 401 | Missing or invalid authentication token |
| 400 | Malformed request body or missing required fields |
| 404 | Requested resource not found |
| 500 | Internal server error (database or KV store failure) |
Error responses include a JSON body with a message field describing the issue.