REST API Reference

The Duet REST API is served by the core Hono server on port 7777 by default. All endpoints return JSON. The base URL is http://localhost:7777 for local development.

Authentication

Duet supports two authentication methods:

  • Human users - session cookie (duet_session) obtained via the login endpoint.
  • Agents - API key passed via the Authorization header using Bearer token format.
# Human: login to get a session cookie
curl -X POST http://localhost:7777/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"password": "your-password"}' \
  -c cookies.txt

# Agent: use Bearer token
curl http://localhost:7777/api/notes \
  -H "Authorization: Bearer duet_xxxxx"

Auth Endpoints

MethodPathDescription
POST/api/auth/setupSet initial password (first run only)
POST/api/auth/loginLogin with password, returns session cookie
POST/api/auth/logoutClear session cookie
GET/api/auth/meCheck current auth state
# Initial setup (first run)
curl -X POST http://localhost:7777/api/auth/setup \
  -H "Content-Type: application/json" \
  -d '{"password": "your-secure-password"}'

# Check auth state
curl http://localhost:7777/api/auth/me -b cookies.txt

Notes

All note endpoints require authentication.

MethodPathDescription
POST/api/notesCreate a new note
GET/api/notesList notes with optional filters
GET/api/notes/searchFull-text search notes
GET/api/notes/:idRead a single note
PATCH/api/notes/:idUpdate a note
POST/api/notes/:id/archiveArchive a note
POST/api/notes/:id/unarchiveUnarchive a note
DELETE/api/notes/:idDelete a note (humans only)
GET/api/notes/:id/versionsGet version history
GET/api/notes/:id/versions/:versionNumberGet a specific version

Create Note

POST /api/notes
Content-Type: application/json

{
  "title": "Meeting Notes",
  "content": "## Attendees\n- Alice\n- Bob",
  "tags": ["meetings", "engineering"]
}

List Notes

Query parameters:

ParameterTypeDescription
authorTypestring"human" or "agent"
authorNamestringSpecific author name
tagNamestringFilter by tag name
isArchivedbooleanInclude archived notes
limitnumberMax results (default 50)
offsetnumberPagination offset (default 0)
GET /api/notes?authorType=agent&tagName=engineering&limit=10

Search Notes

GET /api/notes/search?q=deployment+checklist&limit=5

Update Note

PATCH /api/notes/:id
Content-Type: application/json

{
  "title": "Updated Title",
  "content": "New content here",
  "tags": ["updated-tag"]
}

Todos

MethodPathDescription
POST/api/todosCreate a new todo
GET/api/todosList todos with optional filters
GET/api/todos/:idRead a single todo
PATCH/api/todos/:idUpdate a todo
DELETE/api/todos/:idDelete a todo (humans only)

Create Todo

POST /api/todos
Content-Type: application/json

{
  "title": "Review PR #42",
  "priority": "high",
  "dueDate": "2026-03-20T17:00:00Z",
  "noteId": "optional-note-uuid",
  "assigneeType": "human",
  "assigneeName": "emre"
}

List Todos

Query parameters:

ParameterTypeDescription
statusstring"pending" or "done"
prioritystring"low", "medium", "high", or "urgent"
limitnumberMax results (default 50)
GET /api/todos?status=pending&priority=high

Tags

Tag management is restricted to human users only.

MethodPathDescription
POST/api/tagsCreate a tag (humans only)
GET/api/tagsList all tags
PATCH/api/tags/:idUpdate a tag (humans only)
DELETE/api/tags/:idDelete a tag (humans only)
POST /api/tags
Content-Type: application/json

{
  "name": "engineering",
  "color": "#3b82f6"
}

Agents

Agent management is restricted to human users only. When creating an agent, the API key is returned in the response and is only shown once.

MethodPathDescription
POST/api/agentsRegister a new agent (returns API key)
GET/api/agentsList all registered agents
POST/api/agents/:id/deactivateDeactivate an agent
POST/api/agents/:id/reactivateReactivate an agent
DELETE/api/agents/:idDelete an agent (humans only)
POST /api/agents
Content-Type: application/json

{
  "name": "claude",
  "permissions": ["read", "write", "archive"]
}

// Response:
{
  "agent": { "id": "...", "name": "claude", ... },
  "apiKey": "duet_xxxxx"  // Only shown once!
}

Activity

MethodPathDescription
GET/api/activityGet activity feed

Query parameters:

ParameterTypeDescription
limitnumberMax entries (default 20)
entityTypestring"note", "todo", or "agent_key"
actorNamestringFilter by actor name
GET /api/activity?actorName=claude&entityType=note&limit=10

Settings

Settings are key-value pairs managed by human users only.

MethodPathDescription
GET/api/settings/:keyRead a setting (humans only)
PUT/api/settings/:keyWrite a setting (humans only)
PUT /api/settings/theme
Content-Type: application/json

{
  "value": "dark"
}

Calendar

Google Calendar integration endpoints. See the Google Calendar Sync guide for setup instructions.

MethodPathDescription
GET/api/calendar/auth-urlGet Google Calendar OAuth URL
GET/api/calendar/callbackOAuth callback (redirect target)
GET/api/calendar/statusCheck calendar connection status
POST/api/calendar/syncTrigger a manual sync
POST/api/calendar/disconnectDisconnect Google Calendar

Uploads

MethodPathDescription
POST/api/uploadsUpload a file (multipart/form-data)
GET/api/uploads/:filenameServe an uploaded file
# Upload a file
curl -X POST http://localhost:7777/api/uploads \
  -b cookies.txt \
  -F "file=@screenshot.png"

# Response:
{
  "filename": "abc123-screenshot.png",
  "url": "/api/uploads/abc123-screenshot.png"
}

Health

MethodPathDescription
GET/api/healthHealth check (no auth required)
GET /api/health

// Response:
{ "status": "ok" }

Error Responses

All errors follow a consistent format:

{
  "error": "Not found",
  "message": "Note with id 'abc123' does not exist"
}

Common HTTP status codes:

StatusMeaning
200Success
201Created
400Bad request (invalid parameters)
401Unauthorized (missing or invalid auth)
403Forbidden (insufficient permissions)
404Resource not found
500Internal server error