codai docs
Gateway

Music Room

codai Studio Music Room — a team of role agents writes, plays, sings and mixes a song that sounds played by people, with three approval gates, a monthly plan quota and an editable audio creative.

The Music Room is a team of role agents that produces one song together: a producer plans it, a composer and a lyricist write it, an arranger builds the form, session players write their parts on real sampled instruments, a vocal producer adds the vocals, mix and mastering engineers finish it, a critic listens, and a rights check compares the melody against a reference corpus. The result is a normal audio creative: every note any agent wrote is an editable op with undo, and you can take over in the multitrack editor at any time.

MethodPathResult
POST/v1/studio/music-roomStart a song (202, Idempotency-Key honoured)
GET/v1/studio/music-roomYour songs, newest first (?limit 1–100, ?before ISO date)
GET/v1/studio/music-room/quota{ plan, included, used, remaining, period_end }
GET/v1/studio/music-room/{id}The song; reading it advances the workflow
POST/v1/studio/music-room/{id}/chooseAnswer the open gate
POST/v1/studio/music-room/{id}/directTalk to one player (202, Idempotency-Key honoured)

How a song is made

brief ─► plan ─► [gate 1: choose a plan variant] ─► compose ─► lyrics ─► arrange ─► parts
      ─► humanise ─► demo render ─► [gate 2: approve the demo or direct a player]
      ─► vocals ─► mix ─► master ─► critique (at most 2 rounds of fixes) ─► [gate 3: approve the final mix] ─► ready

Agents never write audio directly. They write symbolic parts on a beat grid; deterministic code humanises timing and dynamics per player; codai-studio-media renders the stems with sampled instruments; ACE-Step sings the vocals over a rendered guide, checked by speech recognition and repaired syllable by syllable. The critic scores groove, harmony, melody, arrangement, mix, humanness and genre fit and sends targeted fixes back to the players at most twice. robot_score (0 human … 1 robotic) is a symbolic measure of the usual "AI tells", and copy_risk reports melody similarity to known tunes.

Starting a song

curl https://ai.codai.ro/v1/studio/music-room \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: song-42" \
  -d '{
    "brief": "a cheerful wedding song about two friends who finally meet again",
    "language": "ro",
    "genre": "hora",
    "duration_seconds": 150,
    "vocals": true
  }'
FieldDefaultNotes
briefrequired10–2000 characters: occasion, mood, story, audience; no artist names
languageroro or en — the language of the lyrics
genrethe producer picksA genre pack id: pop, jingle, rock, indie, hiphop, trap, cinematic, lofi, ballad, hora, sarba, doina, colind, lautareasca, manele, pop_folk_ro, fanfara, cocek, kolo…
duration_seconds—6–240 (four minutes at most)
vocalstruefalse for an instrumental
timbrechosen for the genreA vocal timbre id
voice_passport_id—Sing with your own voice (beta): one of your custom voices with an active singing consent. See Voice passport
instruments—Instrument ids the band must use (drums.kit_rock, balkan.accordion…)
autofalseSkip the three gates (plan variant 0, demo and final approved)
budget_micro_usd10000000Cap for one song beyond the quota, at most 20000000 ($20)
title—Up to 200 characters

The answer is 202 with the song (object: "studio.music_room"). Poll GET /v1/studio/music-room/{id} every 20–60 seconds; the background sweeper also moves the song forward when nobody polls.

Voice passport (beta)

Your own custom voice can sing your songs once you give a separate, revocable singing consent for it: in the hub voice card "Singing (beta)" you read the singing statement aloud and record a short sung sample (the same in-app liveness check as when the voice was created), which calls POST /v1/studio/voices/{id}/singing-consent. DELETE /v1/studio/voices/{id}/singing-consent revokes only the singing purpose; deleting the voice revokes everything. The vocal producer passes your sung sample to ACE-Step as a timbre reference, so the result approximates your timbre; it is not a faithful clone. Available only where voice cloning is enabled for your account.

warnings tells you what happened: voice_passport_beta (sung with your voice), voice_passport_needs_singing_consent (no active singing consent, or revoked during the song), voice_passport_not_found (not one of your active voices), voice_passport_unavailable (voice cloning not enabled for your account, or the voice is paused), voice_passport_lyrics_refused (the lyrics failed the voice-safety check). In every case except the first the song is still made with timbre.

The three gates

When the song reaches a gate it answers status: "waiting" with gate.kind set, and nothing runs until you answer:

GateWhat you seeAnswer
planUp to 3 plan variants in gate.variants: title, genre, BPM, meter, mode, sections and instrumentation, each with the producer's rationale{"gate": "plan", "variant": 0} (0–2)
demoThe arranged demo in demo_asset_id and the team with each player's rationale{"gate": "demo", "approve": true}
finalThe final mix in final_asset_id, stems in stems_asset_ids, the critic's last review{"gate": "final", "approve": true}
curl https://ai.codai.ro/v1/studio/music-room/$ID/choose \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"gate": "plan", "variant": 1}'

At the demo or final gate you can also direct one player, like a producer in the studio. The player re-plays its part (one section or the whole song) and the demo is re-rendered:

curl https://ai.codai.ro/v1/studio/music-room/$ID/direct \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"role": "drums", "section_id": "chorus1", "direction": "lay back on the chorus, fewer fills, more ghost notes"}'

role is one of producer, composer, lyricist, arranger, drums, bass, guitar, keys, strings, brass, winds, balkan_lead, balkan_rhythm, percussion, synth, vocal_producer, sound_designer, mix_engineer, mastering_engineer, critic, rights; direction is at most 500 characters. A gate left unanswered for 7 days fails the song (error: "gate_expired") and releases any hold.

Quota and budget

Songs are counted per calendar month (UTC): every Music Room song that reached ready or is still running.

PlanSongs included per month
Pro4
Builder12
Max30
Team10 per seat
Free, Lite, Student, BYOK1

Within the quota a song is free for you (quota.within: true). Beyond it every step is quoted, held on your wallet and settled with the real cost — 402 insufficient_funds when the wallet is short. budget_micro_usd caps one song either way; spent_micro_usd shows what it cost so far. Agent turns are token-metered under the public model codai (usage model codai::music-<role>, prompt caching on). Check what is left with GET /v1/studio/music-room/quota.

Errors

StatusCodeWhen
400invalid_requestThe body does not match the schema
402insufficient_fundsBeyond the quota and the wallet cannot cover the next step
404not_foundUnknown id, or another account's song
409not_at_gatechoose or direct while the song is not waiting at that gate
429too_many_music_roomsTwo songs are already running for you
503media_ops_unavailableThe music renderer is not configured on this gateway

Credits

credits lists the lines to publish with the song: CC-BY sample libraries used by the players and "Built with Llama" when a symbolic helper ran. Every output carries AI marking like the rest of codai Studio.

SDKs

const room = await codai.studio.musicRoom.create(
  { brief: 'a cheerful wedding song about two old friends', language: 'ro', genre: 'hora' },
  { idempotencyKey: 'song-42' },
);
let r = await codai.studio.musicRoom.wait(room.id, { untilGate: true });
if (r.gate?.kind === 'plan') r = await codai.studio.musicRoom.choose(r.id, { gate: 'plan', variant: 0 });
await codai.studio.musicRoom.direct(r.id, { role: 'drums', direction: 'fewer fills' });
const { remaining } = await codai.studio.musicRoom.quota();
room = client.studio.music_room.create("a cheerful wedding song about two old friends", language="ro", genre="hora")
r = client.studio.music_room.wait(room["id"], until_gate=True)
if r["gate"] and r["gate"]["kind"] == "plan":
    r = client.studio.music_room.choose(r["id"], "plan", variant=0)
print(client.studio.music_room.quota()["remaining"])

TypeScript: studio.musicRoom.create / get / list / choose / direct / quota / wait (wait(id, { untilGate, intervalMs, timeoutMs, signal })). Python: studio.music_room.create / get / list / choose / direct / quota / wait (wait(id, until_gate=, interval=, timeout=)).

Agent tools

The codai desktop, phone and VS Code agents and the MCP Studio toolset use the same workflow through four tools: music_room_create (start a song; auto only when you asked to just make it), music_room_status (read and advance it), music_room_choose (answer a gate with your decision) and music_room_direct (talk to one player).

On this page