Skip to content
BitChordDeveloper Docs
/
Back to site
Addon protocol

Build an addon for BitChord

Connect your own audio catalogue to BitChord with three small JSON endpoints. Your server searches tracks, resolves a playable stream, and describes the audio accurately.

3HTTP routes
JSONWire format
GETEvery request
Overview

How addons work

A listener adds either your root URL or its manifest.json URL. BitChord stores the normalized root, checks the manifest, then tries enabled addons in the order chosen by the listener. When one misses or is unavailable, resolution continues.

01DiscoverGET /manifest.json
02MatchGET /search?q=…
03ResolveGET /stream/{id}
04PlayOpen returned URL

What BitChord does with your answer

  • Matches title, artist, album, and duration to avoid the wrong recording.
  • Negotiates lossless, high, or low quality for the current request.
  • Uses declared codec and transport metadata before guessing from a URL.
  • Caches manifests and searches for 10 minutes, streams for 5 minutes.
Build

Quickstart

Expose three routes from any HTTPS server. The example below is deliberately runtime-neutral: connect handleRequest to the router you already use.

manifest.jsonidentity + capabilities
GET /searchcatalogue lookup
GET /stream/:idplayable audio
JAVASCRIPT
const tracks = [
  {
    id: "track_8f31",
    title: "Midnight Signal",
    artist: "Example Artist",
    album: "Night Drive",
    duration: 242,
    artworkURL: "https://audio.example/art/8f31.jpg",
    format: "flac",
    audioQuality: "LOSSLESS"
  }
]

const MANIFEST = {
  id: "dev.example.reference-addon",
  name: "Reference Addon",
  version: "1.0.0",
  resources: ["search", "stream"]
}

export async function handleRequest(request) {
  const url = new URL(request.url)

  if (url.pathname === "/manifest.json") {
    return json(MANIFEST)
  }

  if (url.pathname === "/search") {
    const query = (url.searchParams.get("q") || "").toLowerCase()
    return json({
      tracks: tracks.filter((track) =>
        [track.title, track.artist, track.album]
          .join(" ").toLowerCase().includes(query)
      )
    })
  }

  if (url.pathname.startsWith("/stream/")) {
    const id = decodeURIComponent(url.pathname.slice(8))
    if (!tracks.some((track) => track.id === id)) {
      return new Response(null, { status: 404 })
    }
    return json({
      url: signedMediaUrl(id),
      codec: "flac",
      container: "flac",
      manifest: "none",
      encrypted: false,
      sampleRate: 96000,
      bitDepth: 24
    })
  }

  return new Response(null, { status: 404 })
}

function json(value) {
  return Response.json(value, {
    headers: { "Cache-Control": "no-store" }
  })
}

function signedMediaUrl(id) {
  return `https://audio.example/media/${encodeURIComponent(id)}.flac`
}
Build

Manifest

Serve the manifest at GET /manifest.json. BitChord needs a stableid and a searchable addon. Unknown fields are ignored, so the document can carry additional metadata for your own tooling.

JSON
{
  "id": "dev.example.reference-addon",
  "name": "Reference Addon",
  "version": "1.0.0",
  "resources": ["search", "stream"],
  "settings": [
    {
      "key": "quality",
      "type": "select",
      "default": "lossless",
      "options": [
        { "label": "Lossless", "value": "lossless" },
        { "label": "High", "value": "high" },
        { "label": "Low", "value": "low" }
      ]
    }
  ]
}
FieldRequiredMeaning
idYesStable, unique addon identifier. An empty id is rejected.
nameRecommendedHuman-readable name shown in the source list.
versionRecommendedDisplayed with health information.
resourcesRecommendedInclude search. Stream may be omitted only when rows carry streamURL.
settingsNoDefaults are forwarded as query parameters on search and stream requests.
Build

Stream endpoint

Accept GET /stream/{id}. Return one openable rendition for the requested quality. A 404 means “not in this addon” and cleanly moves resolution onward.

JSON
GET /stream/track_8f31?quality=lossless&atmos=auto

{
  "url": "https://audio.example/media/track_8f31.flac",
  "format": "flac",
  "quality": "Lossless · 24-bit / 96 kHz",
  "codec": "flac",
  "container": "flac",
  "manifest": "none",
  "encrypted": false,
  "sampleRate": 96000,
  "bitDepth": 24,
  "bitrate": 2800000
}
FieldRequiredMeaning
urlYesAbsolute, directly playable media or manifest URL.
codecRecommendedMost reliable codec signal: flac, alac, mp3, aac, eac3-joc, and others.
containerRecommendedContainer when known.
manifestRecommendednone, hls, or dash. Declare it for extensionless manifest URLs.
encryptedRecommendedUse false. Protected renditions are refused.
sampleRateNoSample rate in Hz; a kHz value below 1000 is also normalized.
bitDepthNoInteger sample bit depth.
bitrateNoBits per second or kbps; BitChord normalizes either convention.
errorNoHelpful explanation when no URL can be returned.
Playback

Quality negotiation

BitChord sends a quality parameter on both search and stream requests. If your manifest lists options, it chooses the closest value by meaning; otherwise it sends one of the native tiers below.

LOSSLESSBit-exact request

Prefer FLAC, ALAC, WAV, or another lossless codec.

HIGHBest lossy request

Return your best lossy rendition, commonly near 320 kbps.

LOWMetered request

Return a compact rendition, commonly 128 kbps or below.

Do not overstate the stream

BitChord verifies quality using the codec it actually decodes. A lossless label on a lossy URL does not create a lossless badge. Accurate codec, sample-rate, bit-depth, and bitrate fields make selection and playback diagnostics reliable.

Playback

Dolby Atmos

Dolby support has two layers: selecting the immersive recording and describing its stream. Some catalogues expose Dolby as another rendition of one id; others return a separate track id. Your addon should support both shapes.

Request behavior

Respond to atmos=auto

BitChord adds this hint only when the device has a compatible decoder and the listener has Dolby enabled. “Auto” means prefer Dolby when available, otherwise return stereo.

HTTP + JSON
// BitChord sends this only when Dolby is enabled and supported:
GET /search?q=midnight&quality=lossless&atmos=auto
GET /stream/dolby_8f31?quality=lossless&atmos=auto

// Search result for a distinct Dolby mix:
{
  "id": "dolby_8f31",
  "title": "Midnight Signal",
  "artist": "Example Artist",
  "album": "Night Drive",
  "duration": 242,
  "format": "dash",
  "audioModes": ["DOLBY_ATMOS"],
  "atmos": true
}

// Stream response:
{
  "url": "https://audio.example/media/dolby_8f31",
  "format": "dash",
  "quality": "Dolby Atmos",
  "codec": "eac3-joc",
  "container": "mp4",
  "manifest": "dash",
  "sampleRate": 48000,
  "audioMode": "DOLBY_ATMOS",
  "encrypted": false
}

Dolby rules that matter

  1. 1
    Mark separate rows.

    Use atmos: true, audioMode, or audioModes. This lets BitChord prefer the Dolby row even if its catalogue quality label says LOW.

  2. 2
    Use a recognized codec.

    Return eac3-joc or a clear Dolby label. BitChord normalizes common underscore and hyphen spellings.

  3. 3
    Return stereo when Dolby is absent.

    With atmos=auto, an ordinary track must still play. Do not turn “no Dolby mix” into an error.

  4. 4
    Declare the transport.

    Dolby is often carried by DASH. Set manifest: "dash" even when the URL has no file extension.

Ship

Install and test

01
Publish your server

Make the root and all three routes reachable over HTTPS.

02
Add the URL

In BitChord, open Settings → Sources → Add an addon. Paste the root or manifest URL.

03
Run the health check

BitChord reads the manifest and verifies the addon is searchable.

04
Play representative tracks

Test direct audio, manifest-based audio, every quality tier, misses, and Dolby.

Fast contract checks

GET /manifest.jsonGET /search?q=test&quality=LOSSLESSGET /stream/<encoded-id>?quality=LOSSLESS
Ship

Production checklist

Stable addon and track ids
HTTPS on every returned URL
JSON content on all success responses
Search answers quickly
Stream URLs survive at least five minutes
404 for a missing track
429 includes Retry-After
No encrypted rendition unless requested
Accurate codec and transport fields
Logs never expose path-based tokens
Stereo fallback for Dolby auto mode
Representative duration metadata
Reference

Troubleshooting

“That URL answered, but not with an addon manifest”

Return an object with a non-empty id from /manifest.json, or confirm the pasted URL resolves to your addon root.

The addon is healthy but returns no results

Check the q parameter, return a tracks array, and ensure each row has both id and title.

Audio fails on an extensionless URL

Declare manifest as hls or dash. For direct audio, provide codec and container.

A track falls back to another source

Return 404 only for a true miss. Check for an empty URL, an encrypted response, malformed URL, or unsupported Dolby stream.

Dolby always returns stereo

Read atmos=auto on both search and stream. If Dolby is a separate catalogue row, mark that row with atmos or audioModes.

The wrong recording is selected

Improve artist, album, and duration metadata. Avoid live, edit, remix, or instrumental variants unless the query asks for them.

Ready to connect?

Ship the smallest honest contract.

Three routes, accurate metadata, graceful misses. BitChord handles the rest.

Review the quickstart