Riding together
Four features share one realtime plane: seeing where each other are, riding as a
convoy, talking, and getting help when something goes wrong. They are built on
the /live and /sos endpoints and a single WebSocket.
Sharing your location
Sharing is opt-in, per session, and to a chosen audience — it is not a setting you leave on.
| Step | Endpoint |
|---|---|
| Start sharing | POST /live/share/start |
| Choose who sees you | POST /live/share/audience |
| Check what is currently shared | GET /live/share/status |
| Stop | POST /live/share/stop |
Your audience is built from friends and sharing groups. Friendship is a
two-sided request — POST /live/friends/request, then the other rider
accepts (/live/friends/accept) or declines. Groups work the same way: create
one with POST /live/groups, invite with POST /live/groups/{gid}/invite, and
the invitee accepts or declines. Nobody appears in your audience because you
typed their name.
Public links
For someone who has no account — a partner following the ride from home — create an expiring link:
POST /live/share/link
The returned token is the credential. GET /live/view/{token} renders a map
page and needs no sign-in; GET /live/public/{token} and
/live/public/{token}/peers back it with data. An expired or unknown token
answers 410, not 404, so the viewer can tell "this link has ended" from "this
link never existed". Revoke early with DELETE /live/share/links/{token}, and
list what you have handed out with GET /live/share/links.
The realtime stream
Positions move over a WebSocket rather than polling, so a peer's marker moves as they move rather than jumping every thirty seconds. When the app is backgrounded it falls back to sending positions over HTTP, and the experience for everyone watching is the same.
The stream is deliberately forgiving in one direction and strict in the other: a malformed frame is ignored rather than tearing down the connection, and a client sending far too fast is throttled before it is disconnected. A dropped frame is cheaper than a dropped rider.
:::info Building your own client? The full protocol — frame types, close codes, reconnect semantics — is in the developer documentation. :::
Group ride
A group ride turns a sharing group into a convoy with a leader and a shared route.
POST /live/ride
The caller becomes leader, their route becomes the shared route, and every other group member receives a ride invite. From there:
| Action | Endpoint |
|---|---|
| Join an invited ride | POST /live/ride/{gid}/join |
| Update the shared route | POST /live/ride/{gid}/route |
| Hand over leadership | POST /live/ride/{gid}/leader |
| Decline leadership | POST /live/ride/{gid}/leader/decline |
| Stay visible | POST /live/ride/{gid}/heartbeat |
| Leave | POST /live/ride/{gid}/leave |
| End for everyone | POST /live/ride/{gid}/end |
GET /live/rides lists the rides you can join; GET /live/ride/{gid} returns
one. Convoy size is capped by GROUP_RIDE_MAX, which the deployment sets.
Talking
Two modes, deliberately different.
Push-to-talk is a short clip fanned out to the group — everyone except the
speaker — and nothing is stored. POST /live/ptt sends one; recipients get
it as a ptt WebSocket event. POST /live/ptt/floor claims the floor so two
riders do not talk over each other.
Full-duplex calls run over a self-hosted LiveKit SFU rather than the WebSocket. The lifecycle is explicit so a phone can ring properly:
ring → (joined | declined | cancelled) → left
POST /live/voice/ring rings a friend or every group member; offline riders
simply have no socket, so the fan-out is a quiet no-op rather than an error —
there is no presence oracle to consult. POST /live/voice/token mints the
LiveKit room token. GET /live/voice/active and GET /live/voice/calls report
what is in progress. Ringing is rate-limited per caller.
SOS
If a crash is detected — or you trigger it yourself — POST /sos/alert raises an
incident. Everyone in your convoy is notified, and your emergency contacts are
alerted through the channels the deployment has configured (push, Telegram, and
SMS where an SMS_PROVIDER is set).
| Manage contacts | PUT /sos/contacts, GET /sos/contacts |
| Suggest contacts from your friends | GET /sos/contacts/candidates |
| Invite someone to be a contact | POST /sos/contacts/invite |
| Link a contact's Telegram | POST /sos/contacts/telegram-link |
| Acknowledge an incident | POST /sos/{incident_id}/ack |
| Close it | POST /sos/{incident_id}/resolve |
| History | GET /sos/incidents |
Acknowledgement matters: an unacknowledged alert escalates, so a contact tapping I'm on it changes what happens next rather than just marking it read.
:::caution Not a substitute for emergency services SOS alerts people who chose to be your contacts. It does not call an ambulance. :::
Where to look next
Every endpoint above, with request and response shapes, is in the API reference under the live and sos tags — available to developer subscribers.