Gateway API

The HTTP surface the frontend talks to.

The HTTP surface. Mount it in your own FastAPI app or serve it directly.

python -m runspace.workspace.serve workspace.yml

For one workspace, app.include_router(gateway.router). For many, resolved by Host header:

from fastapi import FastAPI
from runspace.workspace.backend import WorkspaceRegistry

app = FastAPI()
registry = WorkspaceRegistry.from_tenants_dir("tenants/")
registry.mount(app)          # every tenant, every route, dispatched per request

All paths below are relative to the /api/workspace prefix.

Chat

POST /chatOne request, one reply. Body: app_id, message, session_id
POST /chat/streamThe same turn as server-sent events — tool progress, partial text, then the final payload
GET /chat/historyPast turns for a session
DELETE /chat/historyClear a session

Channels and messages

GET /channelsEvery channel in the workspace
POST /channelsCreate one — name and slug required
GET /channels/{slug}/messagesHistory, oldest first
POST /channels/{slug}/messagesPost. Dispatches any @mention unless you pass dispatch: false
POST /channels/{slug}/readMark read for one member
GET /unreadUnread counts per channel for a member
GET /channels/{slug}/threads/{thread_id}One thread's replies
PATCH /messages/{message_id}Edit; records when
DELETE /messages/{message_id}Soft delete — hidden from reads, kept for audit
POST /messages/{message_id}/reactionsToggle an emoji for one member

A client that streams the turn itself must pass dispatch: false when posting, or the agent answers twice — once from the mention dispatch here and once from its own /chat/stream call.

Members

GET /channels/{slug}/membersAgents and people in a channel
POST /channels/{slug}/membersAdd, or update a role
DELETE /channels/{slug}/members/{member_type}/{member_id}Remove
GET /usersEveryone @-mentionable in this workspace

Routines

GET /routinesAll scheduled work, with next run
POST /routinesCreate — agent_id, schedule, prompt
PATCH /routines/{routine_id}Change schedule, prompt, or enabled
DELETE /routines/{routine_id}Remove
POST /routines/{routine_id}/runRun now, without waiting for the schedule

Writes go to routines.yml, so the file stays the source of truth and a routine created through the API is one you can read in a diff.

External channels

GET /telegram-botsBots this workspace answers on
POST /telegram-botsRegister one
PATCH /telegram-bots/{bot_name}Change its settings
DELETE /telegram-bots/{bot_name}Remove it
GET /discovered-chatsGroups a bot has been added to
DELETE /discovered-chats/{bot_name}/{chat_id}Forget one
GET /pairingsPending DM pairing requests
POST /pairings/{code}/approveApprove a request
POST /pairings/{sender_id}/revokeRevoke access
GET /external-channelsBindings between a channel and an outside provider
POST /external-channelsCreate a binding
DELETE /external-channels/{binding_id}Remove one

Pairing exists so a bot is not open to anyone who finds it: a stranger gets a code, and somebody approves it before their messages reach an agent.

Files and configuration

POST /uploadAttachment upload, with extraction
GET /files/{file_id}Fetch one back
GET /configThe workspace as the frontend sees it
GET /appsAgents, with persona and group
GET /activityAudit feed of tool calls and replies
POST /reload-config · POST /agents/{agent_id}/reloadRe-read workspace.yml without a restart