Verbiage

XRPC API Reference

created just now :: modified just now :: 11.8 KB .md

Tools

download raw

XRPC API Reference#

The radio backend serves AT Protocol XRPC endpoints at /xrpc/{nsid}. Every

endpoint listed here requires a service JWT in the Authorization: Bearer <token>

header, scoped to the called NSID via the lxm claim, and the caller's DID must be

on the admin whitelist.

All endpoints share the data shapes defined in the pet.nkp.radio lexicon; see

Shared Types at the bottom.

Authentication#

🔒 marks an authenticated endpoint. Every endpoint below is authenticated.

A request is accepted only when both checks pass:

1. Method binding — the service JWT's lxm (lexicon method) claim must equal the

NSID being called. A mismatch returns AuthenticationRequired (401).

2. Admin whitelist — the caller's DID (the JWT issuer) must be on the server's

admin whitelist. A non-admin DID returns AdminRequired (403); a whitelist

lookup failure returns InvalidRequest (500).

FailureErrorStatus
lxm claim does not match the NSIDAuthenticationRequired401
Caller DID not on admin whitelistAdminRequired403
Whitelist lookup failedInvalidRequest500

Example service-auth request in fish:

set pds https://example-pds.test
set access_jwt "..."
set service_did did:web:radio.example.com
set lxm pet.nkp.radio.songs.upload

set service_jwt (curl -sS \
  -H "authorization: Bearer $access_jwt" \
  "$pds/xrpc/com.atproto.server.getServiceAuth?aud=$service_did&lxm=$lxm" \
  | jq -r .token)


Queue#

pet.nkp.radio.queue.list 🔒#

query — Load the current radio snapshot, including playback state and the

upcoming queue.

Params: none.

Response:

FieldTypeNotes
snapshotradioSnapshotCombined playback state, current song, and queue.

Errors: AuthenticationRequired, AdminRequired, InvalidRequest.


pet.nkp.radio.queue.modify 🔒#

procedure — Modify the radio queue. The action field selects the operation;

the companion fields it requires are enforced by the server.

Input (application/json):

FieldTypeRequiredNotes
actionstringyesOne of enqueue, remove, clear, reorder.
songIdsstring[]for enqueueSong ids to append, in order. Must be non-empty.
queueIdstringfor removeQueue item id to remove.
queueIdsstring[]for reorderQueue item ids in the desired final order.

Response:

FieldTypeNotes
snapshotradioSnapshotUpdated snapshot after the operation.

Errors: AuthenticationRequired, AdminRequired, InvalidRequest,

SongNotFound, QueueItemNotFound.

ErrorWhen
InvalidRequestMissing required companion field (e.g. songIds for enqueue), empty songIds, or an unknown action.
SongNotFoundAn enqueue song id does not exist.
QueueItemNotFoundA remove queue item id does not exist.


Songs#

pet.nkp.radio.songs.list 🔒#

query — List songs stored in the radio library.

Params: none.

Response:

FieldTypeNotes
songssong[]Songs ordered newest first.

Errors: AuthenticationRequired, AdminRequired, InvalidRequest.


pet.nkp.radio.songs.add 🔒#

procedure — Import one or more remote songs through the backend's URL importer

(HTTP(S) audio, playlists, or any yt-dlp-supported URL).

Input (application/json):

FieldTypeRequiredNotes
sourcessongUrlSource[]yesRemote audio sources to import, in order. 1–100 items.

Response:

Imports run asynchronously: the call returns as soon as the sources are queued,

so songs is normally empty and finished imports surface via the radio websocket

and subsequent queue.list calls. Use accepted to confirm how many sources

were queued for download.

FieldTypeNotes
acceptedintegerNumber of sources accepted and queued for asynchronous download.
songssong[]Imported or deduplicated songs. Empty while imports are still in progress.
snapshotradioSnapshotSnapshot taken after the import.

Errors: AuthenticationRequired, AdminRequired, InvalidRequest,

InvalidUrl, DownloadFailed, UnsupportedAudio.

Only validation errors are returned by this call, because the actual import runs

after the response. Each source is checked for a valid http(s) URL up front;

everything network-bound (yt-dlp, fetch, transcode) happens in the background,

so per-source failures are logged and reflected in later queue.list results

rather than returned here.

ErrorWhenReturned
InvalidRequestsources is empty or contains more than 100 items.synchronously
InvalidUrlA source URL is malformed or not http(s).synchronously
DownloadFailedFetching the URL, running yt-dlp, or reading a playlist entry failed — including a source that is removed, private, or region-locked.logged only (async)
UnsupportedAudioThe downloaded media is missing, unreadable, or an unsupported format.logged only (async)

Example:

set lxm pet.nkp.radio.songs.add
set service_jwt (curl -sS \
  -H "authorization: Bearer $access_jwt" \
  "$pds/xrpc/com.atproto.server.getServiceAuth?aud=$service_did&lxm=$lxm" \
  | jq -r .token)

curl -sS \
  -H "authorization: Bearer $service_jwt" \
  -H "content-type: application/json" \
  -d '{"sources":[{"url":"https://example.com/song.mp3","title":"song title","artist":"artist","album":"album","addToQueue":false}]}' \
  https://radio.example.com/xrpc/pet.nkp.radio.songs.add


pet.nkp.radio.songs.upload 🔒#

procedure — Upload a local audio file through the backend's multipart uploader.

This shares the same parsing, metadata enrichment, cover extraction, duplicate

handling, and optional queueing path as POST /api/songs.

Input (multipart/form-data):

FieldTypeRequiredNotes
filefileyesAudio file to store. Playlist files are rejected; use songs.add for playlist imports.
titlestringnoTitle override. If omitted, embedded tags and filename parsing are used.
artiststringnoArtist override. If omitted, embedded tags and filename parsing are used.
albumstringnoAlbum override.
genrestringnoGenre override.
durationSecondsintegernoDuration override in seconds.
addToQueuebooleannoQueue the uploaded song immediately. Default false.

Response:

FieldTypeNotes
songssong[]Uploaded song, or the existing deduplicated song.
snapshotradioSnapshotSnapshot taken after the upload.

Errors: AuthenticationRequired, AdminRequired, InvalidRequest,

UnsupportedAudio.

ErrorWhen
InvalidRequestMultipart parsing failed, title/artist could not be inferred, or the upload could not be saved.
UnsupportedAudioThe file field is missing, unreadable, or a playlist file sent to the single-song upload path.

Example:

set lxm pet.nkp.radio.songs.upload
set service_jwt (curl -sS \
  -H "authorization: Bearer $access_jwt" \
  "$pds/xrpc/com.atproto.server.getServiceAuth?aud=$service_did&lxm=$lxm" \
  | jq -r .token)

curl -sS \
  -H "authorization: Bearer $service_jwt" \
  -F "file=@./song.wav;type=audio/wav" \
  -F "title=uploaded song" \
  -F "artist=artist" \
  -F "album=album" \
  -F "genre=genre" \
  -F "durationSeconds=123" \
  -F "addToQueue=false" \
  https://radio.example.com/xrpc/pet.nkp.radio.songs.upload


Shared Types#

Defined in the pet.nkp.radio lexicon. nullable fields are present in responses

but may be null.

song#

Song metadata stored by the radio backend.

FieldTypeNullableNotes
idstringnoStable song id (1–128 chars).
titlestringnoSong title (1–512 chars).
artiststringnoSong artist (1–512 chars).
albumstringyesAlbum title.
genrestringyesGenre.
durationSecondsintegeryesDuration in seconds.
mimeTypestringyesStored audio MIME type.
hasCoverbooleannoWhether the song has cover art.
addedByDidstringnoDID that uploaded the song.
createdAtintegernoUnix timestamp of upload (≥ 0).
loudnessLufsstringyesIntegrated loudness in LUFS, as a decimal string.
loudnessPeakstringyesTrue peak in dBFS, as a decimal string.

queueItem#

Queue item joined with its song metadata.

FieldTypeNullableNotes
idstringnoStable queue item id (1–128 chars).
positionintegernoQueue position; lower values play first (≥ 1).
queuedByDidstringnoDID that queued the song.
songIdstringnoQueued song id (1–128 chars).
songsongnoFull metadata for the queued song.
titlestringnoQueued song title (1–512 chars).
artiststringnoQueued song artist (1–512 chars).
albumstringyesQueued song album.
durationSecondsintegeryesQueued song duration in seconds.
addedByDidstringnoDID that originally uploaded the song.

radioState#

Radio playback status persisted by the backend.

FieldTypeNullableNotes
currentSongIdstringyesCurrently active song id, when one is selected.
statusstringnoPlayback status: playing, paused, or stopped.
startedAtintegeryesUnix timestamp playback was last started.
pausedAtintegeryesUnix timestamp playback was last paused.
positionSecondsintegernoStored playback offset in seconds (≥ 0).
updatedByDidstringyesDID or backend actor that last updated state.

radioSnapshot#

Combined radio view returned to clients.

FieldTypeNullableNotes
stateradioStatenoCurrent playback state.
currentSongsongyesFull metadata for the current song.
nowPlayingsongyesCompatibility alias for currentSong.
queuequeueItem[]noUpcoming queued songs.

songUrlSource#

A remote audio source to import through the backend URL importer.

FieldTypeRequiredNotes
urlstring (uri)yesHTTP(S) audio, playlist, or yt-dlp-supported URL (8–4096 chars).
titlestringnoTitle override for plain audio URLs.
artiststringnoArtist override for plain audio URLs.
albumstringnoAlbum override.
addToQueuebooleannoQueue imported songs immediately. Default false.


Regenerating lexicons#

When lexicons change, regenerate the checked-in Rust types:

jacquard-codegen --input lexicons --output crates/radio-lexicons/src
cargo fmt --all


pet.nkp.radio.preferences#

Not an XRPC endpoint — a singleton AT Protocol record holding radio UI settings

(accentColor, theme ∈ {`light, dark, system}, updatedAt`). Volume

intentionally stays browser-local and is not stored here.