lacuna-music
Generate AI music programmatically through the Lacuna Music API.
When to use this
Activate when the user wants to:
- Generate a track from a style description (
"lofi piano, 70 bpm", "synthwave, retro drums")
- Add background music to a video, demo, presentation, or app
- Produce a jingle, intro, outro, or stinger from a brief
- Compose a vocal song from custom lyrics
Pick a transport
Three packages, same underlying API. Pick the first that fits the user's environment:
| When the user is using… |
Use |
| Claude Code, Claude Desktop, Cursor, Zed, or another MCP client |
lacuna-mcp |
| A Node / TypeScript script or backend |
lacuna-sdk |
| Their shell, a CI job, a one-off prompt |
lacuna-toolkit |
lacuna-mcp (preferred for AI agents)
claude mcp add lacuna -- npx -y lacuna-mcp
Set LACUNA_API_KEY in the MCP env block. After adding, three tools become available: generate_music, get_generation, wait_for_generation. Call generate_music then wait_for_generation to receive the final audio_url.
lacuna-sdk
import Lacuna from 'lacuna-sdk'
const lacuna = new Lacuna({ apiKey: process.env.LACUNA_API_KEY })
const task = await lacuna.music.generations.create({
style: 'lofi piano, 70 bpm, mellow',
title: 'Study session',
instrumental: true,
})
const finished = await lacuna.music.generations.waitFor(task.id)
console.log(finished.tracks[0]?.audio_url)
lacuna-toolkit
npx lacuna-toolkit music generate \
--style "synthwave, retro drums, 110 bpm" \
--title "Neon Drive" \
--instrumental \
--wait \
--output json
Authentication
Get a key at lacuna.fm/profile/api. It begins with lyr_live_ and is shown once at creation. Pass it via LACUNA_API_KEY env var.
Music API access requires a Pro plan or above. Lower tiers receive 403 permission_error / tier_insufficient — do not retry; tell the user to upgrade.
Generation parameters
| Field |
Required |
Models |
Notes |
style |
yes |
all |
Free-text style description, up to 1000 chars. |
title |
yes |
all |
Track title. |
lyrics |
yes if not instrumental |
all |
Plain text, up to 5000 chars. Use [Verse] / [Chorus] markers. |
instrumental |
no |
all |
true skips lyrics. |
model |
no |
all |
aether (default) or echo. See model table below. |
vocal_gender |
no |
aether |
'm' or 'f' — lead vocal hint. |
negative_tags |
no |
aether |
Style tags to avoid. |
style_weight |
no |
aether |
0–1. |
weirdness_constraint |
no |
aether |
0–1. |
audio_weight |
no |
aether |
0–1. |
The API rejects model-incompatible fields with 400 invalid_param (e.g. passing style_weight with model: 'echo').
Models
| Codename |
Best for |
Notes |
aether |
Default. General-purpose, supports vocals + advanced weight knobs. |
Supports vocal-gender and weight controls. |
echo |
Full structured tracks up to three minutes. |
Length is model-determined; no vocal-gender / weight knobs. |
Lifecycle
create returns immediately with a task in pending status.
- Generation typically completes in 60–120 seconds.
waitFor (SDK) / --wait (CLI) / wait_for_generation (MCP) polls until ready or failed.
- On
ready, task.tracks[] contains one or more renders, each with audio_url, duration, title, lyrics, image_url, tags.
- On
failed, inspect task.error — credits are refunded automatically.
For production workflows, prefer the job.completed webhook over polling. See the SDK webhook docs for verification helpers.
Credits and pricing
- Cost depends on the selected model — confirm on the pricing page.
- Failed generations refund automatically.
- If a request returns
402 insufficient_credits, do not retry — tell the user to top up.
Working with audio_url
audio_url points to the generated output on Lacuna's CDN and does not have a 24-hour expiry. Downloading a separate copy is optional and depends on the user's own storage or processing workflow.
Constraints to respect
- Use the default 5-second polling interval unless the workflow has a specific reason to change it.
- Don't hardcode API keys; always read from
LACUNA_API_KEY.
- Don't retry on
403 tier_insufficient or 402 insufficient_credits — these are user-action errors.
- On
503 model_unavailable, the requested model is temporarily circuit-broken (error.model names which one). Switch to a different model and retry; do not loop on the same one. The SDK does not auto-fallback because each model has a different credit cost.
Resources
1---2name: lacuna-music3description: Generate AI music tracks via the Lacuna Music API. Use when the user wants AI-generated music, BGM, jingles, soundtrack stingers, lofi/synthwave/orchestral/any-genre tracks, vocal songs from custom lyrics, or any on-demand audio generation. Triggers on phrases like "generate music", "make me a track", "compose a song", "AI music", "background music", "BGM for X", "jingle", "soundtrack".4---56# lacuna-music78Generate AI music programmatically through the [Lacuna Music API](https://lacuna.fm).910## When to use this1112Activate when the user wants to:13- Generate a track from a style description (`"lofi piano, 70 bpm"`, `"synthwave, retro drums"`)14- Add background music to a video, demo, presentation, or app15- Produce a jingle, intro, outro, or stinger from a brief16- Compose a vocal song from custom lyrics1718## Pick a transport1920Three packages, same underlying API. Pick the first that fits the user's environment:2122| When the user is using… | Use |23| ------------------------------------------------------------------ | ----------- |24| Claude Code, Claude Desktop, Cursor, Zed, or another MCP client | `lacuna-mcp` |25| A Node / TypeScript script or backend | `lacuna-sdk` |26| Their shell, a CI job, a one-off prompt | `lacuna-toolkit` |2728### `lacuna-mcp` (preferred for AI agents)2930```sh31claude mcp add lacuna -- npx -y lacuna-mcp32```3334Set `LACUNA_API_KEY` in the MCP env block. After adding, three tools become available: `generate_music`, `get_generation`, `wait_for_generation`. Call `generate_music` then `wait_for_generation` to receive the final `audio_url`.3536### `lacuna-sdk`3738```ts39import Lacuna from 'lacuna-sdk'4041const lacuna = new Lacuna({ apiKey: process.env.LACUNA_API_KEY })4243const task = await lacuna.music.generations.create({44 style: 'lofi piano, 70 bpm, mellow',45 title: 'Study session',46 instrumental: true,47})4849const finished = await lacuna.music.generations.waitFor(task.id)50console.log(finished.tracks[0]?.audio_url)51```5253### `lacuna-toolkit`5455```sh56npx lacuna-toolkit music generate \57 --style "synthwave, retro drums, 110 bpm" \58 --title "Neon Drive" \59 --instrumental \60 --wait \61 --output json62```6364## Authentication6566Get a key at [lacuna.fm/profile/api](https://lacuna.fm/profile/api). It begins with `lyr_live_` and is shown once at creation. Pass it via `LACUNA_API_KEY` env var.6768Music API access requires a **Pro** plan or above. Lower tiers receive `403 permission_error / tier_insufficient` — do not retry; tell the user to upgrade.6970## Generation parameters7172| Field | Required | Models | Notes |73| ---------------------- | ----------------------- | ------------------ | ----------------------------------------------------------------------- |74| `style` | yes | all | Free-text style description, up to 1000 chars. |75| `title` | yes | all | Track title. |76| `lyrics` | yes if not instrumental | all | Plain text, up to 5000 chars. Use `[Verse]` / `[Chorus]` markers. |77| `instrumental` | no | all | `true` skips lyrics. |78| `model` | no | all | `aether` (default) or `echo`. See model table below. |79| `vocal_gender` | no | aether | `'m'` or `'f'` — lead vocal hint. |80| `negative_tags` | no | aether | Style tags to avoid. |81| `style_weight` | no | aether | 0–1. |82| `weirdness_constraint` | no | aether | 0–1. |83| `audio_weight` | no | aether | 0–1. |8485The API rejects model-incompatible fields with `400 invalid_param` (e.g. passing `style_weight` with `model: 'echo'`).8687### Models8889| Codename | Best for | Notes |90| -------- | ------------------------------------------------------------------ | ------------------------------------------------------------------- |91| `aether` | Default. General-purpose, supports vocals + advanced weight knobs. | Supports vocal-gender and weight controls. |92| `echo` | Full structured tracks up to three minutes. | Length is model-determined; no vocal-gender / weight knobs. |9394## Lifecycle95961. `create` returns immediately with a task in `pending` status.972. Generation typically completes in **60–120 seconds**.983. `waitFor` (SDK) / `--wait` (CLI) / `wait_for_generation` (MCP) polls until `ready` or `failed`.994. On `ready`, `task.tracks[]` contains one or more renders, each with `audio_url`, `duration`, `title`, `lyrics`, `image_url`, `tags`.1005. On `failed`, inspect `task.error` — credits are refunded automatically.101102For production workflows, prefer the `job.completed` webhook over polling. See [the SDK webhook docs](https://www.npmjs.com/package/lacuna-sdk) for verification helpers.103104## Credits and pricing105106- Cost depends on the selected model — confirm on the [pricing page](https://lacuna.fm/pricing).107- Failed generations refund automatically.108- If a request returns `402 insufficient_credits`, do not retry — tell the user to top up.109110## Working with `audio_url`111112`audio_url` points to the generated output on Lacuna's CDN and does not have a 24-hour expiry. Downloading a separate copy is optional and depends on the user's own storage or processing workflow.113114## Constraints to respect115116- Use the default 5-second polling interval unless the workflow has a specific reason to change it.117- Don't hardcode API keys; always read from `LACUNA_API_KEY`.118- Don't retry on `403 tier_insufficient` or `402 insufficient_credits` — these are user-action errors.119- On `503 model_unavailable`, the requested model is temporarily circuit-broken (`error.model` names which one). Switch to a different model and retry; do not loop on the same one. The SDK does **not** auto-fallback because each model has a different credit cost.120121## Resources122123- API and pricing: <https://lacuna.fm>124- Get an API key: <https://lacuna.fm/profile/api>125- SDK: [`lacuna-sdk`](https://www.npmjs.com/package/lacuna-sdk)126- CLI: [`lacuna-toolkit`](https://www.npmjs.com/package/lacuna-toolkit)127- MCP server: [`lacuna-mcp`](https://www.npmjs.com/package/lacuna-mcp)128- Source code: <https://github.com/JOYLINK-LTD/lacuna-toolkit>