Open Morse

Reference

Hosting

17 routes, generated from Morse’s own OpenAPI document. Every path below hangs off the base URL, and every one needs the bearer header.

The examples are built from each route’s schema, so the shapes and types are exactly what the API declares. The values are illustrative, and no one has run them.

Say you have switched to a camera pointed at your desk, so your tile says so rather than looking like it has frozen.

POST/meetings/{meeting_id}/desk-view

Through us for the reason my_wide_camera is.

POST/meetings/{meeting_id}/desk-view

curl -X POST "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/desk-view" \
  -H "Authorization: Bearer $MORSE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "on": true }'

Raise or lower your own hand.

POST/meetings/{meeting_id}/hand

Through us rather than from the room: a token that could write its own hand could also rename itself on every tile (livekit.set_hand). Only someone in the room has a hand to raise.

POST/meetings/{meeting_id}/hand

curl -X POST "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/hand" \
  -H "Authorization: Bearer $MORSE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "raised": true }'

Two topics, one connection.

GET/meetings/{meeting_id}/lobby-events

Who is at the door, and how the room is configured — different questions, but a second EventSource to answer the second one would be a second connection to keep alive for no gain.

GET/meetings/{meeting_id}/lobby-events

curl "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/lobby-events" \
  -H "Authorization: Bearer $MORSE_TOKEN"

Locked means even colleagues have to ask.

POST/meetings/{meeting_id}/lock

Guests always knock regardless, so this only changes the internal path.

POST/meetings/{meeting_id}/lock

curl -X POST "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/lock" \
  -H "Authorization: Bearer $MORSE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "locked": true }'
200
{
  "locked": true
}

Soft mute: everyone can unmute themselves again.

POST/meetings/{meeting_id}/mute-all

A hard mute would need tokens reissued with can_publish=False, which is a Phase 3 concern.

POST/meetings/{meeting_id}/mute-all

curl -X POST "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/mute-all" \
  -H "Authorization: Bearer $MORSE_TOKEN"

A member's own admission channel.

GET/meetings/{meeting_id}/my-lobby-events

No in-room requirement — the whole point is that they are not in the room yet — and it is scoped to their own user id, so it reveals nothing about anyone else.

GET/meetings/{meeting_id}/my-lobby-events

curl "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/my-lobby-events" \
  -H "Authorization: Bearer $MORSE_TOKEN"

Who is in the room, and how they got there.

GET/meetings/{meeting_id}/participants

LiveKit knows identities and names; it does not know who is the host, who is a guest, or who let them in. admitted_by is recorded precisely so admission is never anonymous, and this is what surfaces it. Every status the people panel sections by, and no more. left is absent deliberately: someone who left is simply not here, and there is no section for them. denied and removed are present because re-inviting is how a misclick is undone, and that needs a row to act on.

GET/meetings/{meeting_id}/participants

curl "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/participants" \
  -H "Authorization: Bearer $MORSE_TOKEN"
200
[
  {
    "admitted_by_name": "Priya Shah",
    "display_name": "Priya Shah",
    "email": "priya@neuralarc.ai",
    "id": "3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60",
    "identity": "…",
    "invited_by_name": "Priya Shah",
    "kind": "…",
    "meeting_role": "…",
    "provenance": "…",
    "status": "…"
  }
]

Let someone waiting in the lobby into the meeting.

POST/meetings/{meeting_id}/participants/{participant_id}/admit

Anyone in the room may.

POST/meetings/{meeting_id}/participants/{participant_id}/admit

curl -X POST "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/participants/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/admit" \
  -H "Authorization: Bearer $MORSE_TOKEN"

Turn away someone waiting in the lobby; they may ask again.

POST/meetings/{meeting_id}/participants/{participant_id}/deny

Anyone in the room may.

POST/meetings/{meeting_id}/participants/{participant_id}/deny

curl -X POST "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/participants/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/deny" \
  -H "Authorization: Bearer $MORSE_TOKEN"

Lower someone else's raised hand.

POST/meetings/{meeting_id}/participants/{participant_id}/lower-hand

Host only.

POST/meetings/{meeting_id}/participants/{participant_id}/lower-hand

curl -X POST "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/participants/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/lower-hand" \
  -H "Authorization: Bearer $MORSE_TOKEN"

Mute someone's microphone in the meeting.

POST/meetings/{meeting_id}/participants/{participant_id}/mute

Host only.

POST/meetings/{meeting_id}/participants/{participant_id}/mute

curl -X POST "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/participants/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/mute" \
  -H "Authorization: Bearer $MORSE_TOKEN"

Remove someone from the meeting for good: they can't come back into it.

POST/meetings/{meeting_id}/participants/{participant_id}/remove

Host only.

POST/meetings/{meeting_id}/participants/{participant_id}/remove

curl -X POST "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/participants/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/remove" \
  -H "Authorization: Bearer $MORSE_TOKEN"

A colleague in the room asking the host to let them present.

POST/meetings/{meeting_id}/share-requests

In-room, not host: asking is not a host power. The guest half of this lives in guests.py, because a guest authenticates with a launch token and has no CurrentUser to be.

POST/meetings/{meeting_id}/share-requests

curl -X POST "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/share-requests" \
  -H "Authorization: Bearer $MORSE_TOKEN"

Host-only, unlike admitting. Admitting is any member on purpose — nobody should stand at the door while the host runs late — but who may present was asked for as the host's call, and a request costs the room nothing while it waits.

POST/meetings/{meeting_id}/share-requests/{identity}

Granting is LiveKit's answer, not ours, so it is done *before* anyone is told. Telling the room first and then failing would leave someone pressing Present against a server still refusing them. Refusing grants nothing and revokes nothing: a request is answered from a standing start, so "Not now" simply leaves the person as they were.

POST/meetings/{meeting_id}/share-requests/{identity}

curl -X POST "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/share-requests/…" \
  -H "Authorization: Bearer $MORSE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "allow": true }'

The host's switch for whether anyone may present without asking.

POST/meetings/{meeting_id}/sharing-open

Host-only, like the lock and unlike admitting: it decides how the room is configured rather than answering one person's request. The flag alone would only reach the next person to join — permission lives on the LiveKit participant, minted on arrival — so the room is walked too. Turning it off withdraws the share from everyone but the host, which will end a colleague's share in progress. That is the point of the switch; a host closing sharing while a screen is up means that screen.

POST/meetings/{meeting_id}/sharing-open

curl -X POST "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/sharing-open" \
  -H "Authorization: Bearer $MORSE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "anyone_can_share": true }'
200
{
  "anyone_can_share": true
}

Who is waiting in the lobby to be let in.

GET/meetings/{meeting_id}/waiting

Anyone in the room may ask.

GET/meetings/{meeting_id}/waiting

curl "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/waiting" \
  -H "Authorization: Bearer $MORSE_TOKEN"
200
[
  {
    "display_name": "Priya Shah",
    "email": "priya@neuralarc.ai",
    "id": "3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60",
    "invited_by_name": "Priya Shah",
    "kind": "…",
    "provenance": "…"
  }
]

Say your camera is a wide or 360 one, so every tile shows its whole frame (2026-10-01).

POST/meetings/{meeting_id}/wide-camera

Through us for the reason my_hand is.

POST/meetings/{meeting_id}/wide-camera

curl -X POST "$MORSE/meetings/3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60/wide-camera" \
  -H "Authorization: Bearer $MORSE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "on": true }'