Skip to content

Live & Sessions

Livestreaming, and the booking flow for 1-on-1 sessions. All endpoints require authentication.

Livestreams

MethodPathPurpose
GET/livestreams/credentialsGet (or lazily create) your Mux stream credentials
POST/livestreams/credentials/regenerateRotate the stream key
POST/livestreamsCreate a livestream record
POST/livestreams/:id/enableEnable the Mux live stream
POST/livestreams/:id/endEnd it
GET/livestreams/:idFetch a livestream (with playback id)
GET/livestreams/:id/messagesChat history
POST/livestreams/:id/messagesSend a chat message

Credentials exist from registration

registerUser creates a LiveStream record with Mux credentials, so GET /livestreams/credentials normally returns an existing record rather than provisioning one.

json
{
  "id": "clx…",
  "streamKey": "…",
  "muxPlaybackId": "…",
  "muxLivestreamId": "…",
  "status": "idle"
}

The creator publishes RTMP to Mux with streamKey; viewers play HLS using muxPlaybackId.

streamKey is a credential

Anyone holding it can broadcast as that creator. Never log it, never include it in a response meant for anyone but the owner, and rotate it with /livestreams/credentials/regenerate if it leaks.

Status is webhook-driven

LiveStream.status and User.isLive are set by Mux webhooks (video.live_stream.active / .idle), not by the client. Do not set them from the app. See Livestreaming.

Chat

Livestream chat is persisted as LiveStreamMessage rows, so late joiners see history. Coin gifting during a stream produces the CREATOR_REDEEM ledger entries that feed creator payouts.

Sessions (e-dates)

MethodPathPurpose
POST/sessionsBook a session
GET/sessions/mineYour sessions
GET/sessions/:idOne session
PATCH/sessions/:id/cancelCancel

POST /sessions

ts
{
  playpalId: string,           // the creator being booked
  startDate: string,           // ISO 8601 datetime
  endDate: string,             // ISO 8601 datetime
  duration: number,            // positive integer
  notes?: string,              // max 2000 chars
}

Returns 201 with the created session. The purchaser is the authenticated user. A privateChannelRoom is associated with the session, and only the two parties can obtain a LiveKit token for it.

GET /sessions/mine

ParameterValuesMeaning
roleplaypal | purchaserWhich side of the booking
statusupcoming | pastTime filter
cursoropaquePagination
limitnumberPage size

Note that status here is a time filter, distinct from Session.status (scheduled / active / completed / cancelled).

Cancellation

PATCH /sessions/:id/cancel sets status to cancelled and stamps cancelledAt, and fires a SESSION_CANCELLED notification. Refund handling is not implemented — the payment was collected by RevenueCat and there is no compensating flow. Planned

Availability

Creators publish when they can be booked.

Working hours

MethodPathPurpose
GET/availabilityYour working hours, formatted
GET/availability/rawRaw rows
PUT/availabilityReplace the whole schedule
PATCH/availability/dayUpdate one day

Blocked dates

MethodPathPurpose
GET/availability/blocked-datesList blocked dates
POST/availability/block-dateBlock a specific date
DELETE/availability/block-dateUnblock

Recurring hours and one-off blocked dates share the Availability model — recurring rows use dayOfWeek + startTime + endTime, blocked rows use date + blocked: true.

Checking availability

MethodPathPurpose
GET/availability/checkIs you free at a time?
GET/users/:userId/availabilityAnother user's public availability
GET/users/:userId/availability/checkIs they free at a time?

The booking flow calls /users/:userId/availability to render the calendar, then /users/:userId/availability/check before submitting.

No timezone handling

startTime and endTime are stored as strings ("09:00") with no timezone column. A creator in Los Angeles and a fan in Berlin will disagree about when "09:00" is. Fix this before sessions cross regions — it needs a timezone on User and conversion at both read and write.

Internal documentation — PlayPalz platform