API Endpoints Reference
> Complete reference for the Otto backend REST API. > > Base URL: `http://localhost:8000` (configurable via `BACKEND_API_URL`) > > Framework: FastAPI (Python)
Complete reference for the Otto backend REST API.
Base URL:
http://localhost:8000(configurable viaBACKEND_API_URL)Framework: FastAPI (Python)
Authentication
Otto supports two authentication modes controlled by DEPLOYMENT_MODE:
| Mode | Header | Behavior |
|---|---|---|
self-hosted (default) | None | All requests pass through without authentication |
hosted | Authorization: Bearer <firebase-id-token> | Firebase ID token validated on every request |
Auth-exempt paths (hosted mode): /health, /webhook/slack, /docs, /openapi.json, and CORS preflight (OPTIONS).
Slack endpoints use HMAC signature verification via SLACK_SIGNING_SECRET, independent of Firebase auth.
Health and System
No route prefix. Registered in app/routers/health.py.
GET /
Root welcome endpoint.
- Auth: None
- Response:
GET /health
Health check for containers, App Engine, and the control plane. Returns operational metadata only (never customer data).
- Auth: None (exempt)
- Response:
GET /api/health
Detailed API health check.
- Auth: Yes (hosted mode)
- Response:
GET /api/version
API version and feature information.
- Auth: Yes (hosted mode)
- Response:
GET /system/info
System info -- verifies Node.js/npm/npx availability for MCP servers.
- Auth: Yes (hosted mode)
- Response:
Tasks
No route prefix. Registered in app/routers/tasks.py.
GET /tasks
Get all tasks with summary information.
- Auth: Yes (hosted mode)
- Response:
List[TaskResponse]
GET /task/{task_id}
Get a specific task by ID.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description task_idstring Task ID -
Response:
TaskResponse
GET /task/{task_id}/subtasks
Get all subtasks for a parent task.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description task_idstring Parent task ID -
Response:
List[TaskResponse]
DELETE /task/{task_id}
Delete or archive a task. First call archives; second call deletes permanently.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description task_idstring Task ID -
Response:
204 No Content
GET /task/{task_id}/worker-status
Get task worker status for monitoring.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description task_idstring Task ID -
Response:
POST /chat
Create a new task from user input. Queues agent processing via ARQ.
- Auth: Yes (hosted mode)
- Request body:
- Response:
POST /human_response
Resume an agent task that is waiting for human input.
- Auth: Yes (hosted mode)
- Request body:
- Response:
GET /worker/health
Check if ARQ workers are running.
- Auth: Yes (hosted mode)
- Response:
TaskResponse Model
| Field | Type | Description |
|---|---|---|
id | string | Task ID |
title | string | Task title |
description | string | Task description |
status | string | pending, running, completed, failed, waiting_input, waiting_subagents |
created_at | string | ISO 8601 timestamp |
updated_at | string | ISO 8601 timestamp |
progress | integer | 0-100 |
question | string | Pending question (when status=waiting_input) |
plan | string | Agent's execution plan |
last_tool_call_id | string | Tool call ID for resume (may be JSON array for subagent dispatches) |
checkpoint_thread_id | string | Redis checkpoint thread identifier |
logs | array | List of TaskLogEntry objects |
outputs | array | List of TaskOutput objects |
creator | string | Creator user ID |
assignee | string | Assigned user ID |
webhook_type | string | Source webhook type (if created via webhook) |
webhook_payload | object | Original webhook payload |
webhook_headers | object | Original webhook headers |
source_ip | string | Source IP address |
celery_task_id | string | ARQ job ID (legacy field name) |
parent_task_id | string | Parent task ID (for subtasks) |
task_type | string | task or subtask |
agent_type | string | Subagent type: general, research, tool-specialist |
token_budget | integer | Token budget for subagents |
tokens_used | integer | Tokens consumed |
Users
Route prefix: /users. Registered in app/routers/users.py.
GET /users
Get all users in the system.
- Auth: Yes (hosted mode)
- Response:
List[UserDict]
PUT /users/{user_id}
Update user information. Fields provided are marked as manual overrides and will not be overwritten by Slack sync.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description user_idstring User ID -
Request body (all fields optional):
-
Response: Updated user dict
DELETE /users/{user_id}/overrides
Clear all manual overrides for a user, allowing Slack sync to update all fields again.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description user_idstring User ID -
Response:
GET /users/{user_id}/notifications
Get notifications for a specific user.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description user_idstring User ID -
Response:
List[NotificationDict]
POST /users/{user_id}/notifications/{notification_id}/mark_read
Mark a notification as read.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description user_idstring User ID notification_idstring Notification ID -
Response:
DELETE /users/{user_id}/notifications/{notification_id}
Delete a notification.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description user_idstring User ID notification_idstring Notification ID -
Response:
204 No Content
Files
No route prefix. Registered in app/routers/files.py.
POST /upload-file
Upload a file for a user. Validates file extension, checks storage quota, and saves to user-specific sandbox directory.
-
Auth: Yes (hosted mode)
-
Request: Multipart form data
Field Type Description user_idstring User ID filefile File to upload -
Allowed extensions:
.txt,.md,.json,.csv,.pdf,.docx,.pptx,.xlsx,.png,.jpg,.jpeg,.gif,.webp -
Response:
Slack Integration
Route prefix: /slack. Registered in app/routers/slack.py.
All Slack endpoints verify requests via HMAC signature using SLACK_SIGNING_SECRET. This is separate from Firebase auth.
POST /slack/interactive
Handle Slack interactive components (button clicks, e.g., "Stop Reminders").
- Auth: Slack signature verification
- Request: Form-encoded body with
payload(JSON string from Slack) - Response: Slack-format JSON
POST /slack/dm_response
Main Slack Events API handler. Dispatches internally based on event type.
- Auth: Slack signature verification
- Request: JSON body (Slack Events API payload)
- Response: JSON (always 200 for Slack)
Event types handled:
| Event Type | Behavior |
|---|---|
url_verification | Responds with challenge token |
message (DM or channel) | Routes to human response for waiting tasks, or creates continuation tasks for thread replies |
app_mention | Creates new tasks. Recognizes schedule, remind, and recurring remind commands via pattern matching |
member_joined_channel | Syncs channel, triggers project onboarding (if Otto was invited), or adds user membership |
member_left_channel | Removes user membership |
channel_deleted | Deactivates channel in database |
GET /slack/health
Slack integration health check. Tests real Slack API connectivity.
- Auth: Yes (hosted mode)
- Response:
Webhooks
Route prefix: /webhook. Registered in app/routers/webhooks.py.
POST /webhook/{webhook_type}
Receive and queue a webhook of any type. Parses JSON body and enqueues for agent processing.
-
Auth: None for
/webhook/slack(exempt); Yes for others (hosted mode) -
Path parameters:
Parameter Type Description webhook_typestring Webhook type identifier (e.g., github,stripe,slack) -
Request body: Any JSON payload
-
Response:
GET /webhook/health
Webhook subsystem health check.
- Auth: Yes (hosted mode)
- Response:
GET /webhook/stats
Webhook processing statistics.
- Auth: Yes (hosted mode)
- Response:
GET /webhook/status
Webhook processing worker status.
- Auth: Yes (hosted mode)
- Response:
statusis one of:running,stopped,not_initialized.
MCP Status (Legacy)
No route prefix. Registered in app/routers/mcp.py.
GET /mcp/status
Get MCP status. MCP is now handled by ARQ workers, so this returns a static message.
- Auth: Yes (hosted mode)
- Response:
MCP Configuration
Route prefix: /api/mcp. Registered in app/api/mcp_endpoints.py.
GET /api/mcp/status
Get current MCP server status without changing configuration.
- Auth: Yes (hosted mode)
- Response:
POST /api/mcp/configure
Configure MCP servers. Reloads configuration with new server definitions.
- Auth: Yes (hosted mode)
- Request body:
- Response:
MCPConfigResponsewith server counts and per-server status
DELETE /api/mcp/configure
Clear MCP configuration and shut down all servers.
- Auth: Yes (hosted mode)
- Response:
MCPConfigResponse(all counts zero)
Skills
Route prefix: /api/skills. Registered in app/api/skills_endpoints.py.
GET /api/skills/
List all available skills.
- Auth: Yes (hosted mode)
- Response:
List[SkillSummary]
GET /api/skills/requirements
Show all skill requirements and their status (binaries, env vars, Python/Node packages, platform).
- Auth: Yes (hosted mode)
- Response:
GET /api/skills/{skill_name}
Get detailed information for a specific skill.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description skill_namestring Skill name -
Response:
SkillDetail
POST /api/skills/match
Match skills to a query using semantic similarity.
- Auth: Yes (hosted mode)
- Request body:
thresholdandmax_skillsare optional (defaults:0.6and3). - Response:
POST /api/skills/reload
Reload skills from the filesystem.
- Auth: Yes (hosted mode)
- Response:
GET /api/skills/debug/similarity/{query}
Get similarity scores for all skills against a query. For debugging and analytics.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description querystring Search query (URL-encoded) -
Response:
POST /api/skills/
Create a new skill.
- Auth: Yes (hosted mode)
- Request body:
tags,author, andallowed_toolsare optional. - Response:
SkillDetail
PUT /api/skills/{skill_name}
Update an existing skill. All body fields are optional.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description skill_namestring Skill name -
Request body:
-
Response:
SkillDetail
DELETE /api/skills/{skill_name}
Delete a skill.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description skill_namestring Skill name -
Response:
POST /api/skills/generate
Generate a skill using AI from a natural language description.
- Auth: Yes (hosted mode)
- Request body:
- Response:
Channels
Route prefix: /api/channels. Registered in app/api/channels_endpoints.py.
GET /api/channels/
List all discovered Slack channels.
- Auth: Yes (hosted mode)
- Response:
List[SlackChannelResponse]
GET /api/channels/{channel_id}
Get details for a specific channel.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description channel_idstring Slack channel ID -
Response: Channel dict
POST /api/channels/sync
Force sync all channels from Slack.
- Auth: Yes (hosted mode)
- Response:
POST /api/channels/{channel_id}/sync
Sync a single channel from Slack.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description channel_idstring Slack channel ID -
Response:
GET /api/channels/{channel_id}/members
List all members of a channel with their roles.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description channel_idstring Slack channel ID -
Response:
List[ChannelMembershipResponse]
PUT /api/channels/{channel_id}/members/{user_id}
Update role and responsibilities for a channel member.
-
Auth: Yes (hosted mode)
-
Path parameters:
Parameter Type Description channel_idstring Slack channel ID user_idstring User ID -
Request body (all fields optional):
-
Response:
Team Management
Route prefix: /team. Registered in app/routers/team.py.
Only active when DEPLOYMENT_MODE=hosted. Returns 404 in self-hosted mode.
POST /team/invite
Invite a new team member. Enforces tier-based user limits (MAX_USERS).
- Auth: Yes (hosted mode)
- Request body:
roleis optional (default:member). - Response:
GET /team/info
Return team size and tier information.
- Auth: Yes (hosted mode)
- Response:
Standalone Services
These are separate FastAPI applications that run independently from the main API.
Webhook Ingestor (port 8001)
Standalone webhook receiver. Run via app/webhook/ingestor.py.
| Method | Path | Description |
|---|---|---|
POST | /webhook/{webhook_type} | Receive and queue webhooks |
GET | /health | Health check |
GET | /stats | Webhook processing statistics |
GET | / | Service information |
Slack Handler (port 8002)
Standalone Slack event handler. Run via app/slack/handler.py.
| Method | Path | Description |
|---|---|---|
POST | /slack/command | Handle Slack /otto slash command |
POST | /slack/interactive | Handle Slack interactive components |
GET | /slack/health | Health check |
Auto-Generated Documentation
Available when DEPLOYMENT_MODE is not hosted:
| Path | Description |
|---|---|
/docs | Swagger UI (interactive API documentation) |
/redoc | ReDoc API documentation |
/openapi.json | OpenAPI schema JSON |
Endpoint Summary
Main Application (port 8000): 49 endpoints
| Category | Count | Prefix |
|---|---|---|
| Health and System | 5 | /, /health, /api/health, /api/version, /system/info |
| Tasks | 8 | /tasks, /task/{id}, /chat, /human_response, /worker/health |
| Users | 6 | /users |
| Files | 1 | /upload-file |
| Slack Integration | 3 | /slack |
| Webhooks | 4 | /webhook |
| MCP Status (legacy) | 1 | /mcp/status |
| MCP Configuration | 3 | /api/mcp |
| Skills | 10 | /api/skills |
| Channels | 6 | /api/channels |
| Team Management | 2 | /team |
Standalone Services: 7 endpoints
| Service | Count | Port |
|---|---|---|
| Webhook Ingestor | 4 | 8001 |
| Slack Handler | 3 | 8002 |