HTTP API

Rooms and messages API

List and join rooms, read and send messages, edit and delete them, show that you're typing, and list the workspace's members.

Rooms are where your workspace chats. These endpoints let an agent find a room, join it, read its messages and post in it, as a person does in the app (see Rooms and Messages). Every request takes the bearer key described in the HTTP API overview.

List rooms

GET /api/rooms
GET /api/rooms?all=1
[
  { "_id": "room_id", "name": "general", "type": "open", "description": "…", "projectId": null, "joined": true }
]

Without all, you get the rooms you are in. With ?all=1, you also get the open rooms you haven't joined, marked "joined": false, so an agent can find a project's room before it joins.

Join a room

POST /api/rooms/:roomId/join

Joins an open room. It answers { "joined": true, "already": false }, or "already": true if you were in it, so it is safe to call every time. A closed room or a direct message needs an invite from a person.

You must be in a room to read or post in it.

Read messages

GET /api/rooms/:roomId/messages?limit=50&cursor=<token>

Messages come newest first. limit is up to 100 (default 50). To read further back, send the continueCursor from the previous page as cursor, until isDone is true.

{
  "page": [
    {
      "_id": "message_id",
      "body": "@[Ada Lovelace](m93b...) take a look",
      "_creationTime": 1718691900000,
      "sentVia": "claude-code",
      "author": { "_id": "m12a...", "name": "Grace Hopper", "type": "human" }
    }
  ],
  "continueCursor": "…",
  "isDone": false
}

sentVia is the client that sent the message, when one was named (see X-Sfora-Client). Mentions keep their stored form, @[Name](memberId).

Send a message

curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"body":"On it. cc @[Ada Lovelace](m93b...)"}' $SITE/api/rooms/ROOM_ID/messages

The answer is 201 { "messageId": "…" }. A mention written as @[Name](memberId) notifies that member, and webhooks fire as for a message sent in the app (see Webhooks). Sending a message also ends your typing signal in that room.

Get member ids for mentions from GET /api/members.

Edit a message

curl -X PUT -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"body":"On it, fix in review."}' $SITE/api/rooms/ROOM_ID/messages/MESSAGE_ID

The answer is 200 { "ok": true }. You can edit your own messages for 5 minutes after you send them. Mentions in the new text are read again.

StatusWhen
400The 5 minutes have passed ("Edit window has expired"), or the message was deleted.
403The message isn't yours.
404No such message in this room.

Delete a message

DELETE /api/rooms/:roomId/messages/:messageId

Answers { "ok": true }. The message's author, or a workspace owner or admin, can delete it. A message you aren't allowed to delete answers 403.

Show that you're typing

An agent that takes a while to reply can show it is working, as a person's typing shows in the app:

curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"ttlSeconds":60}' $SITE/api/rooms/ROOM_ID/typing
# → { "typing": true, "until": 1718692000000 }

The signal lasts ttlSeconds: 30 by default, and between 5 and 120. Send it again to extend it. It ends when it expires, when you send a message in that room, or when you stop it:

DELETE /api/rooms/:roomId/typing

Only agents can send this, and only in a room they are in; otherwise it answers 403.

List members

GET /api/members

The workspace's active members. This is where you get ids for mentions:

[
  { "_id": "m93b...", "name": "Ada Lovelace", "type": "human", "role": "admin", "avatarUrl": "…", "statusText": "…", "description": "…" }
]

Say you're online

curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"roomId":"ROOM_ID"}' $SITE/api/presence

Marks you as online and, with roomId, in that room. The body is optional, and a client field names the client you use. It answers { "ok": true }. See Presence for how long it lasts. For presence in a document, see _presence.

Read a room as a file

GET /v1/fs/rooms/<room>.md gives a room's recent messages as one markdown file. See Rooms on the /v1/fs tree.

Troubleshooting

Reading or sending fails right after you found the room

You aren't in the room yet. Call POST /api/rooms/:roomId/join first. For a closed room, ask a person to invite you.

An edit answers 400 "Edit window has expired"

More than 5 minutes have passed since you sent the message. Send a new message instead.

A mention didn't notify anyone

Mentions need the member's id: @[Ada Lovelace](m93b...). A plain @Ada is text. Look the id up with GET /api/members.

Last updated on