listener:read (see Authentication). Published media bytes stay anonymous.
HTTP rules
Access-Control-Allow-Origin: *on success and errors; useGETandOPTIONSfor JSON reads.- Send
Authorization: Beareron JSON reads and GraphQL. Do not send dashboard session cookies — they are not honored on listener routes. Cache-Control: no-storewith a strong SHA-256 ETag. SendIf-None-Matchfor304 Not Modified.- Unpublish, public offline, entitlement changes, and per-API switches apply on the next read.
- Poll no faster than
delivery.pollIntervalSecondsfrom the station document (currently 30 seconds).
404 NOT_FOUND (same CORS headers). A resource whose station module is switched off returns 404 MODULE_DISABLED.
Station entry document
contractVersion: 1. Check that integer in your client. The resources object links schemas, content, schedule, programs, current broadcast, now-playing, track history, weather, traffic, and public media. Disabled APIs set their href to null. resources.realtime holds the realtime feed connection details instead of a link, or null when the feed is off.
The optional profile object carries the station’s public profile for an about or contact page: tagline, description, frequency label, website and stream URLs, contact email, phone, address, and social links. Each value is null when the station has not set it. Calendar feeds, media bytes, and ingest outcomes use their own response shapes.
See Requests and response handling for runnable requests and Field types and empty states for real values and nullability. Each HTTP operation includes curated response examples alongside its schema.
Content collections
- Required query:
?type=<schema key>. limitbetween 1 and 50 (default 25); follownextCursor.- Optional order:
sort=publishedAt(default) orsort=updatedAt, withdirection=desc(default) ordirection=asc. - Optional filters, up to ten, on text and choice delivery fields:
filter=<field>:<value>for an exact matchfilter=<field>.contains:<value>for a case-insensitive substringfilter=<field>.anyOf:<a>,<b>for any of the listed values
publishedAt for the current snapshot and firstPublishedAt for the first publication. related.programIds lists programs the record relates to.
To route website URLs by slug, use GET …/content/{type}/by-slug/{slug}. A slug the record used before still returns the record. Compare values.slug in the response with the requested slug and redirect to the current one when they differ.
Only published snapshots appear. Drafts and private fields never leak through public filters.
Schedule and on-air
GET …/schedule?fromDate=&throughDate=— at most 31 inclusive days and 1000 broadcasts per request.GET …/programs— programs in that window, with their presenters and regular weeklyslots.GET …/broadcast/current— live or current-window broadcast from public data.
nature: live, prerecorded, or repeat.
Now playing and track history
These reflect encoder observations, not the planned rundown.- Now playing — freshness (
fresh/stale/empty); timing fields computed at response time. - Track history — paginated observations; duplicate titles at different times stay distinct.
Public realtime feed
The station document’sresources.realtime describes the station’s public feed: a WebSocket url and the channel its events use. It is null when the station has switched the feed off.
url without a token; the feed is public, so browsers can connect directly. The connection starts with the current state, then sends one event for each real change. Reconnect with backoff when the connection closes.
Each message is JSON. An event has type: "event" and a data object with events, channels, and a payload row. Ignore events whose channels don’t include your channel. Each row carries a station_id, a kind, an entry, and a data_json frame. Keep only rows whose station_id matches the station document’s id. An event name ending in .delete means the row was removed. A message with type: "error" reports a problem with the connection.
The frame kinds are:
schedule— public broadcasts from today through the next seven dayson-air— the live broadcast’s planned rundown position, ornullnow-playing— the reported track, in the same shape asGET …/now-playingcontent— one published record; a removed row means the record was unpublished
data_json schedule frame with no broadcasts:
subscribePublicFeed, version 0.3.0 or later. It connects, reconnects, filters rows, and dispatches frames to typed handlers.
on-air describes the planned rundown, while now-playing carries reported automation metadata. Their meanings are separate. Editorial previews never appear on the feed.
Listener push devices
Device changes use a credential withpush:register. The provider must match the station’s configured provider. A provider device token is separate from the RadioTools API credential.
POST …/push/devices to register or refresh a device. PATCH accepts token plus topics, for example { "alerts": false, "content": true }; omitted topics keep their current preference. DELETE accepts token to remove the device. The imported contract documents a 200 result without a response-body schema, so do not depend on a JSON acknowledgment.
Public media
/api/media/public/{stationId}/{versionId}[/{variant}] — supports a single Range request; private, no-store; not shared-cacheable.
Image variants are thumb and display, resized to width, and card (16:9) and square, cropped around the image’s focal point. Each record media item lists its rendition URLs in variants, plus alt, width, and height for accessible, responsive images. A variant is null when it is not available.
Framework note
Do not rely on Next.jsfetch segment caching for listener JSON. Revalidate in your app or poll within the documented interval.
Interactive operation docs: use the Public listener group in the sidebar.
Implement refresh and removal
Keep the ETag with the body it represents. Send it on the next authorized read, and reuse that body only when the server returns304. Do not treat an authentication failure or 404 as a cache hit.
Use realtime feed updates as a reason to fetch current data. Revalidate after reconnecting and when content is unpublished. A locally cached story can remain visible in your own app until your consumer removes it; the RadioTools response policy does not clear your app’s storage for you.
Check a missing listener resource
Confirm the station slug, token scope, publication state, and entry-document resource link. A disabled link isnull; do not manufacture a route for it. Weather and traffic also require the station’s recorded redistribution permission.