temetro is in beta and under active development. Expect things to change.
temetro

Meetings

Staff meeting rooms and the WebRTC call signaling events.

Meeting rooms are persistent staff voice/video channels, scoped to the active clinic. Any clinic member can list, create, join, and delete rooms — calls are staff-to-staff. The REST endpoints manage only the room list; the live call (participants + media) is handled over the realtime Socket.io connection and is never persisted.

MethodPathPurpose
GET/api/meetingsList the clinic's rooms
POST/api/meetingsCreate a room
DELETE/api/meetings/{id}Delete a room
GET/api/meetings/eventsScheduled meetings you're part of
POST/api/meetings/eventsSchedule a meeting
DELETE/api/meetings/events/{id}Delete a scheduled meeting (creator only)

All endpoints require a signed-in member with an active clinic.

Endpoints

Create a room

FieldTypeNotes
namestringRequired. 1–80 chars
curl -b cookies.txt -X POST http://localhost:4000/api/meetings \
  -H "Content-Type: application/json" \
  -d '{ "name": "Morning huddle" }'

Returns 201 with { id, name, createdAt }.

Schedule a meeting

POST /api/meetings/events creates a calendar entry; GET /api/meetings/events returns the meetings where you are the creator or an invited participant (with participant display names resolved). The creator is always added as a participant.

FieldTypeNotes
titlestringRequired. 1–120 chars
datestringRequired. YYYY-MM-DD
timestringRequired. HH:mm
participantsstring[]Invited staff user ids (max 50)

Realtime call signaling

Calls use a WebRTC mesh: every participant holds one peer connection to each other participant, and media flows peer-to-peer. The server only relays SDP/ICE over the authenticated Socket.io connection and tracks who is in a room. A room is capped at 4 participants (mesh degrades beyond that; 5+ would need an SFU).

EventDirectionPayload
call:joinclient → serverroomId (ack returns { ok, peers } or { ok: false, reason: "full" | "not_found" })
call:peer-joinedserver → clients{ socketId, userId, userName }
call:signalboth{ to, signal } relayed as { from, signal }signal carries an SDP offer/answer or an ICE candidate
call:leaveclient → serverroomId
call:peer-leftserver → clients{ socketId }
call:inviteclient → server{ roomId, toUserId } — rings a member
call:inviteserver → client{ roomId, roomName, fromName } — relayed to the invitee (also a meeting notification)
call:presenceserver → clinic{ roomId, count } — live room occupancy

On call:join the newcomer receives the peers already present and initiates an offer to each; existing peers answer. Disconnects emit call:peer-left to the room automatically. Invites ring a member into a room (a toast with a Join action, plus a bell notification); presence broadcasts let the room list show how many people are in each call.

Cross-network calls (symmetric NAT) also need a TURN server — a deployment add-on. The bundled signaling uses public STUN, which covers same-network and simple-NAT setups.

On this page