API Reference
The AudioLib API allows you to fetch random audio tracks from curated libraries. It follows REST conventions and returns JSON responses.
http://api.audiolib.ai
Quick Start
Get a playable audio URL in one request.
cURL
curl -X POST http://api.audiolib.ai/v1/audio \
-H "Authorization: Bearer alp_your_api_key" \
-H "Content-Type: application/json" \
-d '{"library":"audio.focus"}'JavaScript
const res = await fetch('http://api.audiolib.ai/v1/audio', {
method: 'POST',
headers: {
'Authorization': 'Bearer alp_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({ library: 'audio.focus' }),
})
const json = await res.json()
if (json.code !== 0) throw new Error(json.msg || 'Audio request failed')
const { url, quota } = json.data
console.log('Remaining quota:', quota.remaining_quota)
new Audio(url).play()
DSH Plugin
dsh-plugin-audiolib turns AudioLib into an ambient soundtrack for DeepSeek Harness, driven by the agent's own session events: a turn opens and music plays, every turn closes and the room goes quiet. A track is never interrupted by a state change — the state you are in when a track ends decides what plays next.
Install
DSH manages profile plugins with pnpm, so enable it first. If you installed DSH with npx @deepseek-ai/dsh web, the CLI lives inside the profile and plain dsh is not on your PATH — reach it through npx instead.
corepack enable pnpm
npx @deepseek-ai/dsh plugin --profile web add dsh-plugin-audiolibcorepack ships with Node, so this adds no third-party global install, and corepack disable pnpm reverses it. Already have dsh on your PATH some other way? dsh plugin --profile web add dsh-plugin-audiolib works the same.
Add your API key
Restart the harness, then open Settings → Plugins → Plugin configuration and paste your key into the AudioLib soundtrack card. It goes to the DSH credential store (~/.dsh/.credentials.yaml, mode 600), never into a config file, and takes effect on the next track without a restart. The environment works too, if you prefer it:
export AUDIOLIB_API_KEY=alp_your_api_keyGenerate keys from your dashboard — the free tier is 300 requests/month.
Install a streaming player
Tracks stream: playback starts on the first buffered bytes, the way an AudioLib URL is meant to be consumed. That needs a player that reads a URL — mpv or ffplay, whichever is on PATH.
brew install mpv # macOS
apt install mpv # LinuxWithout one, the plugin falls back to macOS's built-in afplay, which only reads local files. It then downloads each track ahead of the moment it is needed — a working fallback, but it spends several megabytes per track and stalls when switching libraries mid-session.
Configure
Override the plugin row in your profile's cordis.patch.yml:
- id: audiolib
name: dsh-plugin-audiolib
config:
workingLibrary: audio.focus
idleLibrary: audio.ambient| Field | Default | Meaning |
|---|---|---|
| apiKeyRef | AUDIOLIB_API_KEY | Name of the credential holding the key — a reference, never the key itself |
| baseUrl | http://api.audiolib.ai/v1/audio | Audio endpoint |
| ambient | true | Let session events drive the soundtrack |
| workingLibrary | audio.focus | Plays while a turn is open; '' for silence |
| idleLibrary | '' | Plays once every turn has closed; '' for silence |
| exposeTools | true | Give the model music_play / music_stop / music_status |
| playerCommand | [] | Player argv; empty auto-selects. {url} declares a streaming player, {file} a file-only one |
| requestTimeoutMs | 15000 | AudioLib request deadline |
Any library id the API accepts works — see the library catalog for the full list.
Note: The card's library pickers are read-only for now — DSH serves only built-in plugins' settings sections to the browser. Set the libraries in cordis.patch.yml until that lifts. The key control is unaffected.
Tools
With exposeTools on, the model can score its own work:
| Tool | Behaviour |
|---|---|
| music_play(library) | Takes effect at the next seam; starts immediately when nothing is playing. |
| music_stop() | Stops now and stays silent until music_play is called again. |
| music_status() | Reports the playing track plus plan, remaining calls, and rate limit. Costs no API call — every audio response carries a quota snapshot. |
These are ordinary registrations on ctx.tools, so Code Mode can call them as await tools.music_play({ library }) too. An explicit choice lasts as long as the work does: when the last open turn closes, the soundtrack returns to idleLibrary.
Authentication
All API requests require a Bearer token in the Authorization header. Generate API keys from your dashboard.
Authorization: Bearer alp_your_api_key_hereKey format: API keys begin with alp_ followed by 32 random characters. Keys are shown only once upon creation. Store them securely.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/audio | Fetch a random audio track from a library |
Request Format
Send a JSON body with the library field set to a library's standard name.
| Field | Type | Required | Description |
|---|---|---|---|
| library | string | Yes | The standard name of the library (e.g. "audio.ambient") |
{
"library": "audio.ambient"
}Response Format
Success responses use a standard envelope. Audio fields are in data.
{
"code": 0,
"data": {
"title": "Deep Space Drift",
"url": "https://cdn.example.com/audio/track.m4a",
"duration_sec": 240,
"quota": {
"plan_name": "Free",
"total_quota": 300,
"used_quota": 7,
"remaining_quota": 293,
"is_unlimited": false,
"rate_per_minute": 60,
"period_end": 1782864000
}
},
"msg": "ok"
}Quota fields
Successful audio responses include the current quota snapshot in data.quota. Use it to show remaining usage or rate-limit information in your product.
| Field | Type | Description |
|---|---|---|
| quota.plan_name | string | Current plan display name. |
| quota.total_quota | number | Total audio calls available in the current period. |
| quota.used_quota | number | Audio calls already used in the current period. |
| quota.remaining_quota | number | Audio calls still available in the current period. |
| quota.is_unlimited | boolean | Whether the active plan has unlimited audio calls. |
| quota.rate_per_minute | number | Maximum audio API calls allowed per minute. |
| quota.period_end | number | Unix timestamp in seconds when the current quota period ends. |
Error Codes
| HTTP Status | Error Code | Description |
|---|---|---|
| 401 | MISSING_AUTH | Authorization header is missing or malformed |
| 401 | INVALID_KEY_FORMAT | API key does not match expected format (alp_...) |
| 401 | INVALID_KEY | API key is invalid, inactive, or revoked |
| 400 | INVALID_JSON | Request body is not valid JSON |
| 400 | MISSING_LIBRARY | The library field is missing from the request body |
| 404 | LIBRARY_NOT_FOUND | No library found with the given name |
| 404 | NO_AUDIO_ITEMS | The library has no active audio items |
| 429 | QUOTA_EXCEEDED | The current quota period has no remaining successful audio API calls |
| 503 | PRESIGN_FAILED | Temporary URL could not be generated (storage/signing error) |
| 500 | INTERNAL_ERROR | An unexpected server error occurred |
{
"error": "Library 'audio.unknown' not found",
"code": "LIBRARY_NOT_FOUND"
}Examples
cURL
curl -X POST http://api.audiolib.ai/v1/audio \
-H "Authorization: Bearer alp_your_api_key" \
-H "Content-Type: application/json" \
-d '{"library":"audio.sleep"}'JavaScript (fetch)
const response = await fetch('http://api.audiolib.ai/v1/audio', {
method: 'POST',
headers: {
'Authorization': 'Bearer alp_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({ library: 'audio.focus' }),
})
const json = await response.json()
if (json.code !== 0) throw new Error(json.msg || 'Audio request failed')
console.log(json.data.url) // Play this URL
console.log(json.data.quota.remaining_quota) // Show remaining quotaPython (requests)
import requests
response = requests.post(
'http://api.audiolib.ai/v1/audio',
headers={'Authorization': 'Bearer alp_your_api_key'},
json={'library': 'audio.ambient'}
)
data = response.json()
if data['code'] != 0:
raise RuntimeError(data.get('msg', 'Audio request failed'))
print(data['data']['url'])
print(data['data']['quota']['remaining_quota'])