Streaming and change tracking
The MultiTaction Canvus REST API pushes changes to you over a long-lived HTTP response. Add ?subscribe to a GET request and the server keeps the connection open, sends the current state, then sends each change as it happens. This is the only change-notification mechanism the API has: there are no webhooks or other outbound calls, so your integration opens the connection and keeps it open.
Start a stream
curl -N -H "Private-Token: YOUR_TOKEN" \
"https://canvus.example.com/api/v1/canvases/CANVAS_ID/widgets?subscribe"
-N turns off curl's output buffering so you see each message as it arrives. Most GET endpoints that return a resource or a list accept ?subscribe; each reference page says whether its endpoint does.
Note
The server starts a stream whenever the query string contains subscribe, whatever value follows it. ?subscribe=false also streams. Leave the parameter out when you want a single response.
What the server sends
Messages are JSON, one per line, separated by newline characters.
- The first message is the full current state. For a list endpoint, such as
/canvases/{id}/widgetsor/canvases/{id}/notes, it is a JSON array of every item. For a single resource, such as/canvases/{id}/notes/{note_id}, it is one JSON object. - Each later message on a list stream contains only what changed. It is a JSON array holding the changed items, usually exactly one. It is not a refreshed copy of the whole list. Each item is the complete current object, not a patch of the changed fields.
- A deleted item arrives with
"state": "deleted". On a widget list stream, the deletion message carries onlyid,widget_typeandstate. - Each later message on a single-resource stream is the whole object. When the resource is deleted, the server sends it with
"state": "deleted"and closes the stream. - Empty lines are keepalives. After the first message, the server writes a newline at a fixed interval, 15 s by default. The server administrator sets the interval in whole seconds with
keepalivein the[rest-api]section ofmt-canvus-server.ini, and it takes effect after a server restart. The value must be 1 or more; see REST API streams. Ignore empty lines.
A list stream therefore needs a local copy that you update by id:
first message [ {note A}, {note B}, {note C} ] replace your copy
later message [ {note B, new text} ] update B
later message [ {note D} ] add D
later message [ {"id": "C", "state": "deleted"} ] remove C
A minimal reader in Python
This reader keeps a dictionary of the widgets on one canvas in step with the server, and reconnects when the connection drops. The widget list also carries the canvas background, which has no id, so the reader keys it by widget_type.
import json
import time
import requests
SERVER = "https://canvus.example.com"
TOKEN = "YOUR_TOKEN"
CANVAS = "CANVAS_ID"
CA_BUNDLE = True # or the path to your server's CA certificate
widgets = {} # key -> latest widget object
def key_of(item):
# The canvas background has no id; there is one per canvas.
return item.get("id") or item.get("widget_type")
def apply(message, first):
items = message if isinstance(message, list) else [message]
if first:
widgets.clear()
for item in items:
if item.get("state") == "deleted":
widgets.pop(key_of(item), None)
else:
widgets[key_of(item)] = item
on_change(item)
def on_change(item):
print(item.get("widget_type"), key_of(item), item.get("state"))
def follow():
url = f"{SERVER}/api/v1/canvases/{CANVAS}/widgets?subscribe"
headers = {"Private-Token": TOKEN}
while True:
try:
with requests.get(url, headers=headers, stream=True,
timeout=(10, 60), verify=CA_BUNDLE) as r:
r.raise_for_status()
first = True
for line in r.iter_lines():
if not line:
continue # keepalive
apply(json.loads(line), first)
first = False
except (requests.RequestException, ValueError) as e:
print("stream ended:", e)
time.sleep(2) # back off, then reconnect
follow()
The read timeout of 60 s is longer than the keepalive interval, so a silent but healthy stream does not time out, and a dead connection is noticed within a minute. If your server's keepalive interval has been raised, set the read timeout above it.
Reconnect safely
- The API has no resume token or sequence number. After a reconnect, the first message is again the full current state. Replace your local copy with it, as the reader above does.
- Changes made while you were disconnected are not replayed one by one. Compare the new snapshot with your old copy if you need to know what changed in between.
- Wait a second or two before reconnecting, and back off further if reconnects keep failing.
- A stream keeps its token valid for as long as it is open. A stream ends when its token is deleted, when its user is deleted, when the server restarts, or when the network drops.
- Once a stream has started, the HTTP status is already
200. A later error closes the connection instead of returning an error status, so treat any close as "reconnect and resynchronise".
Requests that run beside a stream
Ordinary requests have fixed deadlines. Canvus Server ends a request that has had no response headers 120 s after it was sent, and a request without a body that has not finished after 300 s, with 504 Gateway Timeout. Streams are exempt from the 300 s deadline, so a stream can stay open for days. A stream still has to send its first message within 120 s, which it normally does at once. See Limits and operations.
Recognise your own writes
Widgets carry no author or creator field, so a stream does not tell you who made a change. If your integration writes to a canvas it also follows, remember the id of every widget it creates and the time of every change it sends, and skip the stream messages that echo them. Without this, a bot that reacts to note changes can end up reacting to its own output.
Choose what to follow
/canvases/{id}/widgets?subscribefollows every widget on a canvas. Addannotations=1(?subscribe&annotations=1) to include pen strokes.- A typed list, such as
/canvases/{id}/notes?subscribe, follows one widget type and sends less traffic. /canvases?subscribefollows the canvases your account can see, which tells you when canvases are created, renamed or deleted./clients?subscribefollows the Canvus clients connected to the server.
The API has no server-side search or filter on canvas and widget lists. To react only to certain items, such as notes with a given title or colour, filter in your own code.
Debounce edits
A note can change many times while someone is still editing it, and each change arrives as its own message. If you act on note text, wait until a note has been quiet for a few seconds before you process it.