Skip to content

Music

Build a deterministic personalized queue from Brendan’s canonical tracks

POST /api/music/station.json

Read-only bounded structured query over the playable collection. The UTF-8 request body is limited to 24576 bytes. Inputs are canonical affinities, direct votes, recent tracks, optional intent, closed local context, and optional version-1 continuation (up to 120 timestamped plays, 12 buffered canonical IDs, and sequence position). All track attributes are rehydrated from D1; the server retains no listener state. Legacy requests without continuation retain their response shape and hard recency exclusions. Continuation responses additionally report cooldown relaxation and applied context signals.

Markdown · Authoritative OpenAPI

Request policy

Operation
buildMusicStation
Access
anonymous
Automatic retries
None
Redirects
Returned to the caller without following
Request body limit
24576 UTF-8 bytes
Pagination
No collection iterator

Parameters

No path or query parameters.

Request body

application/json

MusicStationRequestV1 required

Schema details

schemaVersion unknown required

stationSeed string required

affinities object[] required

Schema details

object

Schema details

canonicalId string required

weight number required

reasonCodes string[] required

Schema details

string

Allowed: "recording-id" | "recording-name" | "artist-id" | "artist-name" | "album-id" | "album-name" | "genre" | "semantic" | "avoid"

votes object[] required

Schema details

object

Schema details

canonicalId string required

direction string required

Allowed: "more" | "less"

evidence string

Allowed: "explicit" | "completion" | "early-skip"

weight number

hardExclusions string[] required

Schema details

string

recentCanonicalIds string[] required

Schema details

string

intent string

genre string

context object

Schema details

timeOfDay string required

Allowed: "dawn" | "morning" | "afternoon" | "evening" | "night"

weatherKind string required

Allowed: "clear" | "cloudy" | "fog" | "drizzle" | "rain" | "snow" | "storm" | "unknown"

calendarKind string required

Allowed: "weekday" | "weekend" | "holiday"

daylight string

Allowed: "day" | "night" | "unknown"

season string

Allowed: "spring" | "summer" | "autumn" | "winter" | "unknown"

continuation object

Schema details

version unknown required

played object[] required

Schema details

object

Schema details

canonicalId string required

playedAt number required

queuedCanonicalIds string[] required

Schema details

string

position number required

limit number

Request headers

  • API-Version (optional) — Header-based API version selection. Omit it, send an empty value, or send 1.0.0; surrounding whitespace is ignored.
  • Idempotency-Key (optional) — Accepted for clients that attach a key uniformly. GET, HEAD, and OPTIONS are already safe and intrinsically idempotent, so read responses are not stored in an idempotency replay cache.

Responses

200

Canonical personalized queue, honest empty result, or bounded degraded fallback

Response headers
{
  "Content-Usage": {
    "description": "IETF AIPREF usage preferences for this public content",
    "schema": {
      "type": "string",
      "examples": [
        "train-ai=y, search=y"
      ]
    }
  },
  "Content-Signal": {
    "description": "Compatibility AI usage preferences for deployed crawlers",
    "schema": {
      "type": "string",
      "examples": [
        "ai-train=yes, search=yes, ai-input=yes"
      ]
    }
  },
  "API-Version": {
    "description": "Selected public REST contract version",
    "schema": {
      "type": "string",
      "enum": [
        "1.0.0"
      ]
    }
  },
  "RateLimit-Policy": {
    "description": "Public REST quota policy: 1000 Worker requests per 60 seconds for each source and Cloudflare location",
    "schema": {
      "type": "string"
    }
  },
  "RateLimit-Limit": {
    "description": "Compatibility form of the current public REST request limit",
    "schema": {
      "type": "integer",
      "enum": [
        1000
      ]
    }
  }
}

application/json

MusicStationResultV1

Schema details

schemaVersion unknown required

status string required

Allowed: "ready" | "empty" | "degraded"

profile object required

Schema details

seedCount number required

positiveVoteCount number required

negativeVoteCount number required

intentApplied boolean required

contextApplied boolean required

tracks object[] required

Schema details

object

Schema details

canonicalId string required

title string required

artist string required

genres string[] required

Schema details

string

spotifyUri string required

spotifyUrl string required (uri)

itemPath string required

reasonCodes string[] required

Schema details

string

Allowed: "profile-affinity" | "session-more" | "intent-adjacent" | "context-adjacent" | "musical-neighbor" | "exploration" | "editorial-core" | "editorial-recurrent" | "underplayed" | "contrast" | "discovery" | "transition-fit"

programming object

Schema details

role string required

Allowed: "core-opener" | "adjacent" | "deep-cut" | "listener-affinity" | "contrast" | "discovery"

rotation string required

Allowed: "core" | "recurrent" | "deep-cut" | "discovery"

diagnostics object required

Schema details

profileVersion string required

candidateCount number required

eligibleCount number required

fallback boolean required

cooldownRelaxed boolean

contextSignals string[]

Schema details

string

Allowed: "time-of-day" | "weather" | "daylight" | "season"

clock string

slots object[]

Schema details

object

Schema details

position number required

role string required

Allowed: "core-opener" | "adjacent" | "deep-cut" | "listener-affinity" | "contrast" | "discovery"

candidateCount number required

shortlistSize number required

selectedRank number required

Schema details

number

unknown

relaxedRules string[] required

Schema details

string

Allowed: "artist-separation" | "sound-family-separation" | "album-separation"

400

Invalid station request

application/problem+json

ProblemDetails

Schema details

type string required (uri)

Problem type URI (see /problems/)

title string required

Short human-readable summary

status number required

HTTP status code

detail string required

Human-readable explanation

instance string (uri)

URI reference for this occurrence

413

Request exceeds the 24 KiB limit

application/problem+json

ProblemDetails

Schema details

type string required (uri)

Problem type URI (see /problems/)

title string required

Short human-readable summary

status number required

HTTP status code

detail string required

Human-readable explanation

instance string (uri)

URI reference for this occurrence

415

Content-Type is not application/json

application/problem+json

ProblemDetails

Schema details

type string required (uri)

Problem type URI (see /problems/)

title string required

Short human-readable summary

status number required

HTTP status code

detail string required

Human-readable explanation

instance string (uri)

URI reference for this occurrence

429

Public REST request limit exceeded

Response headers
{
  "Retry-After": {
    "description": "Seconds before the request should be retried",
    "schema": {
      "type": "integer",
      "enum": [
        60
      ]
    }
  },
  "RateLimit": {
    "description": "Current quota state for the exhausted public-rest policy",
    "schema": {
      "type": "string"
    }
  },
  "Content-Usage": {
    "description": "IETF AIPREF usage preferences for this public content",
    "schema": {
      "type": "string",
      "examples": [
        "train-ai=y, search=y"
      ]
    }
  },
  "Content-Signal": {
    "description": "Compatibility AI usage preferences for deployed crawlers",
    "schema": {
      "type": "string",
      "examples": [
        "ai-train=yes, search=yes, ai-input=yes"
      ]
    }
  },
  "API-Version": {
    "description": "Selected public REST contract version",
    "schema": {
      "type": "string",
      "enum": [
        "1.0.0"
      ]
    }
  },
  "RateLimit-Policy": {
    "description": "Public REST quota policy: 1000 Worker requests per 60 seconds for each source and Cloudflare location",
    "schema": {
      "type": "string"
    }
  },
  "RateLimit-Limit": {
    "description": "Compatibility form of the current public REST request limit",
    "schema": {
      "type": "integer",
      "enum": [
        1000
      ]
    }
  }
}

application/problem+json

ProblemDetails

Schema details

type string required (uri)

Problem type URI (see /problems/)

title string required

Short human-readable summary

status number required

HTTP status code

detail string required

Human-readable explanation

instance string (uri)

URI reference for this occurrence

503

Station service unavailable

application/problem+json

ProblemDetails

Schema details

type string required (uri)

Problem type URI (see /problems/)

title string required

Short human-readable summary

status number required

HTTP status code

detail string required

Human-readable explanation

instance string (uri)

URI reference for this occurrence

Examples

Examples use synthetic fixture values. Replace fixture identifiers with public IDs returned by the service. Request bodies and precise locations are transient; keep them out of logs.

curl

curl --fail-with-body --max-time 30 --max-filesize 16777216 --request POST 'https://irvinebroque.com/api/music/station.json' --header 'Accept: application/json' --header 'Content-Type: application/json' --data-binary @request.json

TypeScript

import { PublicApi } from '@irvinebroque/public-api';
const api = new PublicApi();
const result = await api.music.station({
  "body": {
    "schemaVersion": "1",
    "stationSeed": "fixture",
    "affinities": [],
    "votes": [],
    "hardExclusions": [],
    "recentCanonicalIds": []
  }
});
console.log(result.status, result.headers.get('content-type')); // Keep transient capabilities in memory.

CLI

irvinebroque music station --body-file request.json

request.json

{
  "schemaVersion": "1",
  "stationSeed": "fixture",
  "affinities": [],
  "votes": [],
  "hardExclusions": [],
  "recentCanonicalIds": []
}

The SDK preserves status, headers, and the server envelope. A 304 has no fresh body. Errors retain structured problem details; ordinary error messages omit response details. Responses are limited to 16 MiB and requests time out after 30 seconds by default.