Skip to content

Presence + Comments API

Real-time collaboration on a Story: see who's editing right now, leave threaded comments on individual blocks, resolve discussions.

Presence

Heartbeat-based — clients ping every 15s with their cursor focus.

http
POST   /v1/spaces/{slug}/stories/{uuid}/presence
GET    /v1/spaces/{slug}/stories/{uuid}/presence
DELETE /v1/spaces/{slug}/stories/{uuid}/presence/{session_id}

POST body:

json
{
  "session_id": "client-uuid-v4",
  "block_path": "blocks/2/headline",
  "selection": { "anchor": 12, "head": 30 }
}

GET returns active sessions (last heartbeat ≤ 30s ago):

json
{
  "data": [
    {
      "session_id": "abc",
      "user_id": 4,
      "user_name": "Lisa",
      "block_path": "blocks/2/headline",
      "last_seen": "2026-05-27T19:00:00Z"
    }
  ]
}

Cleanup

DELETE is a courtesy — sessions older than 5 minutes are pruned by a background worker regardless.

Comments

Block-anchored threads.

http
GET    /v1/spaces/{slug}/stories/{uuid}/comments
POST   /v1/spaces/{slug}/stories/{uuid}/comments
PATCH  /v1/spaces/{slug}/stories/{uuid}/comments/{id}
DELETE /v1/spaces/{slug}/stories/{uuid}/comments/{id}
POST   /v1/spaces/{slug}/stories/{uuid}/comments/{id}/resolve

POST body:

json
{
  "block_path": "blocks/2",
  "body": "Headline reads more like a question than a statement?",
  "parent_id": null
}

Threads form a single-level tree (no nested nesting) — parent_id points at the root comment of the thread.

Resolved comments stay queryable but are hidden in the editor by default. Re-open with POST .../{id}/resolve again (toggles).

RBAC

RoleCan do
viewerread presence + comments
editor+ post comments, edit/delete own comments
admin+ edit/delete any comment, resolve any thread

See also