Make videos from code and agents.

Everything the app does — songs, artist photos, Looks, exact quotes, lyric videos and Canvas loops, downloads — through one API. AI apps connect to the MCP server, local agents and scripts use the CLI, your backend calls the REST API, and n8n runs it on autopilot.

Overview

One host serves everything, and every surface runs the same operations with the same validation and the same exact prices as the app.

Endpoints

REST API
https://kinetune.com/api/v1
MCP server
https://kinetune.com/mcp · Streamable HTTP
OAuth discovery
https://kinetune.com/.well-known/oauth-authorization-server

How an order works

  1. Add a song: the master audio and its square cover art. It is analyzed (lyrics, beats, sections) in about a minute.
  2. Quote the video you want. The quote is the exact number of credits; nothing is charged.
  3. Create it with the same request plus the quote_id. Credits are held, and charged only when the video is delivered.
  4. Wait for status: "completed": poll the video or receive a signed callback.
  5. Download the files: one MP4 per format, links valid for 7 days (ask again for fresh ones).

Quickstart

The same order three ways. Pick the one that matches where your code or agent runs.

Terminal

npm install -g @kinetune/cli
kinetune auth login                       # opens the browser to sign in

kinetune artists create --name "Nova Lane"
kinetune songs create --artist-id ARTIST_ID --title "Midnight Drive" \
  --audio ./midnight-drive.wav --cover-art ./cover.jpg
kinetune songs get SONG_ID                # wait for analysis.status "ready"

kinetune create canvas --song-id SONG_ID  # quotes, shows the credits, asks first
kinetune videos wait VIDEO_ID
kinetune videos download VIDEO_ID

Authentication

Two kinds of credentials, both sent as a Bearer token and both scoped to one organization.

API keys — servers, CI and n8n

Create one on the API page in the app (owners and admins). The secret is shown once; we store only an HMAC. Keys act for the organization and are limited by your plan.

Header

Authorization: Bearer kt_live_YOUR_KEY

Sign in with the app — MCP clients and the CLI

OAuth 2.1: authorization code with PKCE (S256), dynamic client registration and client ID metadata documents, rotating refresh tokens. The person signs in, picks the organization and approves the permissions. Access tokens last an hour; clients refresh them on their own.

Connected apps are listed on the API page, where each can be disconnected. Access stops within 30 seconds.

Scopes

videos:read
See videos, quotes and your credit balance
videos:write
Quote, create, cancel, retry and delete videos (spends credits)
music:read
See artists, their photos and songs
music:write
Add, rename and archive artists and songs, upload songs and artist photos
library:read
Browse Looks
library:write
Rename, delete and publish your Looks

OAuth endpoints

Authorization server
https://kinetune.com/.well-known/oauth-authorization-server
MCP resource
https://kinetune.com/mcp · metadata at /.well-known/oauth-protected-resource/mcp
REST resource
https://kinetune.com/api/v1 · metadata at /.well-known/oauth-protected-resource/api/v1
Authorize · token
/api/auth/oauth2/authorize · /api/auth/oauth2/token
Register · revoke
/api/auth/oauth2/register · /api/auth/oauth2/revoke

Send resource (RFC 8707) with the MCP or REST URL so the token's audience matches; unauthenticated calls answer 401 with a WWW-Authenticate header that points to the resource metadata.

MCP server

Remote agents use Kinetune through the Model Context Protocol: one tool per API operation, Streamable HTTP, sign-in with your account.

Server URL

https://kinetune.com/mcp
  1. In ChatGPT, open Settings → Apps & Connectors → Advanced and turn on Developer mode (it depends on your plan and workspace settings).
  2. Choose Create, name it Kinetune, paste the server URL and pick OAuth.
  3. Sign in, choose the organization and approve. Then enable it in a chat from the tools menu.

Tools

The server is stateless (JSON responses, protocol 2025-11-25). Tools that create videos spend credits, so agents are instructed to quote first and ask. wait_for_video waits up to 55 seconds per call.

ToolWhat it does
get_accountWho you are signed in as, the organization and its credit balance
get_optionsVideo types, Look categories, background sources, formats and the current credit prices
list_artistsEvery artist with their song and photo counts
create_artistAdd an artist by name
get_artistOne artist
rename_artistChange an artist’s name
archive_artistdestructiveRemove an artist that has no songs
list_artist_photosThe artist’s photos (identity references, up to 6)
add_artist_photosUpload one or more photos of the artist (JPEG, PNG or WebP)
delete_artist_photodestructiveRemove one photo
list_songsSongs with their cover, duration and analysis status
create_songAdd a song: master audio and square cover art
get_songOne song and its analysis status
rename_songChange a song’s title
archive_songdestructiveRemove a song (its videos stay)
get_song_analysisWord-timed lyrics, tempo, beats, sections and hook
reanalyze_songRetry a failed analysis
list_looksBrowse Official, Community and your own Looks
get_lookOne Look with its design and preview images
rename_lookRename one of your Looks
delete_lookdestructiveRemove one of your Looks (videos made with it stay)
set_look_visibilityMake one of your Looks public (earns 10 credits) or private
quote_lyric_videoThe exact credits for a lyric video, before anything is charged
quote_canvasThe exact credits for a Spotify Canvas, before anything is charged
create_lyric_videoMake the lyric video a quote priced (spends credits)
create_canvasMake the Canvas a quote priced (spends credits)
list_videosVideos, newest first, with status and thumbnails
get_videoStatus, progress, credits and, when completed, the download links
cancel_videodestructiveStop a queued or processing video (credits released)
retry_videoRun a finished, failed or cancelled video again (new charge)
delete_videodestructiveDelete a finished video and its files
wait_for_videoWaits for a video to finish (up to 55 s per call), then returns it like get_video

CLI

kinetune runs every operation from the terminal. It prints JSON whenever its output is piped, so local agents and scripts read it directly.

Install (Node 20+)

npm install -g @kinetune/cli
# or without installing:
npx @kinetune/cli --help

Sign in

kinetune auth login                            # browser sign-in, picks the organization
kinetune auth login --api-key kt_live_YOUR_KEY  # or store an API key
kinetune auth status

Working with it

Commands follow the API: kinetune songs list, kinetune looks get LOOK_ID, kinetune quote lyric-video --song-id SONG_ID. Path ids are arguments, fields are flags, and --input file.json (or - for stdin) takes the whole request. Files go up by path or https URL: --audio ./song.wav.

kinetune create … always quotes first and asks before charging; pass --yes or a ceiling with --max-credits 120 in scripts. kinetune videos wait and kinetune videos download finish the job.

For agents

kinetune schema create canvas   # JSON Schema of a command's input
kinetune openapi                 # the whole API as OpenAPI 3.1
kinetune songs list --json       # JSON even in a terminal

Environment and exit codes

KINETUNE_API_KEY
An organization API key; wins over the stored sign-in (CI, servers).
KINETUNE_URL
Another deployment of the app (defaults to this site).
KINETUNE_CONFIG_DIR
Where credentials live; default ~/.config/kinetune (mode 600).
Exit codes
0 done · 1 API or network error · 2 invalid usage or over --max-credits · 3 not signed in or not allowed

REST API

JSON over HTTPS. Every request carries a Bearer credential; ids are prefixed strings; times are ISO 8601.

Basics

Base URL
https://kinetune.com/api/v1
Auth
Authorization: Bearer … — an API key or an OAuth access token
Uploads
multipart/form-data files, or JSON with public https URLs (audio_url, cover_art_url, photo_urls)
Quotes
Valid for a limited time and once; create with the identical request plus quote_id. Re-sending the same quote returns the videos it already made.
Download links
Signed, valid 7 days; GET /videos/{id} returns fresh ones

Upload files (multipart)

curl -s https://kinetune.com/api/v1/songs -H "Authorization: Bearer $KINETUNE_API_KEY" \
  -F artist_id=ARTIST_ID -F title="Midnight Drive" \
  -F [email protected] -F [email protected]

Callbacks

Pass callback_url (https, public) with the quote and the create call, and the video is POSTed to you when it completes, fails or is cancelled.

Delivery

Body
The video, exactly as GET /videos/{id} returns it
Headers
Kinetune-Event (video.completed, video.failed, video.cancelled) · Kinetune-Delivery (unique id) · Kinetune-Signature
Retries
Any 2xx within 10 seconds counts. Otherwise it retries after 30 s, 2 min, 10 min, 30 min and 2 h.
Signing secret
On the API page in the app (whsec_…); rotate it there.

Verify Kinetune-Signature (Node)

import { createHmac, timingSafeEqual } from "node:crypto";

// header: "t=1760000000,v1=5f2c…"  body: the raw request body
export function verified(header, body, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map(p => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // replay window
  const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest("hex");
  return v1?.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}

Errors and limits

Errors are JSON: {"error": "…"} plus details where useful (field errors, required and available credits).

Status codes

400
Invalid request; details.fieldErrors names the fields.
401
Missing, invalid or expired credentials (OAuth clients refresh and retry).
402
Not enough credits: required and available tell you how many.
403
The credentials lack the scope, or the action is reserved to the app.
404
Not found in this organization.
409
A state conflict: the song is still being analyzed, or the quote expired, was used or no longer matches.

Concurrent renders are limited by your plan; extra videos wait in the queue.

n8n

The community node covers the same operations: songs, artists and photos, Looks, quotes, videos and downloads.

  1. In a self-hosted n8n, open Settings → Community nodes → Install and enter @kinetune/n8n-nodes-kinetune.
  2. Create a Kinetune API credential with an API key from the app; the base URL is https://kinetune.com.
  3. Quote, then create with the quote id. To continue when the video is ready, set its callback URL to an n8n Webhook node.

API reference

Every operation with its REST call, CLI command and MCP tool. Generated from the same definitions the API validates with.

Account

Get the account

GET/api/v1/account

Who you are signed in as, the organization and its credit balance. Returns the organization the credentials act for, how they authenticate and their scopes, and the credit balance (available, monthly, top-ups). Check it before creating videos.

CLI kinetune accountMCP get_accountScopes videos:read or music:read or library:read

No parameters.

List the options

GET/api/v1/options

Video types, Look categories, background sources, formats and the current credit prices. Everything a request can choose from, with the credit table. Use it to pick a category or source and to explain prices.

CLI kinetune optionsMCP get_optionsScopes videos:read or music:read or library:read

No parameters.

Artists and photos

List artists

GET/api/v1/artists

Every artist with their song and photo counts. Songs belong to an artist; create the artist first.

CLI kinetune artists listMCP list_artistsScopes music:read or videos:read

No parameters.

Create an artist

POST/api/v1/artists

Add an artist by name. Names are unique per organization (409 when taken).

CLI kinetune artists createMCP create_artistScopes music:write or videos:write

FieldTypeDescription
name
body · required
stringThe artist name

Get an artist

GET/api/v1/artists/{artist_id}

One artist.

CLI kinetune artists getMCP get_artistScopes music:read or videos:read

FieldTypeDescription
artist_id
path · required
stringThe artist id

Rename an artist

PATCH/api/v1/artists/{artist_id}

Change an artist’s name.

CLI kinetune artists renameMCP rename_artistScopes music:write or videos:write

FieldTypeDescription
artist_id
path · required
stringThe artist id
name
body · required
stringThe new name

Archive an artist

DELETE/api/v1/artists/{artist_id}

Remove an artist that has no songs. Refused (409) while the artist still has songs.

CLI kinetune artists archiveMCP archive_artistScopes music:write or videos:write

FieldTypeDescription
artist_id
path · required
stringThe artist id

List artist photos

GET/api/v1/artists/{artist_id}/photos

The artist’s photos (identity references, up to 6). Photos keep the artist recognizable when a Look or Canvas shows them; the cover art stays the creative source.

CLI kinetune artists photos listMCP list_artist_photosScopes music:read or videos:read

FieldTypeDescription
artist_id
path · required
stringThe artist id

Add artist photos

POST/api/v1/artists/{artist_id}/photos

Upload one or more photos of the artist (JPEG, PNG or WebP). Up to 6 per artist, at least 512 px on the short side, up to 15 MB each. Only with the rights to use them (rights_confirmed). They are identity references only: never shown publicly or used as they are.

CLI kinetune artists photos addMCP add_artist_photosScopes music:write or videos:write

Files: photos (multipart) or photo_urls (JSON).

FieldTypeDescription
artist_id
path · required
stringThe artist id
photo_urls
body
URL[] (1–6)Public https URLs of the photos (or upload files with the CLI)
rights_confirmed
body · required
trueYou have the rights to use these photos of the artist

Delete an artist photo

DELETE/api/v1/artists/{artist_id}/photos/{photo_id}

Remove one photo.

CLI kinetune artists photos deleteMCP delete_artist_photoScopes music:write or videos:write

FieldTypeDescription
artist_id
path · required
stringThe artist id
photo_id
path · required
stringThe photo id

Songs

List songs

GET/api/v1/songs

Songs with their cover, duration and analysis status. A song must be analyzed (analysis.status "ready") before videos can be made.

CLI kinetune songs listMCP list_songsScopes music:read or videos:read

FieldTypeDescription
artist_id
query
stringOnly this artist’s songs

Upload a song

POST/api/v1/songs

Add a song: master audio and square cover art. Audio: MP3, WAV or M4A up to 250 MB. Cover: a square JPEG or PNG, 1000–6000 px (3000×3000 recommended), up to 20 MB. The song is analyzed next (lyrics, beats, sections): poll get_song until analysis.status is "ready", usually about a minute.

CLI kinetune songs createMCP create_songScopes music:write or videos:write

Files: audio (multipart) or audio_url (JSON), cover_art (multipart) or cover_art_url (JSON).

FieldTypeDescription
artist_id
body · required
stringThe artist id
title
body · required
stringThe song title
language
body
stringLyrics language (ISO 639-1, e.g. "en"); detected when omitted
audio_url
body
URLA public https URL of the master audio
cover_art_url
body
URLA public https URL of the square cover art

Get a song

GET/api/v1/songs/{song_id}

One song and its analysis status.

CLI kinetune songs getMCP get_songScopes music:read or videos:read

FieldTypeDescription
song_id
path · required
stringThe song id

Rename a song

PATCH/api/v1/songs/{song_id}

Change a song’s title.

CLI kinetune songs renameMCP rename_songScopes music:write or videos:write

FieldTypeDescription
song_id
path · required
stringThe song id
title
body · required
stringThe new title

Archive a song

DELETE/api/v1/songs/{song_id}

Remove a song (its videos stay).

CLI kinetune songs archiveMCP archive_songScopes music:write or videos:write

FieldTypeDescription
song_id
path · required
stringThe song id

Get a song’s analysis

GET/api/v1/songs/{song_id}/analysis

Word-timed lyrics, tempo, beats, sections and hook. Use the section times to choose a trim for a lyric video.

CLI kinetune songs analysisMCP get_song_analysisScopes music:read or videos:read

FieldTypeDescription
song_id
path · required
stringThe song id

Analyze a song again

POST/api/v1/songs/{song_id}/analysis

Retry a failed analysis.

CLI kinetune songs reanalyzeMCP reanalyze_songScopes music:write or videos:write

FieldTypeDescription
song_id
path · required
stringThe song id

Looks

List Looks

GET/api/v1/looks

Browse Official, Community and your own Looks. A Look is a complete, reusable lyric-video design. Pass its id as {"mode":"existing","id":…} to reuse it exactly.

CLI kinetune looks listMCP list_looksScopes library:read or videos:read

FieldTypeDescription
scope
query
"all" | "official" | "community" | "mine"Which library; default all
category
query
stringA category id from get_options
q
query
stringSearch words
sort
query
"newest" | "popular"—
limit
query
integer 1–100—
offset
query
integer 0–9007199254740991—

Get a Look

GET/api/v1/looks/{look_id}

One Look with its design and preview images.

CLI kinetune looks getMCP get_lookScopes library:read or videos:read

FieldTypeDescription
look_id
path · required
stringThe Look id

Rename a Look

PATCH/api/v1/looks/{look_id}

Rename one of your Looks.

CLI kinetune looks renameMCP rename_lookScopes library:write or videos:write

FieldTypeDescription
look_id
path · required
stringThe Look id
name
body · required
stringThe new name

Delete a Look

DELETE/api/v1/looks/{look_id}

Remove one of your Looks (videos made with it stay).

CLI kinetune looks deleteMCP delete_lookScopes library:write or videos:write

FieldTypeDescription
look_id
path · required
stringThe Look id

Publish or unpublish a Look

POST/api/v1/looks/{look_id}/visibility

Make one of your Looks public (earns 10 credits) or private. Public Looks join the Community library under your @username. A Look that shows your artist stays private (409).

CLI kinetune looks visibilityMCP set_look_visibilityScopes library:write or videos:write

FieldTypeDescription
look_id
path · required
stringThe Look id
visibility
body · required
"public" | "private"—

Quotes and videos

Quote a lyric video

POST/api/v1/quotesbody {"type":"lyric-video"}

The exact credits for a lyric video, before anything is charged. Returns quote_id, credits (total), credits_per_video and a breakdown. Always quote first and tell the user the credits before creating; credits are reserved at creation and charged only when the video is delivered. A quote is valid for 30 minutes.

CLI kinetune quote lyric-videoMCP quote_lyric_videoScopes videos:write or videos:read

FieldTypeDescription
song_id
body · required
stringThe song id
variations
body
integer 1–41–4 different New Looks, one video each (an Existing Look renders one) Default 1.
callback_url
body
stringAn https URL to POST the finished video to (signed; see Callbacks). Quote and create with the same value
metadata
body
objectYour own JSON (an order id, for example): kept with the video and returned in its request. Quote and create with the same value
look
body · required
object{"mode":"existing","id":"look_…"} to reuse a saved Look, or {"mode":"new","category":"…","background":{"source":"ai-image","quality":"high"},"visibility":"private","direction":"…","feature_artist":"auto"} to have one designed
aspect_ratios
body
"9:16" | "16:9" | "1:1"[] (1–3)Formats to render, each its own file: "9:16" (vertical), "16:9" (wide), "1:1" (square) Default ["9:16"].
display
body
objectWhich elements show (title, artist, cover, lyrics, badges, headline) and their sizes Default {"title":true,"artist":true,"cover":true,"lyrics":true,"badges":true}.
trim
body
objectRender only this section of the song (at least 8 seconds)

Quote a Canvas

POST/api/v1/quotesbody {"type":"canvas"}

The exact credits for a Spotify Canvas, before anything is charged. Returns quote_id, credits and a breakdown. Always quote first and tell the user the credits before creating; credits are reserved at creation and charged only when the video is delivered.

CLI kinetune quote canvasMCP quote_canvasScopes videos:write or videos:read

FieldTypeDescription
song_id
body · required
stringThe song id
variations
body
integer 1–41–4 different Canvases Default 1.
callback_url
body
stringAn https URL to POST the finished video to (signed; see Callbacks). Quote and create with the same value
metadata
body
objectYour own JSON (an order id, for example): kept with the video and returned in its request. Quote and create with the same value
source
body
"ai-video" | "ai-image" | "stock-photo" | "stock-video"ai-video (default), ai-image, stock-photo or stock-video Default "ai-video".
quality
body
"standard" | "high"AI video model tier: standard or high Default "standard".
resolution
body
"720p" | "1080p"1080p (1080×1920) or 720p Default "1080p".
style
body
"cinematic" | "dreamy" | "abstract" | "surreal" | "retro" | "minimal" | "dark" | "vibrant" | "nature" | "urban"Optional visual style
direction
body
stringOptional mood, motifs or references; the concept still comes from the cover
feature_artist
body
"auto" | "always" | "never"Whether the Canvas shows the artist (AI sources; needs artist photos for "always") Default "auto".
seconds
body
integer 5–8Canvas length in whole seconds, 5–8 (default 8). Spotify loops it, so no part of the song is chosen Default 8.

Create a lyric video

POST/api/v1/videosbody {"type":"lyric-video"}

Make the lyric video a quote priced (spends credits). Send the same request as the quote plus its quote_id. Returns the video ids at once; poll get_video until status is completed (or failed). Each video has one file per format.

CLI kinetune create lyric-videoMCP create_lyric_videoScopes videos:write

FieldTypeDescription
song_id
body · required
stringThe song id
variations
body
integer 1–41–4 different New Looks, one video each (an Existing Look renders one) Default 1.
callback_url
body
stringAn https URL to POST the finished video to (signed; see Callbacks). Quote and create with the same value
metadata
body
objectYour own JSON (an order id, for example): kept with the video and returned in its request. Quote and create with the same value
look
body · required
object{"mode":"existing","id":"look_…"} to reuse a saved Look, or {"mode":"new","category":"…","background":{"source":"ai-image","quality":"high"},"visibility":"private","direction":"…","feature_artist":"auto"} to have one designed
aspect_ratios
body
"9:16" | "16:9" | "1:1"[] (1–3)Formats to render, each its own file: "9:16" (vertical), "16:9" (wide), "1:1" (square) Default ["9:16"].
display
body
objectWhich elements show (title, artist, cover, lyrics, badges, headline) and their sizes Default {"title":true,"artist":true,"cover":true,"lyrics":true,"badges":true}.
trim
body
objectRender only this section of the song (at least 8 seconds)
quote_id
body · required
uuidThe quote_id from the matching quote: the request must be identical

Create a Canvas

POST/api/v1/videosbody {"type":"canvas"}

Make the Canvas a quote priced (spends credits). Send the same request as the quote plus its quote_id. Returns the video ids at once; poll get_video until status is completed. Upload the file in Spotify for Artists.

CLI kinetune create canvasMCP create_canvasScopes videos:write

FieldTypeDescription
song_id
body · required
stringThe song id
variations
body
integer 1–41–4 different Canvases Default 1.
callback_url
body
stringAn https URL to POST the finished video to (signed; see Callbacks). Quote and create with the same value
metadata
body
objectYour own JSON (an order id, for example): kept with the video and returned in its request. Quote and create with the same value
source
body
"ai-video" | "ai-image" | "stock-photo" | "stock-video"ai-video (default), ai-image, stock-photo or stock-video Default "ai-video".
quality
body
"standard" | "high"AI video model tier: standard or high Default "standard".
resolution
body
"720p" | "1080p"1080p (1080×1920) or 720p Default "1080p".
style
body
"cinematic" | "dreamy" | "abstract" | "surreal" | "retro" | "minimal" | "dark" | "vibrant" | "nature" | "urban"Optional visual style
direction
body
stringOptional mood, motifs or references; the concept still comes from the cover
feature_artist
body
"auto" | "always" | "never"Whether the Canvas shows the artist (AI sources; needs artist photos for "always") Default "auto".
seconds
body
integer 5–8Canvas length in whole seconds, 5–8 (default 8). Spotify loops it, so no part of the song is chosen Default 8.
quote_id
body · required
uuidThe quote_id from the matching quote: the request must be identical

List videos

GET/api/v1/videos

Videos, newest first, with status and thumbnails.

CLI kinetune videos listMCP list_videosScopes videos:read

FieldTypeDescription
song_id
query
stringOnly this song’s videos
type
query
"lyric-video" | "canvas"—
limit
query
integer 1–100—

Get a video

GET/api/v1/videos/{video_id}

Status, progress, credits and, when completed, the download links. status is queued, processing, completed, failed or cancelled. Completed videos have outputs with download_url (full quality), web_url (720p) and poster_url, signed for 7 days.

CLI kinetune videos getMCP get_videoScopes videos:read

FieldTypeDescription
video_id
path · required
stringThe video id

Cancel a video

POST/api/v1/videos/{video_id}/cancel

Stop a queued or processing video (credits released).

CLI kinetune videos cancelMCP cancel_videoScopes videos:write

FieldTypeDescription
video_id
path · required
stringThe video id

Make a video again

POST/api/v1/videos/{video_id}/retry

Run a finished, failed or cancelled video again (new charge). Reuses its Look, background media and loops, so nothing already made is paid for again.

CLI kinetune videos retryMCP retry_videoScopes videos:write

FieldTypeDescription
video_id
path · required
stringThe video id

Delete a video

DELETE/api/v1/videos/{video_id}

Delete a finished video and its files.

CLI kinetune videos deleteMCP delete_videoScopes videos:write

FieldTypeDescription
video_id
path · required
stringThe video id