GET/BOAR
Health check used before the app saves a custom server host.
Response
I am BOAR!
The body must equal I am BOAR! after trimming whitespace.
The app stores only a host name, then builds requests as https://{host}/{path}.
Custom servers must be reachable over HTTPS at the root of the host. A path prefix such as
https://example.com/boar/ is not supported by the app.
null..m4a is recommended./BOARHealth check used before the app saves a custom server host.
I am BOAR!
The body must equal I am BOAR! after trimming whitespace.
BOAR does not require a specific backend implementation. A compatible server may proxy YouTube Music, redirect to YouTube Music, or produce compatible JSON itself. What matters is the request and response shape seen by the app.
/v1/searchSearches artists or songs. The app sends a YouTube Music InnerTube-style JSON request.
{
"context": {
"client": {
"clientName": "WEB_REMIX",
"clientVersion": "1.20251111.09.00"
}
},
"query": "search text",
"params": "..."
}
Return a successful YouTube Music search-compatible JSON response. A 307
redirect that preserves the original POST body is also compatible if the final response is
the YouTube Music JSON.
/v1/browseFetches artist pages, album pages, release shelves, and lyrics pages.
{
"context": {
"client": {
"clientName": "WEB_REMIX",
"clientVersion": "1.20251111.09.00"
}
},
"browseId": "MPREb_...",
"params": "optional"
}
Return a successful YouTube Music browse-compatible JSON response for the requested
browseId. The app extracts artist metadata, album lists, album tracks, video
IDs, thumbnails, and lyrics from this response.
/playlist/{playlist_id}Returns lightweight metadata for a YouTube playlist without returning audio bytes.
{
"playlist_id": "PL...",
"entries": [
{
"id": "youtubeVideoId",
"title": "Song title",
"channel": "Artist or channel",
"thumbnail": "https://..."
}
],
"count": 1,
"queued": false,
"cached": false
}
playlist_id and entries are required. Each entry must include
id. The app can tolerate missing title, channel,
and thumbnail, but providing them gives better imported metadata.
/queue/{playlist_id}
Optional preload hint. The app calls this after opening an album when a playlist ID is
known. Return 200 with any JSON body.
{
"playlist_id": "PL...",
"queued": true
}
/{playlist_id}/{index}
Returns the playable audio file for a playlist entry. index is zero-based and
matches the app's album track order.
Audio file bytes with a successful HTTP status.
/video/{video_id}Returns the playable audio file for a single YouTube video ID.
Audio file bytes with a successful HTTP status.
/cache/has?video_id={video_id}
Tells the app whether server-side audio for a video ID can be reused in a listening room.
If your server does not support reusable cached audio, return {"cached": false}.
{"cached": true}
/cover/has?video_id={video_id}&cover_hash={sha1}
Tells the app whether a cover image is already available on the server. At least one query
parameter should be accepted. If your server does not support reusable cached covers, return
{"cached": false}.
{"cached": true}
Listening rooms use server-authoritative playback state. The app joins a room, receives a
member_id, uploads or references tracks, repeatedly syncs room state, and
downloads room audio from URLs supplied by the server.
member_id on join.member_id is missing or not in the room.RoomSnapshot from state-changing room endpoints unless documented otherwise.state.track_id aligned with queue[state.queue_index] whenever the queue is non-empty.state.version when playback state, membership, tracks, queue, or chat changes.server_time in snapshots when possible so clients can estimate clock offset./roomsLists public active rooms.
{"rooms": [RoomSummary]}
/rooms/version/check
Checks whether the current app build may use listening rooms. To allow the app, return
ok: true.
{"build": 42}
{
"ok": true,
"build": 42,
"min_build": null
}
/rooms/version
Legacy fallback used only if /rooms/version/check is unavailable or unusable.
Implementing /rooms/version/check is enough for current app builds.
{"min_version": "1.0"}
/roomsCreates a room.
{
"name": "Friday Night",
"public": false
}
{
"code": "123456",
"name": "Friday Night",
"public": false
}
/rooms/{code}/joinJoins a room and returns the current snapshot plus the caller's member_id.
{"member_name": "Listener"}
RoomSnapshot with "member_id"
/rooms/{code}/leaveLeaves a room.
{"member_id": "member-id"}
{
"left": true,
"room_deleted": false
}
/rooms/{code}/heartbeatRefreshes member presence without returning a full snapshot.
{"member_id": "member-id"}
{"ok": true}
/rooms/{code}/sync?member_id={member_id}&since={version}
Returns the authoritative room snapshot. member_id and since may
be absent, but if member_id is present it should refresh member presence.
RoomSnapshot
/rooms/{code}/tracksReturns room track metadata and queue order.
{
"tracks": [RoomTrack],
"queue": ["track-id"]
}
/rooms/{code}/upload
Uploads one playable audio file to a room. This endpoint uses
multipart/form-data.
| Field | Required | Value |
|---|---|---|
member_id | Yes | Room member ID. |
metadata | No | JSON text matching UploadTrackMetadata. |
file | Yes | Playable audio file. The app sends MIME type audio/m4a. |
cover | No | JPEG cover image. |
RoomSnapshot with "added_track_id" and "added"
/rooms/{code}/add_cached
Adds server-cached audio to a room without uploading audio bytes. If you return
{"cached": false} from /cache/has, the app will normally use
/upload instead.
{
"member_id": "member-id",
"video_id": "youtubeVideoId",
"metadata": UploadTrackMetadata
}
RoomSnapshot with "added_track_id" and "added"
If the audio exists but the requested cover does not, return 412 so the app
can fall back to /upload.
/rooms/{code}/stateUpdates synchronized playback state.
| Field | Type | Required |
|---|---|---|
member_id | string | Yes |
client_version | integer | No |
command | next or previous | No |
track_id | string | No |
queue_index | integer | No |
status | playing or paused | No |
position | number | No |
playback_rate | number | No |
lyrics_open | boolean | No |
client_time | number | No |
server_time_offset | number | No |
RoomSnapshot
/rooms/{code}/force_trackStarts playback of an existing room track. If the track is not already in the queue, append it or otherwise make it the current queue item.
{
"member_id": "member-id",
"track_id": "track-id",
"position": 0,
"playback_rate": 1.0,
"status": "playing",
"client_time": 1767311999.95,
"server_time_offset": 0.12,
"client_version": 5
}
RoomSnapshot
/rooms/{code}/replace_current
Replaces the current queue item with an existing room track. BOAR uses this for private
rooms. Public rooms may reject it with 403.
{
"member_id": "member-id",
"track_id": "track-id",
"position": 0,
"playback_rate": 1.0,
"status": "playing",
"client_time": 1767311999.95,
"server_time_offset": 0.12,
"client_version": 5
}
RoomSnapshot
/rooms/{code}/chatAdds a chat message and returns recent chat messages.
{
"member_id": "member-id",
"text": "hello"
}
{"chat": [RoomMessage]}
/rooms/{code}/track/{track_id}?member_id={member_id}Returns the playable room audio file for a track. The member must still belong to the room.
Audio file bytes with a successful HTTP status.
/rooms/{code}/cover/{track_id}?member_id={member_id}
Returns a JPEG cover image for a track. If cover_url is present in
RoomTrack, this URL should return bytes beginning with JPEG magic bytes.
JPEG file bytes with a successful HTTP status, or 404 if no cover exists.
{
"code": "123456",
"name": "Friday Night",
"public": true,
"members": 3,
"tracks": 12,
"current_track_title": "Track title",
"current_track_artist": "Artist name"
}
code, name, public, members, and
tracks are required. Current track fields may be null.
{
"room": RoomInfo,
"state": RoomState,
"queue": ["track-id"],
"tracks": [RoomTrack],
"members": [RoomMember],
"chat": [RoomMessage],
"server_time": 1767312000.456,
"member_id": "member-id",
"added_track_id": "track-id",
"added": true
}
room, state, queue, tracks, and
members are required. member_id is needed in the join response.
added_track_id and added are needed in upload/add-cached
responses. chat and server_time are recommended in every
snapshot.
{
"code": "123456",
"name": "Friday Night",
"public": false
}
{
"version": 5,
"status": "playing",
"playback_rate": 1.0,
"lyrics_open": false,
"track_id": "track-id",
"queue_index": 0,
"position": 42.35,
"updated_at": 1767312000.123,
"rewind_floor_index": 0
}
All fields except rewind_floor_index are required. Use
status: "playing" or "paused". position is seconds.
{
"id": "track-id",
"title": "Song title",
"artist": "Artist",
"duration": 214.2,
"owner_id": "member-id",
"size": 3456789,
"hash": "content-hash",
"lyrics": LyricsInfo,
"download_url": "/rooms/123456/track/track-id",
"cover_url": "/rooms/123456/cover/track-id"
}
id, title, and download_url are required.
artist, duration, owner_id, size,
hash, lyrics, and cover_url may be
null or omitted when unavailable.
{
"id": "member-id",
"name": "Listener"
}
{
"id": "message-id",
"member_id": "member-id",
"member_name": "Listener",
"text": "hello",
"timestamp": 1767312000.123
}
{
"title": "Song title",
"artist": "Artist",
"duration": 214.2,
"video_id": "youtubeVideoId",
"cover_hash": "sha1-of-cover-bytes",
"lyrics": LyricsInfo
}
The app always sends title. Other fields may be null or absent.
{
"status": "available",
"text": "Lyrics text",
"source": "embedded"
}
status must be available or notAvailable. For
unavailable lyrics, use {"status":"notAvailable","text":null,"source":null}
or omit the whole lyrics field.
| Status | Meaning |
|---|---|
| 200 | Successful JSON or file response. |
| 307 | Allowed for search/browse redirects when the original POST body is preserved. |
| 400 | Missing or invalid required field. |
| 403 | Member is missing from a room or the action is not allowed. |
| 404 | Room, track, playlist, video, cover, or cached file not found. |
| 412 | Cached audio exists but the requested cached cover is missing. |
| 429 | Rate limit exceeded. |
| 500 | Unexpected server failure. |
Most app flows only require a non-2xx status to treat a request as failed. For room sync
errors, a JSON body such as {"description":"Room expired."} lets the app show a
clearer message.