Partner API · v1
Enliven Music API reference
The Enliven Music API lets approved partners generate, extend and export music from their own servers and automated pipelines. Access is provided under an individual partner agreement. If you have questions about your account, contact support@enlivenmusic.ai.
Overview
- Base URL:
https://api.enlivenmusic.ai/v1 - All requests and responses are JSON. Use HTTPS only.
- Generation is asynchronous. A request returns a job immediately with status
running; results are delivered by webhook or retrieved withGET /v1/jobs/{id}. - HTTP status codes are meaningful:
2xxsuccess,4xxa problem with the request or account,5xxa temporary problem on our side. - Model:
ultra-2.0(Ultra 2.0), the default and currently the only model on the API.
Authentication
Authenticate every request with your API key in the Authorization header. Keys begin with enl_live_. Keys are issued for server-side use only: never embed them in a browser, mobile app or public repository. If a key may have been exposed, ask us to revoke it and issue a new one.
Authorization: Bearer enl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxQuick start
Submit a generation, then poll the job until it finishes.
curl https://api.enlivenmusic.ai/v1/music/generate \
-H "Authorization: Bearer $ENLIVEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Night Shift",
"style": "tech house, 132 BPM, driving bassline, crisp hats, short vocal hooks, DJ-friendly 32-bar intro and outro",
"lyrics": "[Intro]\n\n[Hook]\nWe move all night\n\n[Outro]",
"duration_seconds": 270,
"style_strength": 0.7,
"creativity": 0.4,
"client_reference": "release-0142"
}'{
"id": "job_8sJd2kLq0VnRt5wXy7ZbA1cE",
"object": "job",
"type": "generate",
"status": "running",
"model": "ultra-2.0",
"created_at": "2026-03-18T09:30:12.000Z",
"completed_at": null,
"client_reference": "release-0142",
"billable": true,
"tracks": [],
"error": null
}curl https://api.enlivenmusic.ai/v1/jobs/job_8sJd2kLq0VnRt5wXy7ZbA1cE \
-H "Authorization: Bearer $ENLIVEN_API_KEY"A typical generation completes in one to four minutes. When polling, wait at least 10 seconds between requests for the same job.
Generate music
Creates a new song. Each request produces a job that normally contains two alternative tracks; a job counts as one generation regardless of the number of tracks returned.
| Field | Type | Required | Description |
|---|---|---|---|
| title | string | Yes | Track title. Up to 80 characters. |
| style | string | One of style / lyrics | Genre, mood, instrumentation, tempo and structure. Up to 1,000 characters. |
| lyrics | string | One of style / lyrics | Lyrics, optionally with section tags such as [Verse] and [Chorus]. Up to 5,000 characters. Not allowed when instrumental is true. |
| instrumental | boolean | No | true for a track without vocals. Default false. |
| duration_seconds | integer 10–360 | No | Target length in seconds. A target, not an exact length. If omitted, the model chooses. |
| model | string | No | Default ultra-2.0. |
| exclude_styles | string | No | Styles or traits to avoid. Up to 1,000 characters. |
| vocal_gender | "m" | "f" | No | Preferred vocal gender. Raises the likelihood; not guaranteed. Not available for instrumentals. |
| style_strength | number 0–1 | No | How closely the output follows the style description. Up to two decimals. |
| creativity | number 0–1 | No | Higher values allow more unexpected results. Up to two decimals. |
| quality_priority | number 0–1 | No | Weight given to audio quality over prompt adherence. Vocal tracks only. |
| variety | integer 0–4 | No | Variation between the generated tracks. 0 = closest to the style, 4 = most varied. Default 1. |
| client_reference | string | No | Your own identifier, returned on the job and in webhooks. Up to 255 characters. |
Tempo and length
BPM is not a separate parameter. Include the tempo in style (for example “132 BPM”); it is generally followed but not guaranteed to be exact, so verify tempo in your quality checks. For longer arrangements, set duration_seconds or extend a track.
Extend a track
Continues an existing track from a chosen point. The extension uses the same model as the source track and counts as one generation. Only tracks generated by your account can be extended.
| Field | Type | Required | Description |
|---|---|---|---|
| track_id | string | Yes | The id of a track from a succeeded job. |
| continue_at_seconds | number | No | Where the extension starts, in seconds. Greater than 0 and less than the source track's length. If omitted, the track continues from its end. |
| title | string | No | Up to 100 characters. |
| style | string | No | Up to 1,000 characters. Usually best kept close to the source track. |
| lyrics | string | No | Lyrics for the new section. Up to 5,000 characters. Not allowed when instrumental is true. |
| instrumental | boolean | No | Default false. |
| exclude_styles | string | No | Styles or traits to avoid. Up to 1,000 characters. |
| vocal_gender | "m" | "f" | No | Preferred vocal gender. Raises the likelihood; not guaranteed. Not available for instrumentals. |
| style_strength | number 0–1 | No | How closely the output follows the style description. Up to two decimals. |
| creativity | number 0–1 | No | Higher values allow more unexpected results. Up to two decimals. |
| quality_priority | number 0–1 | No | Weight given to audio quality over prompt adherence. Vocal tracks only. |
| variety | integer 0–4 | No | Variation between the generated tracks. 0 = closest to the style, 4 = most varied. Default 1. |
| client_reference | string | No | Your own identifier, returned on the job and in webhooks. Up to 255 characters. |
curl https://api.enlivenmusic.ai/v1/music/extend \
-H "Authorization: Bearer $ENLIVEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "track_id": "8b1f3c9e-...", "continue_at_seconds": 180, "style": "tech house, 132 BPM, breakdown then final drop" }'WAV conversion
Produces a WAV file for a track. WAV conversion is included in all plans and never counts against your allowance. Requesting the same track again returns the existing conversion job.
| Field | Type | Required | Description |
|---|---|---|---|
| track_id | string | Yes | The id of a track from a succeeded generate or extend job. |
| client_reference | string | No | Your own identifier. |
{
"id": "job_Q2w9...",
"object": "job",
"type": "wav",
"status": "succeeded",
"source_track_id": "8b1f3c9e-...",
"tracks": [{ "id": "8b1f3c9e-...", "wav_url": "https://cdn.enlivenmusic.ai/api/.../8b1f3c9e-....wav" }],
"error": null
}Jobs
Returns one job, or a newest-first list of your jobs (limit 1–100, default 20; page with starting_after set to the last id you received, while has_more is true).
Job status
| Field | Type | Required | Description |
|---|---|---|---|
| queued | Accepted, not yet started. | ||
| running | In progress. | ||
| succeeded | Finished. Tracks are available. | ||
| failed | Finished without a result. See error. Failed jobs are never counted against your allowance. |
Track object
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Track id. Use it for extend and WAV requests. | |
| title | string | Track title. | |
| duration_seconds | number | Actual length of the track. | |
| audio_url | string | MP3 file. | |
| image_url | string | Cover image. | |
| style | string | Style tags applied to the track. | |
| lyrics | string | Lyrics used for the track, if any. |
Jobs that have not finished after one hour are failed with generation_timeout and are not counted.
Account and usage
{
"object": "account",
"organization": "Example Records",
"status": "trial",
"plan": { "code": "trial", "name": "Trial" },
"period": { "start": "2026-03-18T00:00:00.000Z", "end": "2026-04-01T00:00:00.000Z", "expired": false },
"usage": {
"included_generations": 20,
"used_generations": 3,
"remaining_generations": 17,
"additional_generations": 0,
"failed_jobs": 1
},
"limits": {
"max_concurrent_jobs": 2,
"requests_per_minute": 10,
"additional_usage_enabled": false,
"additional_usage_price_cents": null,
"spend_cap_cents": null
}
}Webhooks
If a webhook URL is configured for your account, we send a POST to it when a job succeeds or fails. The URL must use HTTPS. Respond with any 2xx status within 10 seconds. Ask us to set or change the URL and to issue your signing secret (whsec_…).
POST https://your-server.example.com/webhooks/enliven
Content-Type: application/json
X-Enliven-Event: job.succeeded
X-Enliven-Signature: t=1792000000,v1=5f2b9c...
{
"id": "evt_8sJd2kLq0VnRt5wXy7ZbA1cE_1",
"type": "job.succeeded",
"created_at": "2026-03-18T09:32:40.000Z",
"data": { ...job object... }
}Verifying the signature
The signature is the hex HMAC-SHA256 of {t}.{raw request body} using your signing secret. Compute it over the raw body exactly as received, compare in constant time, and reject requests whose timestamp is more than five minutes old.
// Node.js
import crypto from "crypto";
export function verifyEnlivenWebhook(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1 || "");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}# Python
import hashlib, hmac, time
def verify_enliven_webhook(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t = int(parts.get("t", "0"))
if abs(time.time() - t) > 300:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))Retries
If delivery fails (timeout or non-2xx response), we retry up to five more times with increasing delays, over roughly the following hour. Webhooks can arrive more than once or out of order: use the job id to deduplicate. The job resource is always the source of truth, so poll GET /v1/jobs/{id} if a webhook does not arrive.
Errors
Errors return a JSON body with a stable machine-readable code:
{
"error": {
"code": "quota_exceeded",
"message": "The generation allowance for the current period has been used.",
"details": { "included": 20, "used": 20, "period_end": "2026-04-01T00:00:00.000Z" }
}
}| Field | Type | Required | Description |
|---|---|---|---|
| 400 invalid_request | A parameter is missing or invalid. details.field names it when applicable. | ||
| 401 invalid_api_key | The key is missing, malformed or revoked. | ||
| 402 quota_exceeded | The allowance for the current period is used and additional usage is not enabled. | ||
| 402 spend_cap_reached | The additional-usage spending cap for the period has been reached. | ||
| 403 trial_expired / period_ended | The trial or prepaid period has ended. | ||
| 403 account_suspended / account_closed | API access is not active for this account. | ||
| 404 job_not_found / track_not_found | The id does not exist for your account. | ||
| 429 rate_limited | Too many requests per minute. Retry after the Retry-After header. | ||
| 429 concurrency_limit | Too many jobs are running at once. Wait for one to finish. | ||
| 500 internal_error | Unexpected error. Safe to retry. | ||
| 502 upstream_error / 503 service_unavailable | Generation is temporarily unavailable. Retry with backoff. The job is failed and not counted. |
Job-level errors (on a failed job): generation_failed, content_rejected, generation_timeout.
Limits and billing
- Each generate or extend job counts as one generation. WAV conversion, failed jobs and content-filter rejections are not counted.
- Allowances, concurrency and request-rate limits depend on your plan;
GET /v1/accountalways shows the values that apply to you. - When the allowance is used, requests stop with
402 quota_exceeded. Additional usage is only ever enabled with your written approval, and can be combined with a hard spending cap. - Trial access ends at its end date or allowance, whichever comes first, with no charge and no automatic conversion.
Files and retention
Audio, cover images and WAV files are hosted on cdn.enlivenmusic.ai. Links remain available for at least 30 days; download and store the files you intend to keep. Usage rights for generated tracks are set out in your API agreement.
MCP
The same operations are available to AI agents through a remote MCP (Model Context Protocol) server. It uses the same API key, allowance and limits as the REST API, and every tool call appears in your usage like a REST request.
- Server URL:
https://api.enlivenmusic.ai/mcp - Transport: Streamable HTTP (stateless, JSON responses). Protocol versions 2025-06-18, 2025-03-26 and 2024-11-05.
- Authentication:
Authorization: Bearer enl_live_…header on every request.
Connecting a client
Most MCP clients accept a configuration like this:
{
"mcpServers": {
"enliven-music": {
"type": "http",
"url": "https://api.enlivenmusic.ai/mcp",
"headers": { "Authorization": "Bearer enl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
}
}
}With Claude Code, for example:
claude mcp add --transport http enliven-music https://api.enlivenmusic.ai/mcp --header "Authorization: Bearer $ENLIVEN_API_KEY"Programmatically, with the official TypeScript SDK:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const transport = new StreamableHTTPClientTransport(new URL("https://api.enlivenmusic.ai/mcp"), {
requestInit: { headers: { Authorization: `Bearer ${process.env.ENLIVEN_API_KEY}` } },
});
const client = new Client({ name: "my-pipeline", version: "1.0.0" });
await client.connect(transport);
const job = await client.callTool({
name: "generate_music",
arguments: { title: "Night Shift", style: "tech house, 132 BPM, DJ intro and outro", duration_seconds: 270 },
});
const done = await client.callTool(
{ name: "get_job", arguments: { job_id: job.structuredContent.id, wait_seconds: 180 } },
undefined,
{ timeout: 240_000 }
);Tools
| Tool | Allowance | Description | |
|---|---|---|---|
| generate_music | Counts 1 | Same parameters as POST /v1/music/generate. Returns a job. | |
| extend_track | Counts 1 | Same parameters as POST /v1/music/extend. Returns a job. | |
| convert_to_wav | Free | Same as POST /v1/music/wav. Returns a job with wav_url when done. | |
| get_job | Free | job_id, optional wait_seconds (0–240): waits server-side until the job finishes, so agents need fewer polling calls. | |
| list_jobs | Free | limit, status, starting_after. | |
| get_account | Free | Plan, period, usage and limits. |
Tool results contain the same JSON objects as the REST API, both as text and as structuredContent. Errors (for example quota_exceeded) are returned as tool results with isError: true and the same error codes as REST. Each tool call counts towards the per-minute request limit; protocol messages (initialize, tool listing) do not. When using wait_seconds, set your client's request timeout above the wait time.
© Enliven Music. This reference is provided to partners under their API agreement and may change; material changes are announced by email in advance.