LASTWAVE ADDON DOCS
Build an addon.
An addon is a small web service that LastWave asks for lossless, Hi-Res and Dolby Atmos streams. Implement three endpoints, return the JSON shown here, and any LastWave app can use your addon.
01 / START · OVERVIEW
How an addon fits in
LastWave streams from YouTube Music by default. When a user has an addon, LastWave first asks the addon whether it has a better version of the track that’s about to play. If it does, that stream plays. If it doesn’t, playback continues from YouTube, so a missing track is never a problem.
- 1
Validate
GET manifest.json– is this a usable addon? - 2
Search
GET search– find the closest matching track. - 3
Stream
GET stream/{id}– return a playable source. - 4
Play
LastWave plays it and shows the real quality.
At a glance
| Endpoint | Purpose | Minimum response |
|---|---|---|
GET manifest.json | Identify your addon | id, name, version, resources containing search and stream |
GET search?q= | Find the track | tracks[], each with id and title |
GET stream/{id} | Return the audio | A playable source (url or manifestXml), plus sampleRate and bitDepth |
02 / START · QUICK START
Up and running in three steps
- Build the three endpoints. Start from the minimal example, or follow the reference for the manifest, search and stream responses.
- Test them with curl. Each request should return the JSON shown in the reference.
curl https://addon.example.com/manifest.json curl "https://addon.example.com/search?q=Example%20Song%20Example%20Artist&quality=hi_res&atmos=none" curl "https://addon.example.com/stream/48213?quality=hi_res&atmos=none" - Add it to the app. On Desktop open Settings → Sources → Add addon. On Android open Provider Modules → Addon URL and use Test connection.
Stuck on step three? A row marked unreachable means nothing usable answered at that URL. Check HTTPS, the manifest, and that the root ends where manifest.json lives.
03 / START · ADDON URL
One URL is all a user adds
Users paste a single URL: your addon’s root. LastWave appends manifest.json, search and stream/{id} to it, so all three must live under the same root.
| Form | Example | Notes |
|---|---|---|
| Standalone root | https://addon.example.com/ | Simplest. A path prefix such as https://example.com/lw/ also works. |
| Per-user root | https://addon.example.com/a/<token>/ | A hex token of 16+ characters after /a/ is treated as a private, per-user URL. Revoke a token by answering 404. |
Rules
- LastWave accepts the root with or without a trailing slash, a pasted
manifest.jsonURL, or a deeperstream/…orsearchURL, and normalizes all of them to the root. - Use HTTPS in production. Only
httpandhttpsURLs are accepted. - Send
Access-Control-Allow-Origin: *so browsers and tools can read your responses. - LastWave may send extra request headers (such as a
User-AgentofLastWave-Player). Ignore any header you don’t recognize.
04 / API REFERENCE · MANIFEST
Tell LastWave what you are
Request
GET{root}manifest.jsonLastWave caches the response for about 10 minutes.
Response
{
"id": "com.example.addon",
"name": "Example Addon",
"version": "1.0.0",
"resources": ["search", "stream"]
}| Field | Required | Meaning |
|---|---|---|
id | Yes | A stable identifier for your addon, for example a reverse-DNS string. |
name | Yes | The name shown to users in Settings. |
version | Yes | Your addon’s version string. |
resources | Yes | Must include both "search" and "stream". If either is missing, LastWave rejects the URL as “not an addon”. |
Notes
- Extra fields are ignored, so it’s safe to add your own.
05 / API REFERENCE · SEARCH
Find the track
Request
GET{root}search?q=…&quality=…&atmos=…Returns JSON with a tracks array.
| Parameter | Value | Meaning |
|---|---|---|
q | text | What to search for. Usually the title and artist together, sometimes the title alone. |
quality | hi_res, lossless, high, atmos | The tier the user picked. Use it to filter, so you don’t return a track that only exists at another tier. |
atmos | auto or none | auto when LastWave would like Atmos candidates included. |
Response
{
"tracks": [
{
"id": "48213",
"title": "Example Song",
"artist": "Example Artist",
"album": "Example Album",
"duration": 215,
"format": "flac",
"audioQuality": "LOSSLESS"
}
]
}| Track field | Type | Notes |
|---|---|---|
id | string | Required. Your identifier for the track. It’s sent back to stream/{id}. |
title | string | Required. Include version info the way a listener would see it, e.g. Song (Remastered 2011). |
artist | string | Join several artists with a comma. |
album | string | Album title. |
duration | number | Length in seconds. Used to confirm the match. |
format | string | For example flac. |
audioQuality | string | For example LOSSLESS or HI_RES_LOSSLESS. |
atmos | boolean | true if this is a Dolby Atmos version. |
audioModes | string[] | For example ["STEREO", "DOLBY_ATMOS"]. |
Notes
- Accurate metadata matters. LastWave picks the closest match using title, artist and duration. It penalises artist mismatches and expects the duration to be within a few seconds, so a live or remix version doesn’t play by accident.
- No match is fine. Return
{"tracks": []}. LastWave treats that as a miss and keeps playing from YouTube. - Return the best handful of results. LastWave only uses the first 15.
- Respond within about 12 seconds, the search time limit.
06 / API REFERENCE · STREAM
Hand over the audio
Request
GET{root}stream/{id}?quality=…&atmos=…Returns JSON describing one playable stream.
Response
Return at least one playable source. You can use a direct file, or a DASH manifest:
Progressive file
A direct URL to a FLAC (or other audio) file.
{
"url": "https://cdn.example.com/48213.flac",
"format": "flac",
"codec": "flac",
"container": "flac",
"sampleRate": 96000,
"bitDepth": 24,
"quality": "Hi-Res Lossless",
"encrypted": false
}DASH manifest
A URL to an .mpd, or the manifest inline as manifestXml.
{
"url": "https://example.com/48213.mpd",
"format": "dash",
"manifest": "dash",
"codec": "flac",
"container": "mp4",
"sampleRate": 96000,
"bitDepth": 24,
"quality": "Hi-Res Lossless",
"encrypted": false
}| Field | Notes |
|---|---|
url | The playable source. LastWave also reads dataUrl, directUrl, streamUrl, downloadUrl and mediaUrl, and an inline manifestXml. At least one is required. If you offer both a direct file and a manifest, Desktop prefers the direct file. |
format, codec, container | For example flac / flac / flac, or dash / flac / mp4. Lossy streams use a codec such as mp4a.40.2. |
sampleRate | In Hz: 44100, 48000, 96000, 192000. |
bitDepth | For lossless: 16 or 24. Leave it out for lossy streams. Report the real value, because LastWave shows it to the listener and never guesses it from the bitrate. |
bitrate | Optional, in bits per second. |
quality | A label such as Lossless, Hi-Res Lossless or Dolby Atmos. |
encrypted | Set to false for plain streams. |
Notes
- The audio player fetches media with no extra headers. If your media URLs need protection, put the proof in the URL itself, for example a signed link with an expiry, and have
/streammint it. - Respond quickly. LastWave retries a failed stream request once after about 150 ms, and gives a whole lossless lookup roughly 30 seconds in total.
07 / CONCEPTS · QUALITY TIERS
What the quality value means
Users pick a tier in the app. LastWave turns it into the quality value your addon receives, and if you answer 404, it steps down to the next one.
| User picks | LastWave sends | Then tries |
|---|---|---|
| Dolby Atmos | atmos (with atmos=auto) | hi_res, lossless, high |
| Hi-Res · 24/192 or 24/96 | hi_res | lossless, high |
| Lossless · 16/44.1 | lossless | hi_res, high |
| 320k | high | lossless |
| Data saver, YouTube | nothing | LastWave doesn’t ask the addon for the lossless ladder. |
What each value means
hi_res: the best you have, up to 24-bit / 192 kHz.lossless: CD-quality 16-bit / 44.1 kHz FLAC.high: a high-bitrate lossy stream (320 kbps class).
Notes
- Read the value case-insensitively and be forgiving: treat
hires,maxorflacsensibly. - Serve what you actually have. A track that only exists at 16/44.1 should return that for a
hi_resrequest instead of an error. LastWave labels it honestly from yourbitDepthandsampleRate.
08 / CONCEPTS · DOLBY ATMOS
Spatial audio, only when asked
Atmos is opt-in. LastWave only wants it when the user chose Dolby Atmos, which it signals with atmos=auto and quality=atmos.
Response
{
"url": "https://example.com/48213-atmos.mpd",
"format": "dash",
"manifest": "dash",
"codec": "eac3-joc",
"container": "mp4",
"sampleRate": 48000,
"quality": "Dolby Atmos",
"audioMode": "DOLBY_ATMOS",
"encrypted": false
}Rules
- Set
audioModetoDOLBY_ATMOS,codectoeac3-joc(E-AC-3 with Joint Object Coding) andqualitytoDolby Atmos. Atmos streams are 48 kHz. - Flag Atmos tracks in search results with
"atmos": trueor anaudioModeslist containingDOLBY_ATMOS. - Never return Atmos for a stereo request. If the user picked Hi-Res, Lossless or 320k, LastWave discards any Atmos stream it receives, so don’t send one.
- No Atmos version? Return the stereo stream, or a 404. LastWave steps down to Hi-Res stereo. On Android it also checks that the device can render spatial audio before it plays Atmos.
09 / OPERATE · ERRORS AND QUOTA
Say “no” the right way
| Status | Means | What LastWave does |
|---|---|---|
200 | Here is a stream (or search results). | Plays it. |
404 | Unknown track, no rendition at this tier, or a revoked URL. | A clean miss. Steps down a tier, then falls back to YouTube. |
429 | The user is out of quota. | Stops asking and shows the limit. Send Retry-After in seconds. |
5xx or timeout | Your service is having trouble. | Retries once quickly, then treats it as a miss. After several misses in a row it skips your addon for about 30 seconds. |
Quota headers
If you meter usage, tell LastWave about it on your responses. Desktop shows these as quota meters in Settings:
X-Quota-Limit: 500
X-Quota-Used: 120
X-Quota-Remaining: 380Notes
- Only count successful stream resolutions against a quota, never searches or failed lookups.
- Answer a revoked or unknown per-user URL with
404on every route, so it stops working immediately and LastWave moves on.
10 / OPERATE · SECURITY
Treat the URL like a password
- A per-user URL is the credential: anyone who has it plays on that user’s quota. Use long, random tokens (at least 128 bits) and offer a way to regenerate one.
- Never put API keys or backend credentials in the URL. Keep them on your server.
- Don’t write full tokens to your logs.
- Serve everything over HTTPS, and validate the token format before touching your database.
- Rate-limit your endpoints. Search and manifest requests are cheap for LastWave to repeat.
11 / BUILD · MINIMAL EXAMPLE
A working addon in under 40 lines
This Node.js server implements the whole contract with a one-track catalog. Replace the catalog with your own source.
import express from 'express';
const app = express();
const catalog = [
{ id: '1', title: 'Example Song', artist: 'Example Artist', album: 'Example Album',
duration: 215, file: 'https://cdn.example.com/audio/1.flac', sampleRate: 44100, bitDepth: 16 },
];
app.use((req, res, next) => { res.set('Access-Control-Allow-Origin', '*'); next(); });
app.get('/manifest.json', (req, res) => {
res.json({ id: 'com.example.addon', name: 'Example Addon', version: '1.0.0',
resources: ['search', 'stream'] });
});
app.get('/search', (req, res) => {
const q = String(req.query.q || '').toLowerCase();
const tracks = catalog
.filter((t) => q.includes(t.title.toLowerCase()) || q.includes(t.artist.toLowerCase()))
.map(({ file, sampleRate, bitDepth, ...track }) => ({ ...track, format: 'flac', audioQuality: 'LOSSLESS' }));
res.json({ tracks });
});
app.get('/stream/:id', (req, res) => {
const track = catalog.find((t) => t.id === req.params.id);
if (!track) return res.status(404).json({ error: 'Unknown track.' });
// No Atmos version here: serve stereo (or 404) and LastWave steps down on its own.
res.json({
url: track.file, format: 'flac', codec: 'flac', container: 'flac',
sampleRate: track.sampleRate, bitDepth: track.bitDepth,
quality: 'Lossless', encrypted: false,
});
});
app.listen(8787, () => console.log('Addon on http://localhost:8787/'));12 / BUILD · PUBLISH CHECKLIST
Before you share your addon
- The manifest lists both
searchandstream. - Search returns
id,title,artistand a duration in seconds. - Stream returns a playable source plus real
sampleRateandbitDepth. - A miss is a clean
404, never a placeholder file. - Atmos is returned only for Atmos requests.
- Everything is served over HTTPS and answers quickly.
- A revoked URL answers
404on every route. - You only serve audio you have the rights to provide.
13 / HELP · GET HELP
Questions, or want your addon listed?
For Hi-Res addons and more info, contact support on Discord or Telegram . Tell us what your addon serves and which app you’re testing with.