Overview
The SonoVault API gives you access to music metadata for 90 million tracks and counting. Look up ISRC codes, ISWC work codes, artist credits, label, genre tags, release dates, and cross-platform IDs — all from a single REST endpoint.
Use it to power music discovery apps, catalog management, sync and royalty tools, or any workflow that needs accurate track-level data.
All responses are JSON. Every request requires an API key in the x-api-key header. There are no SDKs to install — any HTTP client works.
What you can do
- Search tracks by artist and title — returns ISRC, duration, genre, and artist credits.
- Look up by ISRC — get full metadata for a specific recording.
- Resolve ISWC work codes — find the composition code(s) behind a recording, or list every recording of a work by its ISWC.
- Resolve cross-platform IDs — given a Spotify, Beatport, Apple Music, Tidal, Discogs, or MusicBrainz ID, get matching IDs on all other platforms.
- Search artists and releases — find artists by name or browse their discography.
- New releases — paginated feed of the latest releases, sorted by artist popularity.
Try it without signing up
Every endpoint on this page has a Try it out button that opens the API explorer. It uses a public demo key so you can make real requests and see live responses — no account required. Click Try it out on any endpoint to get started.
Quickstart
Get from zero to your first API response in under 5 minutes. Create a free account to get an API key — no credit card required.
1. Sign up and verify your email
Create a free account at sonovault.now. We send a verification link to your inbox — click it before making API calls. Until your email is verified, every /v1 request returns 403 Email not verified.
2. Get your API key
Once verified, grab your key from the dashboard. All requests must include it in the x-api-key header.
3. Make your first request
Search for a track by artist and title. The response includes ISRC, duration, genre, and full artist credits.
curl https://api.sonovault.now/v1/tracks/search \-H "x-api-key: YOUR_API_KEY" \-G -d "artist=Daft Punk" \-d "title=One More Time"
4. Explore the API
Now that you have your first result, try looking up the ISRC with the ISRC lookup endpoint or resolve cross-platform links with Platform links.
Authentication
All API requests require a valid API key passed in the x-api-key header.
x-api-key: svk_live_xxxxxxxxxxxxxxxxxx
Test keys prefixed svk_test_ return mocked data. Live keys prefixed svk_live_ hit the real database.
Base URL
All API requests use the following base URL:
https://api.sonovault.now
All endpoints return JSON with Content-Type: application/json. Request parameters are passed as query strings for GET requests.
Rate limits
Rate limits are enforced per API key on a rolling 60-second window. If you exceed your limit, requests return 429 Too Many Requests.
| Tier | Requests/month | Burst (per 60s) |
|---|---|---|
| Free | 1,000 | 20 |
| Starter | 50,000 | 60 |
| Growth | 500,000 | 300 |
| Scale | 5,000,000 | 1,000 |
Every response includes rate limit headers so you can track your usage and avoid hitting limits:
| Header | Description |
|---|---|
RateLimit-Limit | Your burst limit (max requests per 60-second window). |
RateLimit-Remaining | Requests remaining in the current window. |
RateLimit-Reset | Seconds until the current window resets and RateLimit-Remaining refills. |
When you receive a 429 response, wait the number of seconds in RateLimit-Reset before retrying. For batch workloads, check RateLimit-Remaining and throttle proactively rather than waiting for a rejection.
Errors
All errors return a JSON object with an error string:
{"error": "Not found"}
| Status | Description |
|---|---|
400 | Missing or invalid parameters. |
401 | Missing or invalid API key. |
403 | Endpoint requires a paid plan. Upgrade to access browse. |
410 | Endpoint disabled. The endpoint will return in another form later. |
404 | No matching track or resource. |
429 | Rate limit exceeded. Check RateLimit-Reset header for retry timing. |
500 | Unexpected server error. |
Webhooks & events
For live broadcast monitoring you don't have to poll — SonoVault can push every recognised play to you in real time. Two delivery options, both bundled with stream monitoring (no API credits):
- Webhooks — register an endpoint and we POST a signed event whenever a track is recognised on any of your streams. Best for server-to-server integrations (logging, royalties, automation).
- Live feed (SSE) — open a
GET /v1/streams/liveconnection and receive events as a Server-Sent Events stream. Best for dashboards and live displays.
Event types
| Event | When | Payload |
|---|---|---|
stream.play.started | A track is confirmed playing on a stream. | { id, type, created, data: { stream_id, started_at, track } } |
track is the standard public track object (same shape as search results). id is the event id — stable across retries, so use it to deduplicate. Manage endpoints with the Webhooks API, or from your dashboard. A track-*end* event isn't offered yet — there's no reliable signal for when a track stops.
Verifying signatures
Every webhook POST carries a SonoVault-Signature header: t=<unix-timestamp>,v1=<hex>, where v1 is HMAC-SHA256(secret, "{t}.{rawBody}") using the signing secret shown once when you created the endpoint. Recompute it over the raw request body and compare in constant time; reject if the timestamp is too old to block replays.
import crypto from "crypto";// Express: needs the RAW body, e.g. app.use(express.raw({ type: "application/json" }))function verify(rawBody: Buffer, header: string, secret: string): boolean {const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));const t = Number(parts.t);if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // replay windowconst expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody.toString()}`).digest("hex");return crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));}
Delivery & retries
- Return any 2xx to acknowledge. A non-2xx response or a timeout is retried with exponential backoff (~10s → 6h), given up on after 12 attempts.
- Deliveries are at-least-once — dedupe on
SonoVault-Event-Id. - An endpoint that keeps failing is automatically disabled; re-enable it from the dashboard or
PATCH /v1/webhooks/:id. - Inspect recent attempts (status, HTTP code, error) via
GET /v1/webhooks/:id/deliveriesor send a sample withPOST /v1/webhooks/:id/test.
Live feed (SSE)
GET /v1/streams/live returns a text/event-stream of the same events across all your streams. Each message is an SSE event: line (the event type) with the event JSON in data:. Authenticate with the x-api-key header. In the browser, the EventSource API can't send headers — proxy the request through your own server so your key isn't exposed.
FAQ
Track search
Search for tracks by artist and title. Returns ISRC, genre, release dates, and full metadata.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| artist | query | string | required | Artist name. |
| title | query | string | required | Track title. |
| limit | query | integer | optional | Max results, 1–100. Default 20. |
| cursor | query | string | optional | Cursor from previous response for pagination. |
Pagination
When more results are available, the response includes a next_cursor string. Pass it as the cursor parameter to fetch the next page:
curl https://api.sonovault.now/v1/tracks/search \-H "x-api-key: YOUR_API_KEY" \-G -d "artist=Daft Punk" \-d "title=Around" \-d "cursor=eyJpZCI6MTIzfQ"
When next_cursor is null, you have reached the last page.
Example response
ISRC lookup
Exact lookup by ISRC code. The ISRC is part of the path — there is no query string. Returns full track detail including genre and subgenre.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| isrc | path | string | required | ISRC code (e.g. GBDUW0000053). |
Example response
ISWC lookup
Find the ISWC(s) — the work/composition codes — for a recording, by ISRC or SonoVault track ID. Provide exactly one of isrc or id. A recording can carry several ISWCs (medleys, samples). ISWCs are sourced from the MLC and are not part of the standard track payload.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| isrc | query | string | optional | ISRC of the recording. Provide this or id. |
| id | query | integer | optional | SonoVault track ID. Provide this or isrc. |
Example response
Recordings by ISWC
Reverse lookup: every recording of a composition, by ISWC. The ISWC is part of the path; human-readable separators (T-070142799-7) are accepted. Recordings are returned most-popular-first, each with a representative ISRC; total is the full count even when limit truncates the page.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| iswc | path | string | required | ISWC code (e.g. T0701427997). |
Query parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | optional | Max recordings, 1–200. Default 50. |
Example response
Track by ID
Get a single track by its internal SonoVault track ID. Returns the same track payload as ISRC lookup — title, artists, releases, ISRC, duration, genre, and subgenre.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | required | Internal SonoVault track ID. |
Example response
Platform links
Lightweight cross-platform ID resolver. Given an ISRC or a source-specific ID, returns all known external platform links.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | query | integer | optional | Internal SonoVault track ID. Provide one lookup param at a time. |
| isrc | query | string | optional | ISRC code. |
| spotify_id | query | string | optional | Spotify track ID. |
| beatport_id | query | string | optional | Beatport track ID. |
| discogs_id | query | string | optional | Discogs release ID. |
| musicbrainz_id | query | string | optional | MusicBrainz recording ID. |
| applemusic_id | query | string | optional | Apple Music track ID. |
| tidal_id | query | string | optional | Tidal track ID. |
| youtube_id | query | string | optional | YouTube video ID (11-char watch?v= id). |
Provide exactly one lookup parameter per request: isrc, spotify, beatport, discogs, musicbrainz, applemusic, or tidal.
Example response
Bulk resolve
Resolve up to 100 inputs in a single request — track names, ISRCs, or platform IDs — to canonical SonoVault tracks plus their cross-platform links. One credit is charged per input line. If your monthly quota runs out mid-batch the response is partial: unresolved lines come back with status skipped_no_credits instead of an error.
Request body (JSON)
input_type(string, required) — one oftrack_name,isrc,sonovault_id,spotify_id,applemusic_id,tidal_id,beatport_id,discogs_id,musicbrainz_id.items(array, required) — 1 to 100 entries. Fortrack_name, each entry is an object{ artist, title }; for every other input type, each entry is a string.
Response
results— one entry per input line, in input order:{ input, status, track, links }, wherestatusismatched,not_found, orskipped_no_credits.partial,processed,credits_used,credits_remaining,message— the batch billing summary.
Example response
Identify (audio recognition)
Recognise a track from audio. Send a Chromaprint fingerprint (run fpcalc -raw client-side) — or the raw audio file and we fingerprint it for you — and get back the best-matching catalog tracks, ranked, each with a confidence score. A fingerprint costs 10 credits; raw audio costs 10 + ceil(MB). A request that would cost more credits than you have left this month is refused with a 429 before any matching runs. Paid plans only.
Request body (JSON) — fingerprint (recommended)
fingerprint(array, required) — 50–50000 integers fromfpcalc -raw(Chromaprint), run client-side. ~1 KB upload. Costs 10 credits.fingerprint_duration(number, optional) — clip duration in seconds.top_n(integer, optional) — max results to return, 1–25.
Alternative — upload raw audio (no fpcalc needed)
- Alternatively send the file itself:
Content-Type: application/octet-stream(oraudio/*) with the raw bytes as the body, up to 100 MB. Any ffmpeg-decodable format works — mp3, flac, wav, m4a, mp4, ogg, opus, … (the format is detected from the content). - Optional
?length=&top_n=query params. We analyse a ~30 s window at a time and automatically scan deeper into your audio on a miss. - Cost: 10 + ceil(MB) credits (recognition + a per-MB surcharge). Prefer the fingerprint path when you can.
How much audio to send, cost & retries
- Send the whole track (or a generous section), not a short clip. The part that matches our catalog reference can sit anywhere — often the main hook a few minutes in, not the intro — so a snippet from the wrong section misses the match even though the track is in the catalog. ~10 s is the minimum; more audio is more reliable.
- You're billed on the bytes you upload (10 + ceil(MB)), so a full ~4-min track is ~15–20 credits. To keep that small with no loss of accuracy, downsample to mono 11025 Hz (Chromaprint's native rate) before sending — about a quarter the size of the source. Trimming to a few seconds is cheaper but risks a miss.
- On a miss, the upload path automatically scans deeper windows of your audio and votes across them before returning no match. On the fingerprint path, fingerprint a longer span (e.g.
fpcalc -length 0for the whole file) or retry from a different part of the track.
Response
matched(boolean) andresults— the matching catalog tracks, best match first. Each result is slim:id,title,artists, andconfidence(a 0–1 score; higher means a more certain match).- Need ISRC, genre, release info, or cross-platform links? Look the track up by
idwithGET /v1/tracks/:id(or/v1/tracks/links) — identify keeps its response small on purpose. credits_charged— credits billed for this call.
Upload raw audio (no fpcalc)
Instead of a fingerprint, send the raw audio file itself — any ffmpeg-decodable format (mp3, flac, wav, m4a, ogg, opus…), up to 100 MB. Set Content-Type: application/octet-stream and put the file bytes in the request body; we fingerprint it server-side.
curl -X POST https://api.sonovault.now/v1/tracks/identify \-H "x-api-key: YOUR_API_KEY" \-H "Content-Type: application/octet-stream" \--data-binary @track.mp3
Example response
Browse tracks
Discover tracks by filtering on label, artist, genre, or year. Returns up to 20 tracks ordered by release date (newest first) by default; pass randomize=true to sample 20 random tracks from the top-popularity pool instead. At least one filter parameter is required — calls with no filters return 400. Filter by genre with either genreId (a canonical id from GET /v1/genres) or genre (a free-text exact name) — prefer genreId as it is stable and unambiguous; use genre only to reach rare subgenres that /v1/genres does not list. genre and genreId are mutually exclusive — passing both returns 400.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| labelId | query | integer | optional | Filter by label ID. |
| artistId | query | integer | optional | Filter by artist ID. |
| genre | query | string | optional | Exact genre name, case-insensitive (House matches House but not Tech House). Free-text alternative to genreId — use it for rare subgenres not listed by GET /v1/genres. Mutually exclusive with genreId. |
| genreId | query | integer | optional | Canonical genre id from GET /v1/genres — the stable, unambiguous way to filter by genre. Mutually exclusive with genre. |
| year | query | integer | optional | Release year (e.g. 2025). |
| randomize | query | boolean | optional | If true, return 20 random tracks sampled from the top 1000 by popularity instead of the most recent releases. Defaults to false. |
Example response
Artist search
Search for artists by name. Returns each artist with country, formation year (and full formation date when day-level precision is known), social links, and Wikidata ID where available.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| name | query | string | required | Artist name to search. |
| limit | query | integer | optional | Max results, 1–100. Default 20. |
| cursor | query | string | optional | Cursor from previous response for pagination. |
Pagination
When more results are available, the response includes a next_cursor string. Pass it as the cursor parameter to fetch the next page:
curl https://api.sonovault.now/v1/artists/search \-H "x-api-key: YOUR_API_KEY" \-G -d "name=Daft Punk" \-d "cursor=0:5000000:1"
When next_cursor is null, you have reached the last page.
Example response
Artist by ID
Get artist by ID. Returns the public artist fields (country, formation_year + optional formation_date, social_links, wikidata_id) flattened alongside a release_count summary. For the actual release list, use /v1/artists/:id/releases — the embedded array was removed because it became huge for prolific artists.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | required | Artist ID. |
Example response
Artists by label
Paginated list of artists with releases on a specific label, ordered by number of releases on that label (descending). Each artist carries the public profile fields and a release_count scoped to this label.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | required | Label ID. |
Query parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | optional | Max results, 1–100. Default 20. |
| cursor | query | string | optional | Cursor from previous response for pagination. |
Example response
Label search
Search for record labels by name. Returns labels sorted by relevance and release count.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| name | query | string | required | Label name to search. |
| limit | query | integer | optional | Max results, 1–100. Default 20. |
| cursor | query | string | optional | Cursor from previous response for pagination. |
Pagination
When more results are available, the response includes a next_cursor string. Pass it as the cursor parameter to fetch the next page:
curl https://api.sonovault.now/v1/labels/search \-H "x-api-key: YOUR_API_KEY" \-G -d "name=Drumcode" \-d "cursor=0:42:1"
When next_cursor is null, you have reached the last page.
Example response
Label by ID
Get label by ID. Returns the public label fields (id, name) flattened alongside release_count and artist_count summaries. For the actual lists, use /v1/labels/:id/releases and /v1/labels/:id/artists — embedded arrays were removed because they became huge for prolific labels.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | required | Label ID. |
Example response
Releases by artist
Paginated feed of releases by a specific artist, ordered by release date (newest first). The per-item artist field is omitted (the caller already knows it).
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | required | Artist ID. |
Query parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | optional | Max results, 1–100. Default 20. |
| cursor | query | string | optional | Cursor from previous response for pagination. |
Example response
Releases by label
Paginated feed of releases on a specific label, ordered by release date (newest first). Each result carries the primary artist, label, catalog number, and track count.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | required | Label ID. |
Query parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | optional | Max results, 1–100. Default 20. |
| cursor | query | string | optional | Cursor from previous response for pagination. |
Example response
Release search
Search for releases by title, optionally filtered by artist.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| title | query | string | required | Release title to search. |
| artist | query | string | optional | Artist name filter. |
| limit | query | integer | optional | Max results, 1–100. Default 20. |
| cursor | query | string | optional | Cursor from previous response for pagination. |
Pagination
When more results are available, the response includes a next_cursor string. Pass it as the cursor parameter to fetch the next page:
curl https://api.sonovault.now/v1/releases/search \-H "x-api-key: YOUR_API_KEY" \-G -d "title=Discovery" \-d "cursor=0:85:1"
When next_cursor is null, you have reached the last page.
Example response
Release by ID
Get release by ID. Returns the release fields flat with nested artist and label objects, plus a peer tracks[] array — each track has ISRC, genre, subgenre, and artist credits.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | required | Release ID. |
Example response
New releases
Paginated feed of releases ordered by release date (newest first).
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | optional | 1–100. Default 20. |
| cursor | query | string | optional | Cursor from previous response for pagination. |
Pagination
When more results are available, the response includes a next_cursor string. Pass it as the cursor parameter to fetch the next page:
curl https://api.sonovault.now/v1/releases/new \-H "x-api-key: YOUR_API_KEY" \-G -d "limit=20" \-d "cursor=2026-03-22:78:789"
When next_cursor is null, you have reached the last page.
Example response
List genres
Lists every canonical genre and subgenre SonoVault recognises. Use this to find the numeric genreId for a genre suggestion — the suggestion endpoint validates against this exact list, so it never goes out of date.
Example response
Suggest a genre
Suggest a better genre for a track. Suggestions are reviewed by the SonoVault team before they affect the catalog — a successful call returns the suggestion with status: "pending". Requires a paid plan.
Request body (JSON)
genreId(integer, required) — a genre id fromGET /v1/genres.note(string, optional) — a short explanation for the reviewer, max 100 characters.
Limits
- Up to 100 suggestions per day per account. Can be increased upon request.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | required | Internal SonoVault track ID. |
Example response
List your suggestions
Lists the edit suggestions you have submitted and their review status (pending, approved, or rejected), newest first.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | optional | Max results, 1–100. Default 20. |
| cursor | query | string | optional | Cursor from previous response for pagination. |
Example response
Register a stream
Register a live broadcast/radio stream URL for continuous "now playing" recognition. We start monitoring immediately and recognise each track as it airs. Returns the stream id you'll poll for results. Billed at €29 per active stream / month. Paid plans only.
Request body (JSON)
url(string, required) — the direct stream URL (HTTP/Icecast/Shoutcast mp3 or AAC). Give the audio stream itself, not a webpage or playlist page.name(string, optional) — a label for the stream, shown in your dashboard.format(string, optional) — recognition-tuning hint for the station's music. One ofelectronic,classical, orpop; omit forauto(all recognisers), which is safest for mixed stations.electronicandclassicalnarrow the recognisers to the ones that work for that material (e.g.classicaluses AcoustID only). Change it any time withPATCH /v1/streams/:id.detection_mode(string, optional) — how sure we must be before reporting a track, trading off accuracy vs. coverage.precisereports fewer tracks but almost all are correct;balanced(the default) is a strong mix;broadcatches more tracks but with some wrong guesses. We auto-tune each stream toward the chosen target using the station's own now-playing data. Change it any time withPATCH /v1/streams/:id.
Billing
- Each active stream is €29 / month, prorated, on a subscription separate from your API plan. Stop a stream and billing stops at period end.
- The /v1/streams endpoints (register, poll now-playing, history, stop) don't use API credits — they're included with the per-stream subscription.
Example response
List streams
List the streams you've registered (active and stopped), newest first.
Example response
Stream status + now playing
Get a stream's status and the track playing right now. now_playing is null during ad breaks, talk, or unrecognised audio. The track is a plain track object — the same shape as search results.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | required | Stream id from registration. |
Example response
Update stream settings
Update a stream's format and/or detection_mode. format tunes which recognisers run, based on the kind of music the station plays (electronic, classical, pop, or null to clear back to auto) — classical, for example, runs AcoustID only. detection_mode sets your accuracy/coverage preference (precise, balanced, or broad). Send either field, or both. Takes effect within a minute, no need to re-register.
Request body (JSON) — at least one field
format(string or null) — one ofelectronic,classical,pop, ornullto clear it back toauto.detection_mode(string) — one ofprecise,balanced,broad.precise= fewer tracks, almost all correct;broad= catch more, with some wrong guesses. We auto-tune the stream toward this target from the station's own now-playing.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | required | Stream id. |
Example response
Recognition history
The stream's recognition log, newest first. Each entry is a recognised track with the time it started. Use since to fetch only what's new.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | required | Stream id. |
Query parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| since | query | string | optional | ISO-8601 timestamp — return only plays that started after it. |
Example response
Airplay report
Airplay/spin report over a date range — across all your streams, or one via stream_id. The report is grouped by stream: each streams[] entry carries its own per-track summary of play counts plus a timestamped detail play-by-play log, each row carrying its ISRC. Plays are de-duplicated per airing: one airing counts once, not once per recognition window. JSON only.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| from | query | string | required | Start of the window (inclusive), ISO date — e.g. 2026-06-01. |
| until | query | string | required | End of the window (exclusive), ISO date. The window must be 92 days or less. |
| stream_id | query | string | optional | Limit the report to one stream id. Omit for an account-wide report across all your streams. |
Example response
Stop a stream
Stop monitoring a stream. Billing for it stops at the end of the current period. Idempotent.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | required | Stream id. |
Example response
Create a webhook
Register an endpoint to receive a signed HTTP POST whenever a track is recognised on any of your monitored streams — no polling. The response includes the signing secret once; store it to verify deliveries. Bundled with stream monitoring (no API credits).
Request body (JSON)
url(string, required) — your HTTPS endpoint; we POST events here.event_types(string[], optional) — which events to receive; defaults to all currently-available events. For now there is just one:stream.play.started.description(string, optional) — a label shown in your dashboard.
Verifying deliveries
- Every POST carries
SonoVault-Signature: t=<unix>,v1=<hex>wherev1 = HMAC-SHA256(secret, "{t}.{rawBody}"). Recompute over the raw body and compare; reject iftis too old (replay protection). - Also sent:
SonoVault-Event-Id(stable across retries — dedupe on it) andSonoVault-Event-Type. - Return any 2xx to acknowledge. Non-2xx / timeouts are retried with exponential backoff (~10s → 6h, given up after 12 attempts); sustained failures auto-disable the endpoint.
Event payload
stream.play.started—{ id, type, created, data: { stream_id, started_at, track } }. (A track-end event isn't offered yet — there's no reliable end signal.)trackis the standard public track object (same shape as search results).
Example response
List webhooks
List your webhook endpoints. The signing secret is never returned after creation.
Example response
Update a webhook
Update an endpoint's url, event_types, and/or enabled. Re-enabling a disabled endpoint clears its failure streak. Send at least one field.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | required | Webhook id. |
Example response
Delete a webhook
Delete an endpoint. Delivery stops immediately; past delivery history is retained.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | required | Webhook id. |
Example response
Send a test event
Deliver a sample stream.play.started event to the endpoint right now (signed exactly like a real event) and report the result.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | required | Webhook id. |
Example response
Recent deliveries
The endpoint's recent delivery attempts (status, HTTP response, attempt count) — for debugging integrations.
Path parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | required | Webhook id. |
Example response
Live feed (SSE)
A Server-Sent Events stream of your stream-play events in real time, across all your streams — the push alternative to polling now-playing. Each message is an SSE event: line (stream.play.started) with the event JSON in data:. Authenticate with the x-api-key header; in the browser, proxy this through your own server so the key isn't exposed (the EventSource API can't send headers). Bundled with stream monitoring (no API credits).
Example response
Click Try it out on any endpoint to load it here.