Collaboration โ Presence & Comments
Tags: Presence, Comments ยท Version: v1 ยท Stability: ๐ก Beta
Real-time-ish collaboration without full co-editing: see who's looking at a template, and discuss it inline with @mentions. Both are JWT-only and workspace-scoped.
These endpoints are ๐ก Beta โ functionally complete and shipped end-to-end, but newer than the core surface and may still gain response fields within v1. Pin to the documented fields.
Presenceโ
Presence is poll-based: clients send a heartbeat while viewing a resource and fetch the current viewers. The designer itself uses the WebSocket collab hub below; this HTTP presence API remains for surfaces without a live document (the templates list).
Send a heartbeatโ
POST /api/presence ยท JWT
{ "resourceType": "template", "resourceId": "3f6bโฆuuid" }
Both fields required (resourceId is a UUID). Call periodically while a user
views the resource to keep them shown as present.
Get current viewersโ
GET /api/presence/{resourceType}/{resourceId} ยท JWT โ who is currently viewing
this resource (e.g. "Sarah is viewing this template").
Clear presenceโ
DELETE /api/presence/{resourceType}/{resourceId} ยท JWT โ explicitly mark the
user as no longer viewing (e.g. on navigate-away).
{resourceType} is a string (e.g. template); {resourceId} is a UUID.
Commentsโ
Threaded discussion attached to a template, with @mention resolution.
List commentsโ
GET /api/templates/{templateId}/comments ยท JWT โ {templateId} is a UUID.
Add a commentโ
POST /api/templates/{templateId}/comments ยท JWT
{ "body": "Can we move the logo up? cc @sarah" }
body is required. @mentions are resolved to workspace members.
Delete a commentโ
DELETE /api/templates/{templateId}/comments/{commentId} ยท JWT
{commentId} is a UUID. Allowed for the comment author or a workspace
Owner โ otherwise 403 Forbidden.
Relatedโ
- Templates โ comments hang off a template.
- Workspaces & Invites โ who can be @mentioned.
Live collaboration (Yjs over SignalR) ๐ขโ
Saved templates are edited as one shared CRDT document. The browser connects to
/hubs/collab (SignalR, MessagePack protocol required, JWT passed as
?access_token=) and joins doc:{templateId}.
| Hub method | Direction | Purpose |
|---|---|---|
JoinDocument(docId, awarenessClientId) | client โ server, returns {snapshot, snapshotSeq, updates[], peerAwareness[], canEdit} | Durable state to converge on; canEdit is false for Viewers |
SendUpdate(docId, bytes) โ seq | client โ server | One Yjs update; persisted before fan-out |
ReceiveUpdate(seq, bytes) | server โ others | A peer's update |
SendAwareness(docId, bytes) / ReceiveAwareness(bytes) | both | Cursors, selection, interaction lock, drag ghost (never persisted) |
PeerLeft(clientId) | server โ others | A peer disconnected |
Compaction rides the ordinary save: PUT /api/templates/{id} may carry yState
(base64) and yStateSeq; the server keeps one compacted snapshot per template
and drops covered log rows. Viewers may join and watch but cannot write.
Version history ๐ขโ
| Method | Path | Notes |
|---|---|---|
| GET | /api/templates/{id}/versions?limit=50&before= | Newest first; kind is auto / manual / restore |
| GET | /api/templates/{id}/versions/{versionId} | Includes content |
| POST | /api/templates/{id}/versions { label } | Name the current saved state |
| PATCH | /api/templates/{id}/versions/{versionId} { label } | Rename |
A version is recorded on every successful save (identical content is skipped,
rapid saves by the same author coalesce for 2 minutes; auto versions are thinned
to hourly after 24h and daily after 30 days, capped at 200). Restore happens in
the designer as a forward edit into the live document followed by a save with
versionKind: "restore", so every collaborator converges and the restore itself
can be undone.