Skip to content

Heavenly Docs

Your guide to editing videos, connecting AI apps, and working with Heavenly.

Introduction

What Heavenly is

Heavenly is LensLab’s video workspace, with a timeline and a Gemini assistant beside it. Describe an edit, review the proposed operations, then apply or undo them. Clip discovery and rendered MP4 downloads require a connected private media service.

The path that ships today:

  1. Choose your footage. Clip discovery uploads it for processing.
  2. Tell the assistant what to change and inspect its proposal.
  3. Apply the complete edit. Undo and redo walk revisions.
  4. Download a supported MP4 cut, or export an EDL, Final Cut XML, or shot sheet.

Four pieces

PieceWhat it is
ApplicationThe studio at /heavenly/dashboard/edit. Timeline, captions, looks, export.
AgentGemini proposes validated edit operations. You preview and apply them.
APIThe Clipping API turns a video link or file into short clips. The Motion API makes a motion design from a prompt.
MCPhttps://lenslab.dev/api/heavenly-mcp lists Heavenly tools with no key. Project and publish calls need an hv_pat_ token. Footage stays on the machine.

Timeline look settings: Clean, Handheld, Fast, Loud, Talk. Caption presets: Pop, Karaoke, Boxed, Bounce, Minimal, Outline. A setting in the editor is not a promise of rendered support: export checks the sequence and explains unsupported operations.

Footage

Local timeline editing preserves the original file. Clip discovery uploads the source to the private media service for transcription, candidate selection, previews, and export. Gemini chat sends your prompt and relevant editing context to the server. Jobs and media are scoped to your signed-in account; server credentials are never sent to the browser.

Quickstart

Your first edit

  1. Sign in with the same LensLab account.
  2. Open the workspace and choose a file you already have.
  3. For clip discovery, upload it and follow the processing job.
  4. Ask for an edit, review the proposal, and apply it. Undo if it is wrong.
  5. Preview the sound and picture, then download a supported MP4 or an edit interchange file.
Terminal
$ open https://lenslab.dev/heavenly/dashboard/edit
TypeScript
// Local deterministic helper, separate from Gemini chat. Not a published SDK.
const plan = planEdits("cut the pause", state);
// plan.steps are edit operations. plan.questions is what it will not guess.

If the media service or a provider is unavailable, Heavenly reports that state. Processing and downloads are not simulated. Your existing local timeline remains editable.

Public API

The Motion API is live: a prompt in, a motion design out, over HTTP or MCP.

The Clipping API is live too: POST /api/heavenly/v1/jobs with a video link, and get scored, captioned clips back.

Authentication

App session

The studio uses the LensLab sign-in session. Media jobs validate that session and enforce ownership. Gemini and media-service credentials stay on the server; you do not paste keys into the chat. The local deterministic planning helper needs no provider key.

Access tokens

Get an API key →

Tools that aren't a browser, such as Claude Code, sign in with an access token you make in Settings → Workspace → API tokens. A token does only what you allow (read, edit, publish), expires, and stops working the moment you revoke it. It can't change billing, manage teams, connect accounts, or make more tokens. It works with the Heavenly MCP, the Clipping API and the Motion API only.

The Motion API and the Clipping API accept the same token as a Bearer header. There is no general secret key.

The public LensLab MCP URL does not use a Heavenly token. A burst of bad requests is limited. See Rate Limits.

Rate limits and plans

Plans

Prices are USD. Yearly is the monthly rate when twelve months are prepaid.

PlanMonthlyYearlyPublished allowance
Hobby201650 / month
Creator5040300 / month
Arena10080800 / month
Arena 2×2001601,600 / month

The editor does not meter that allowance. Unused credits do not roll over (rollover is off). They are not cash.

Public MCP

These numbers are the live cap on https://lenslab.dev/api/heavenly-mcp. They are not a clipping quota.

Requests / minute60
Failed auth / minute10
Body256 KB
Batch10

Over the cap the server answers HTTP 429 and sends Retry-After in seconds.

Errors

MCP errors

The public MCP speaks JSON-RPC. A limit looks like this. The Motion API's REST errors are listed on its own page.

JSON
{
  "jsonrpc": "2.0",
  "id": null,
  "error": { "code": -32000, "message": "rate_limit" }
}

HTTP

Public MCP rate limits return 429 with Retry-After. The app’s private media gateway returns 503 when its service is not configured or reachable. Jobs expose failure and cancellation in the workspace; an unsuccessful job never becomes a finished clip.

The Motion API has its own error bodies and status codes.

The Clipping API lists its error codes on its own page.

Motion API

Overview

A job turns plain English into a finished motion design: titles, callouts and lower thirds timed across the length you choose, in a look you choose. You create a job, poll it until it completes, then open the design in the Motion designer, download its scene document, or refine it with a followup. Jobs run in the background, so a create returns at once.

Terminal
$ curl -X POST https://lenslab.dev/api/heavenly-motion/sessions \
    -H "Authorization: Bearer hv_pat_YOUR_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"prompt":"A 20-second launch teaser for Heavenly","aspect_ratio":"9:16"}'

The same API is also reachable at /api/heavenly-workspace/motion/…, and from Claude Code or any MCP client (see MCP tools below).

Authentication

Send an access token as Authorization: Bearer hv_pat_…. Make one in Settings → Workspace → API tokens. Creating jobs, followups and uploads needs the edit permission; polling and listing need read. The account needs a Heavenly plan, and a job is visible only to the account that made it (anyone else gets a 404). A token may make 25 credit-spending calls a day. Responses are never cached.

Create a job

POST https://lenslab.dev/api/heavenly-motion/sessions returns 202. Unknown fields are refused.

FieldTypeMeaning
promptstring, required1–12,000 characters.
aspect_ratio16:9 · 9:16 · 1:1 · 4:5Default 16:9. 21:9 isn’t offered: Heavenly’s frame has these four shapes.
duration<10s · 10-30s · 30s-1min · 1-5minDefault 10-30s. Longer designs are planned in passes of a minute at most.
design_system_ida lookheavenly (Heavenly), editorial (Editorial), mono (Mono), neon (Neon), sunset (Sunset), ocean (Ocean), forest (Forest), paper (Paper), broadcast (Broadcast), playful (Playful). Default heavenly.
design_mdstring or objectYour own look as a DESIGN.md, up to 60,000 characters. Either the text, or { filename, content, jsonFilename?, jsonContent? }. It wins over design_system_id.
style_reference_urlYouTube URLA public YouTube video whose pacing the design should match.
attachmentsarray, up to 100References: { url, name?, type?, content_type? }. Public https links only.
202 Accepted
{
  "job_id": "5c1f0e7a-9b3a-4b6e-8f0e-2d9c6a1b7e44",
  "status": "queued",
  "status_url": "https://lenslab.dev/api/heavenly-motion/sessions/5c1f0e7a-9b3a-4b6e-8f0e-2d9c6a1b7e44",
  "chat_url": "https://lenslab.dev/heavenly/dashboard/motion/jobs?job=5c1f0e7a-…",
  "credits": 1
}

Send an Idempotency-Key header (8–100 characters) when you might retry: the same key from the same account returns the first job with 200 and never spends twice.

Poll a job

GET https://lenslab.dev/api/heavenly-motion/sessions/{job_id}. Wait poll_after_seconds (3) between calls; when it is null nothing more will change by itself. GET https://lenslab.dev/api/heavenly-motion/sessions lists your recent jobs.

StatusMeaning
queuedAccepted, waiting for a worker. Credits are reserved.
createdA worker has it and is getting ready.
runningReading references, designing, building the project.
awaiting_user_inputThe designer couldn’t make anything of the prompt and asks a question (question). Answer with a followup.
completedDone. output and sources are set. Credits are charged.
failederror and error_code say why. Credits are returned.
200 OK, completed
{
  "job_id": "5c1f0e7a-…",
  "status": "completed",
  "stage": "Done",
  "poll_after_seconds": null,
  "output": {
    "status": "completed",
    "project_id": "0b6c…",
    "project_url": "/heavenly/dashboard/motion?project=0b6c…",
    "download_url": "https://…signed…",
    "expires_at": "2026-10-04T02:00:00.000Z",
    "format": "heavenly-motion-scene+json",
    "summary": { "pieces": 5, "layers": 14, "duration_seconds": 20,
                 "frame": { "width": 1080, "height": 1920 },
                 "rounds": 2, "planner": "model", "notes": [] }
  },
  "sources": [ … ]
}

download_url (set on a single poll, left null in the list) is a signed link to the design’s scene document, valid for an hour and signed again on every poll. Statuses: queued, created, running, awaiting_user_input, completed, failed. A job that hasn’t finished 20 minutes after it started is failed rather than left waiting, and an interrupted worker’s job is picked up again within a minute.

Followups

POST https://lenslab.dev/api/heavenly-motion/sessions/{job_id}/followups takes the same body as a create (only prompt is required) and returns the same 202. It is allowed when the job is completed or awaiting_user_input; any other status is a 409. A followup keeps the earlier job’s shape, length, look and references unless you set them again, reads the earlier design back, and makes a new project; the earlier result is untouched. Answering a waiting job closes it and returns its credits.

Uploads and attachments

Attachments are links the server can reach without cookies or headers. For a local file, upload it first:

  1. POST https://lenslab.dev/api/heavenly-motion/uploads with { filename, content_type } returns upload_url, complete_url and the size limit.
  2. Send the file to upload_url: an HTTP PUT, multipart form data with cacheControl=3600 and the file as the unnamed part, header x-upsert: false. The link is good for an hour.
  3. POST to complete_url. It checks the file finished and fits, and returns attachment_url, valid for 7 days. Pass that in attachments.
KindLargestTypes
video5,120 MBvideo/mp4, video/quicktime, video/x-msvideo, video/webm, video/x-matroska, video/x-m4v
audio500 MBaudio/mpeg, audio/mp3, audio/wav, audio/mp4, audio/x-m4a, audio/flac, audio/ogg, audio/aac, audio/opus, audio/webm
image50 MBimage/png, image/jpeg, image/jpg, image/gif, image/webp, image/svg+xml

You can start 100 uploads every 5 hours (429 after that). Uploaded files are deleted after 14 days. A link that points at a private address, redirects, or isn’t https is not fetched.

What the designer does with an attachment: up to 4 JPEG, PNG or WebP images are shown to it for colour, subject and mood. Anything else is kept on record as a reference and not analysed. Nothing you attach is placed in the design itself; the sources audit says so for each one.

Style reference

Give a public YouTube link and a model watches it for timing only: the average time between changes, the tempo, how text enters and leaves. The designer is told to match that rhythm. No footage, audio or text is copied. If the video can’t be read the job carries on without it, and the audit says whether it was read.

Looks and DESIGN.md

Ten built-in looks, each a set of colours, a typeface and a feel for how things move: heavenly, editorial, mono, neon, sunset, ocean, forest, paper, broadcast, playful. Or pass your own DESIGN.md. Heavenly reads the lines it can use: a hex colour on a line that names a role (primary or background, accent, text, on-primary), a typeface (serif, Inter, Montserrat), and a motion word (snappy, smooth, playful, cinematic). A JSON token file is read the same way. The rest of the file goes to the designer as written direction, so a file that only names a background still works: the text colour is chosen to be readable on it.

Sources audit

Every completed job lists where its material came from, built from what actually happened. source_kind is generated for the motion Heavenly made (with a usages entry per piece: start, end, scene, slot and element), upload or web for your attachments, and web for a style reference. rights_status is generated, user_provided or restricted (a style reference, used for timing only). Heavenly’s designer doesn’t place stock footage, articles or web media in a design, so none is claimed. Links to your own storage are never repeated back.

Credits

Credits are reserved when a job is accepted, charged when it completes, and returned if it fails or is replaced. A job costs 1 for <10s, 1 for 10-30s, 1 for 30s-1min, 3 for 1-5min. With too few you get a 402 and nothing is queued. Credits are bought in the app, not through the API.

Errors

StatusBodyWhen
400error: Invalid request, details[]A field is missing, too long or not one of the values.
401errorNo token, or the token is expired or revoked.
402error: insufficient_credits, code, minimum_required_credits, current_balanceNo plan, or not enough credits.
403code: token_scope / token_not_allowedThe token lacks edit or read.
404error: Job not foundNo such job, or it belongs to another account.
409error: followup_requires_completed_job, statusA followup on a job that is still running or failed; or an upload not finished.
413error: file_too_large / request_too_largeA file over its limit, or a body that is too large.
429error: rate_limited / code: token_daily_capToo many uploads, or the token’s daily limit.
503error: storage_unavailableUploads aren’t available on this server.

A job can fail with designer_not_configured, designer_unavailable, designer_busy (retried automatically before it fails), project_limit, timed_out or internal_error. A failed job never delivers a half-made design.

MCP tools

With the Heavenly MCP an agent can do all of this: create_motion_design, create_motion_followup, get_motion_design, list_motion_designs, upload_motion_asset and finish_motion_upload. They are the same calls under the same token, plan and daily limit. Buying credits, changing plans and managing tokens are not available to a token, by design.

What a job is, and isn’t

A job’s result is a Heavenly motion design: a validated scene (text, shapes, particles and grain with their timing), saved as a project. It plays and exports in the Motion designer; the server doesn’t render video pixels. It isn’t footage: there is none to edit, so the designer composes on the look’s own background. Open it in the Motion designer to add your footage and export.

Clipping

Overview

Give Heavenly a long video and get short clips back: it finds the moments worth posting, scores each one, reframes it to 9:16, 1:1 or 16:9 and burns in captions. The same Clips you use in the app, from code, an agent or the terminal. A job starts at once and runs in the background; poll it, read its clips, export the ones you want at 1080p, and put them on the calendar.

Terminal
$ curl -X POST https://lenslab.dev/api/heavenly/v1/jobs \
    -H "Authorization: Bearer hv_pat_YOUR_TOKEN" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: launch-video-1" \
    -d '{"video_url":"https://www.youtube.com/watch?v=VIDEO_ID","aspect_ratio":"9:16","caption_style":"hormozi"}'

Authentication and limits

Send an access token as Authorization: Bearer hv_pat_…, made in Settings → Workspace → API tokens. Reading needs read, clipping and exporting need edit, scheduling needs publish. The account needs a Heavenly plan. A token makes 60 calls a minute (upload chunks have their own 600), and an account starts 30 clip jobs and 120 exports an hour. Clipping spends Clips minutes for the length of the video, exactly as in the app: held when the job starts, settled to the real length, returned if it fails. Exports are free.

MethodPathPermissionWhat it does
POST/jobseditClip a video from a public link
GET/jobsreadYour 30 most recent jobs
GET/jobs/{job_id}readA job's status and progress
GET/jobs/{job_id}/clipsreadIts clips, best first, with scores and reasons
GET/jobs/{job_id}/downloadsreadFresh download links (?clip_id= for one)
PATCH/jobs/{job_id}/clips/{clip_id}editChange one clip's crop, captions or headline
GET/backgroundsreadThe loops a 9:16 clip can be stacked over
PATCH/jobs/{job_id}editChange the frame or captions of every clip
POST/jobs/{job_id}/reanalyzeeditLook again in the same video
POST/jobs/{job_id}/canceleditStop a job
POST/jobs/{job_id}/retryeditStart a failed or cancelled job again
POST/jobs/{job_id}/clips/{clip_id}/exporteditExport a clip at 1080p
GET/jobs/{job_id}/clips/{clip_id}/exportreadThat export's status and link
POST/jobs/{job_id}/clips/{clip_id}/schedulepublishPut a clip on the calendar
POST/uploadseditStart uploading a local file
POST/uploads/{upload_id}/chunks?offset=NeditSend one chunk
GET/uploads/{upload_id}editWhat has arrived
POST/uploads/{upload_id}/completeeditStart the job once every chunk is in
POST/captionseditCaption a whole video (when available)
GET/accountsreadConnected social accounts
GET/postsreadScheduled and published posts
POST/posts/{post_id}/readypublishRelease a held post
POST/posts/{post_id}/cancelpublishCancel a post
GET/usagereadClips minutes and AI credits left, and the caps
POST/quotereadWhat a clip or export will spend, without starting it

Every path is under https://lenslab.dev/api/heavenly/v1.

Clip a link

POST /jobs returns 202 and the job. Unknown fields are refused. Send an Idempotency-Key (1–255 characters) when you might retry: the same key returns the first job and holds minutes once.

FieldTypeMeaning
aspect_ratio9:16 · 1:1 · 16:9Frame of the clips: 9:16 (default), 1:1 or 16:9.
caption_styleviral-bold · clean-creator · high-energy · editorial · none · highlighted · scale · minimalist · box · staticbox · glow · hormozi · vlog · classic · pop-box · beast · karaoke · bold-pop · one-word · comic · neon · read-along · reveal · highlighter · clean · podcast · label · impact · cinema · elegant · marker · bubble · typewriterBurned-in caption look; none turns captions off. Default: Heavenly's highlight look.
caption_colorstringAccent colour of the spoken word, #RRGGBB.
clip_countintegerHow many clips to return. Default: as many as the video holds.
min_secondsnumberShortest clip. Default 30.
max_secondsnumberLongest clip. Default 60.
modeauto · high-retention · story · insight · funny · controversial · emotional · quotes · product · customWhat to look for: auto (default), funny, story, insight, quotes and more.
platformTikTok · Shorts · Reels · LinkedIn · XWhere the clips will be posted. Default Shorts.
instructionsstringPlain words about what to find, e.g. "every moment about pricing".
video_urlstring, requiredPublic https link to the video (YouTube, TikTok, Instagram, X, Twitch, Kick, Vimeo, a file URL…).
start_secondsnumberOnly clip from here (seconds).
end_secondsnumberOnly clip up to here (seconds).
titlestringName for the project.
202 Accepted
{
  "id": "6c7a8bcb-c595-4c71-934c-08be77502832",
  "kind": "clips",
  "status": "queued",
  "stage": "queued",
  "progress": 0,
  "message": "Waiting in queue",
  "clips_ready": 0,
  "poll_after_seconds": 15,
  "app_url": "https://lenslab.dev/heavenly/dashboard/clips?project=6c7a8bcb-…"
}

Follow it and read the clips

GET /jobs/{job_id} until status is completed (or failed, with error). Wait poll_after_seconds between calls. Then GET /jobs/{job_id}/clips:

200 OK
{
  "clips": [{
    "id": "c-12-53",
    "rank": 1,
    "title": "The best moment",
    "start_seconds": 12, "end_seconds": 53, "duration_seconds": 41,
    "score": 93,
    "scores": { "hook": 95, "payoff": 90 },
    "reasons": ["Opens on a question", "Laughter at 0:31"],
    "hook": "Nobody tells you this about launching",
    "preview": { "url": "https://lenslab.dev/api/heavenly-clipping/jobs/…/media/c-12-53?ticket=…&download=1",
                 "expires_at": "2026-10-15T10:17:51.000Z" }
  }]
}

Download and export

Every link is signed and works for about an hour without a token. Store the clip, not the link: GET /jobs/{job_id}/downloads always signs fresh ones. A preview is ready to post; POST /jobs/{job_id}/clips/{clip_id}/export renders the 1080p MP4 with its captions and look. Calling it again never renders twice: poll it (or its GET) until status is completed, then download.url is the file and file its SHA-256 and size.

Adjust what Heavenly found

Heavenly crops, captions and trims each clip itself. Everything it chose can be changed afterwards without paying for the video again. Only the fields you send change, the clip's preview redraws (poll the job until completed), and the next export is a new one with the change in it. Clips list their current look.

One clip

PATCH /jobs/{job_id}/clips/{clip_id} takes a headline, caption look and colour, who the 9:16 crop follows, a hand-placed crop, or a trim: start_seconds and end_seconds in seconds of the source video, inside the clip Heavenly found (each clip lists that as trim_room) and at least 3 seconds. reset_trim puts the found clip back. It costs nothing.

FieldTypeMeaning
headlinestringThe line over the clip's first seconds. An empty string removes it.
caption_styleviral-bold · clean-creator · high-energy · editorial · none · highlighted · scale · minimalist · box · staticbox · glow · hormozi · vlog · classic · pop-box · beast · karaoke · bold-pop · one-word · comic · neon · read-along · reveal · highlighter · clean · podcast · label · impact · cinema · elegant · marker · bubble · typewriterThis clip's caption look; none turns its captions off.
caption_colorstringAccent colour of the spoken word on this clip, #RRGGBB.
framingsingle · active · group · split · gamingWho the 9:16 crop follows: single (one person), active (whoever speaks), group (everyone in frame), split (two people stacked, one above the other, as in a podcast; needs two people on screen) or gaming (stream camera above the game).
crop_xobjectPlace the crop by hand: 0 is the left edge of the picture, 1 the right, 0.5 the centre. null goes back to the automatic crop. Holds one crop for the whole clip.
start_secondsnumberTrim: start the clip here, in seconds of the source video (as list_clips gives them). Inside the clip Heavenly found: trim_room says how far.
end_secondsnumberTrim: end the clip here, in seconds of the source video.
reset_trimbooleanPut the clip back to the start and end Heavenly found.
background_idobjectStack this clip over a looping background (an id from list_backgrounds), in the bottom half of a 9:16 frame. null turns the background off for this clip, even when the job has one.

A background under the clip

A 9:16 clip can sit in the top half of its frame with a looping video in the bottom half, muted, the split-screen look many feeds use. GET /backgrounds lists what can go there: the loops Heavenly makes, and footage this account added in the Heavenly app, where its rights are stated. Send one as background_id; null turns it off. On one clip it overrides the job's.

Every clip

PATCH /jobs/{job_id} changes the frame or the caption look of every clip at once. A clip's own look still wins over it. It counts as one spending call for the token's day.

FieldTypeMeaning
aspect_ratio9:16 · 1:1 · 16:9Frame of the clips: 9:16 (default), 1:1 or 16:9.
caption_styleviral-bold · clean-creator · high-energy · editorial · none · highlighted · scale · minimalist · box · staticbox · glow · hormozi · vlog · classic · pop-box · beast · karaoke · bold-pop · one-word · comic · neon · read-along · reveal · highlighter · clean · podcast · label · impact · cinema · elegant · marker · bubble · typewriterBurned-in caption look; none turns captions off. Default: Heavenly's highlight look.
caption_colorstringAccent colour of the spoken word, #RRGGBB.
background_idobjectStack every clip over a looping background (an id from list_backgrounds), in the bottom half of a 9:16 frame. null takes it off every clip that has not chosen its own.

Look again

POST /jobs/{job_id}/reanalyze looks for something different in the video already analysed, for example {"instructions":"every moment about pricing"}. It reuses the transcript, so no Clips minutes are held, and it replaces the job's clips, including any change made to them. It counts as one spending call.

FieldTypeMeaning
modeauto · high-retention · story · insight · funny · controversial · emotional · quotes · product · customWhat to look for: auto (default), funny, story, insight, quotes and more.
platformTikTok · Shorts · Reels · LinkedIn · XWhere the clips will be posted. Default Shorts.
clip_countintegerHow many clips to return. Default: as many as the video holds.
min_secondsnumberShortest clip. Default 30.
max_secondsnumberLongest clip. Default 60.
instructionsstringPlain words about what to find, e.g. "every moment about pricing".

POST /jobs/{job_id}/cancel stops a queued or running job and POST /jobs/{job_id}/retry starts a failed or cancelled one again. Until Heavenly's worker is updated these answer adjust_unavailable.

Upload a file

Start the upload with the file's name and exact size, send each offset in missing_offsets as raw bytes (chunk_bytes each, in any order, at most 100 MB per request), then complete it. Repeating a start with the same Idempotency-Key resumes the same upload. The CLI does all of this for you.

FieldTypeMeaning
aspect_ratio9:16 · 1:1 · 16:9Frame of the clips: 9:16 (default), 1:1 or 16:9.
caption_styleviral-bold · clean-creator · high-energy · editorial · none · highlighted · scale · minimalist · box · staticbox · glow · hormozi · vlog · classic · pop-box · beast · karaoke · bold-pop · one-word · comic · neon · read-along · reveal · highlighter · clean · podcast · label · impact · cinema · elegant · marker · bubble · typewriterBurned-in caption look; none turns captions off. Default: Heavenly's highlight look.
caption_colorstringAccent colour of the spoken word, #RRGGBB.
clip_countintegerHow many clips to return. Default: as many as the video holds.
min_secondsnumberShortest clip. Default 30.
max_secondsnumberLongest clip. Default 60.
modeauto · high-retention · story · insight · funny · controversial · emotional · quotes · product · customWhat to look for: auto (default), funny, story, insight, quotes and more.
platformTikTok · Shorts · Reels · LinkedIn · XWhere the clips will be posted. Default Shorts.
instructionsstringPlain words about what to find, e.g. "every moment about pricing".
filenamestring, requiredThe file's name, with its extension.
size_bytesinteger, requiredThe file's exact size in bytes.
captionsobjectCaption the whole video instead of finding clips (needs Caption a video).
Terminal
$ curl -X POST https://lenslab.dev/api/heavenly/v1/uploads -H "Authorization: Bearer hv_pat_YOUR_TOKEN" -H "Content-Type: application/json" \
    -d '{"filename":"podcast.mp4","size_bytes":734003200}'
$ curl -X POST "https://lenslab.dev/api/heavenly/v1/uploads/UPLOAD_ID/chunks?offset=0" -H "Authorization: Bearer hv_pat_YOUR_TOKEN" \
    -H "Content-Type: application/octet-stream" --data-binary @chunk-0
$ curl -X POST https://lenslab.dev/api/heavenly/v1/uploads/UPLOAD_ID/complete -H "Authorization: Bearer hv_pat_YOUR_TOKEN" -H "Content-Type: application/json" -d '{}'

Schedule a clip

POST /jobs/{job_id}/clips/{clip_id}/schedule exports the clip if needed (answering export_pending until it is ready), then puts it on the calendar as a video post to your connected accounts. The post is held: nothing goes out until POST /posts/{post_id}/ready. It must be at least 10 minutes ahead, and TikTok posts are made in Heavenly's composer.

FieldTypeMeaning
captionstring, requiredThe post's text.
networksarray, requiredConnected networks to post to (from list_connected_accounts), e.g. x, youtube-shorts.
run_atstring, requiredWhen it goes out, ISO 8601, at least 10 minutes ahead.
titlestringTitle, for networks that take one (YouTube).
timezonestringIANA timezone the time was chosen in.

Caption a whole video

POST /captions burns captions into a whole video from a link and returns a job; once it completes, captioned_video.url is the MP4. It answers captions_unavailable until Heavenly's caption worker is live. It spends Clips minutes like a clip job.

FieldTypeMeaning
shapesource · 9:16 · 1:1Frame of the result: source keeps the video's own (default), or 9:16 / 1:1.
caption_styleviral-bold · clean-creator · high-energy · editorial · highlighted · scale · minimalist · box · staticbox · glow · hormozi · vlog · classic · pop-box · beast · karaoke · bold-pop · one-word · comic · neon · read-along · reveal · highlighter · clean · podcast · label · impact · cinema · elegant · marker · bubble · typewriterCaption look. Default: Heavenly's highlight look.
caption_colorstringAccent colour, #RRGGBB.
video_urlstring, requiredPublic https link to the video (YouTube, TikTok, Instagram, X, Twitch, Kick, Vimeo, a file URL…).
start_secondsnumberOnly clip from here (seconds).
end_secondsnumberOnly clip up to here (seconds).
titlestringName for the project.

Errors

A refusal is { "error": "a sentence", "code": "a_code" } with the HTTP status, and Retry-After when waiting helps.

StatusCodeMeaning
400invalid_requestA field is missing or wrong; the message names it.
401unauthorizedNo token, or one that is unknown, expired or revoked.
402no_planThe account has no Heavenly plan.
402insufficient_creditsNot enough Clips minutes left this month for this video.
403token_scopeThe token lacks the permission the endpoint needs.
404not_foundNo such job, clip or upload on this account.
409idempotency_conflictThat Idempotency-Key was used with other settings.
400invalid_trimA trim stays inside the clip Heavenly found and is at least 3 seconds.
404background_not_foundThat background isn't on this account's shelf.
409background_needs_verticalA background goes under a 9:16 clip.
409no_splitHeavenly didn't find two people to stack in this video.
409not_a_clips_jobThat job captions a whole video: it has no clips to change.
409job_stateThe job isn't in a state that allows it (still rendering, not failed): poll it and ask again.
409export_pendingScheduling waits for the 1080p export: call again.
429rate_limitedToo many calls; wait Retry-After seconds.
429token_daily_capThe token's spending calls for today are used.
429export_busyAnother export of yours is rendering; retry shortly.
503clips_api_unavailableClips over the API is switched off right now.
503background_unavailableBackgrounds over the API aren't live yet; the app still can.
503split_unavailableStacking two people over the API isn't live yet; the app still can.
503trim_unavailableTrimming over the API isn't live yet; the app still can.
503adjust_unavailableChanging clips over the API isn't live yet; the app still can.

Agents and the terminal

The same Clips is in the Heavenly MCP as clip_video, get_clip_job, list_clip_jobs, list_clips, get_clip_downloads, export_clip and schedule_clip, with adjust_clip, restyle_clips, find_more_clips, cancel_clip_job and retry_clip_job for access tokens once the worker is updated, and in the CLI:

Terminal
$ node heavenly.mjs clip https://www.youtube.com/watch?v=VIDEO_ID --confirm --wait
$ node heavenly.mjs clip ./podcast.mp4 --confirm --aspect 9:16
$ node heavenly.mjs download JOB_ID CLIP_ID --out best.mp4

The agent

Gemini can propose edits using the current project context. Timeline operations include cutting, trimming, splitting, speed, framing, and captions. Proposals are validated before application. If an operation fails, the whole batch leaves the previous sequence intact. Unsupported finishing operations are identified before export.

Operations the timeline can apply include cut_range, trim_clip, razor, set_aspect, set_look, and set_captions.

Jobs

A connected media service accepts uploads, persists jobs, transcribes speech, selects candidates, and renders previews with FFmpeg. The workspace shows progress, failures, cancellation, and downloadable results. Low-confidence or speechless input may produce no candidates instead of an invented result.

Keep/reject feedback and imported style references inform personalization within your account.

Captions

Styles

These presets are in the editor. Asking for one sets it on the project.

  • Pop. One phrase at a time, bold with an outline. Keyword highlights pop in.
  • Karaoke. Every word lights up as it is spoken.
  • Boxed. Text on a solid rounded box, brand colour behind the words.
  • Bounce. Words bounce in one by one. Loud, short-form energy.
  • Minimal. Small, clean sans, no animation. Talking heads and interviews.
  • Outline. Thick outline, no box. Readable over busy footage.

Generation, rendering, editing

The media service generates timed captions from a processed source transcript. Automatic transcription requires a configured transcription backend. You can also edit caption settings in the timeline or ask the assistant to change them.

Supported caption styles are burned into preview and final MP4 files. Editor and renderer share caption state. Bounce animation and oversized captions are blocked at MP4 export until supported, rather than silently omitted. Translation is not part of this workflow.

Final MP4 currently supports one processed source, contiguous cuts, speed, framing, and captions. Extra video layers, titles, effects, and an additional music mix are not yet composited. Export explains these limits. Undo restores the previous caption settings.

Automations

What an automation does

An automation watches one YouTube channel, or your own Twitch channel. Each new long-form upload, or each finished stream, is clipped with your Clips Template, the best clips are exported at 1080p, and they are posted to the accounts you choose at your posting times. Shorts, and videos published before the automation was set up, are left alone. A premiere is clipped once it has been published.

Setting one up

In Clips, open Automations and choose New automation. Paste the channel's link or @handle, then choose the clip length and focus, how many clips to post per video and the minimum score, the accounts, your posting times and timezone, and whether clips post automatically or wait for your approval. The Creator plan runs 1 automation at a time and Arena runs 5. Twitch automations and YouTube stream replays come with Arena. A paused automation doesn't count.

Twitch streams

Connect your Twitch account on the Integrations page, then choose My Twitch streams in New automation. Twitch doesn't let anyone download a whole stream, so Heavenly works from the clips Twitch shares with you: half an hour after a stream ends, it takes the clips your viewers made during it, most watched first, and the markers you placed, which Twitch cuts into clips of their own. Each one, at most a minute long, is downloaded through Twitch, framed and captioned with your Clips Template, and posted or held for your approval like any clip. A moment Clips finds no clean clip in is kept whole. A stream with no viewer clips and no markers is skipped. Only your own channel can be watched, and Twitch clips run up to 60 seconds, so clip lengths stop at 30–60s. Keep Store past broadcasts on in Twitch's stream settings: a stream is found by its video.

Clipping hours

Each upload is charged in Clips hours exactly like a pasted link: its length is held when clipping starts and settled when it finishes. Each Twitch clip is charged like an upload of its own length. Exporting and posting cost nothing. A video longer than Clips takes is clipped from its start, up to that length. When the month's hours run out, the automation pauses and emails you, keeping the video it found; resume it when you have hours again. Videos published while it is paused are not clipped.

Posting times and approval

Each clip takes the next free posting time on your clock, at least 15 minutes ahead, and two clips never share a time. With Hold for my approval, clips wait under their upload until you approve or skip each one. TikTok posts always wait for approval, because TikTok asks you to choose who sees each post. YouTube's made-for-kids answer and visibility, and a Pinterest board, are chosen once when the automation is set up. The calendar's daily limits per account apply: a full day moves the clip to the next.

From Claude and other agents

With an access token, the MCP tools find_youtube_channel, create_automation, list_automations, list_automation_runs, update_automation, pause_automation, resume_automation and delete_automation manage automations. Approving a held clip stays in Heavenly.

Scheduling

Status

A network that is not switched on yet shows Coming soon in Social accounts.

The calendar posts each scheduled cut at its time to the accounts you chose. Social accounts, opened from the rail, connects them with each network’s own sign-in.

From code or an agent, the Clipping API schedules a clip as a held video post; it goes out once it is marked ready.

Credits and usage

Allowance

Hobby is USD 20 a month. Creator is USD 50. Arena is USD 100, and Arena 2× is USD 200. The published monthly allowances are 50, 300, 800, and 1,600.

Meter

The editor does not subtract credits when you cut. Clips is paid in minutes of source video: a job holds them when it starts, settles to the video's real length, and gives them back if it fails; exporting a clip is free. GET /api/heavenly/v1/usage returns the Clips minutes and AI credits left. Motion designs are paid in credits: a job reserves them when it is accepted, charges them when it completes, and returns them if it fails (see the Motion API). Rollover is off. Credits are not cash.

Pricing and the terms read the same rule.

MCP reference

Heavenly MCP

Drive Heavenly from Claude, ChatGPT, Cursor, Claude Code, and any other MCP-capable client. Read projects, preview edits, and save approved changes through one secure connection.

Connect Heavenly MCP · Get an API key

Introduction

The Heavenly MCP server lets assistants work with your saved projects and timeline. The server validates commands and returns a proposal; edits and publishing still require your approval. Footage stays on your device.

Base URL:

https://lenslab.dev/api/heavenly-mcp

Listing is public for discovery. Project, edit, credit, and publish tools require the correct account connection and scope.

Quickstart

Three steps: connect, list tools, then ask Heavenly to work on a project.

1. Connect your client

For OAuth clients, paste the URL above and sign in. For Claude Code and other token-based clients, create a scoped Heavenly access token first.

2. Check the connection

Run this read-only request to see the tools available to an unauthenticated client:

Terminal · zsh
$ curl --fail-with-body --silent --show-error 'https://lenslab.dev/api/heavenly-mcp' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json, text/event-stream' \
  --data '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

3. Ask Heavenly

Try: “List my Heavenly projects.” Approve any edit or publishing proposal before it is saved.

Authentication

OAuth is the default for hosted clients such as Claude and ChatGPT. Claude Code and compatible terminal clients can use a scoped, revocable token prefixed withhv_pat_.

Terminal · zsh
$ claude mcp add --transport http heavenly https://lenslab.dev/api/heavenly-mcp --header "Authorization: Bearer hv_pat_YOUR_TOKEN"

Never commit a token to source control. Publishing requires the publish scope and an explicit approval step.

Tools

Tool visibility depends on the client connection and its granted scopes.

  • list_projects
  • get_project
  • create_project
  • get_timeline
  • list_edit_commands
  • edit_timeline
  • ask_editor_agent
  • apply_editor_proposal
  • discard_editor_proposal
  • get_credits
  • list_connected_accounts
  • list_posts
  • schedule_post
  • mark_post_ready
  • move_post
  • cancel_post
  • create_motion_design
  • create_motion_followup
  • get_motion_design
  • list_motion_designs
  • upload_motion_asset
  • finish_motion_upload
  • clip_video
  • get_clip_job
  • list_clip_jobs
  • list_clips
  • get_clip_downloads
  • export_clip
  • schedule_clip
  • add_footage
  • index_footage
  • get_footage_index
  • search_footage
  • create_variant_batch
  • generate_hooks
  • plan_variants
  • set_hook
  • render_batch
  • edit_variant
  • capture_frames
  • export_variant
  • find_youtube_channel
  • list_automations
  • list_automation_runs
  • create_automation
  • update_automation
  • pause_automation
  • resume_automation
  • delete_automation

Errors

Errors are returned as JSON-RPC errors with a stable code and a human-readable message. A missing or revoked token returns an authentication challenge; a missing scope explains which permission is required.

MCP and Heavenly

MCP is the connection layer that lets an AI assistant send approved commands to Heavenly. Running on Heavenly means using Heavenly's editor and workspace directly.

Claude Code

Use a scoped token to connect Claude Code to Heavenly. It can read projects, inspect timelines, propose edits, check credits, and schedule posts within the permissions you grant.

Terminal · zsh
$ claude mcp add --transport http heavenly https://lenslab.dev/api/heavenly-mcp --header "Authorization: Bearer hv_pat_YOUR_TOKEN"

See the interactive MCP setup for every client and tool.

CLI

Install

Clip from your terminal. Or let your agent do it.

npm install -g @heavenly/cli@1.0.0. The commands are heavenly and hv. Node.js 20 or newer. There are no runtime dependencies.

Clip

heavenly login then heavenly clip <url> --wait --download ./clips --json. JSON is the default when stdout is not a terminal. Progress is written to stderr. Exit codes run from 0 to 8.

Agents

Skill

Clip from your terminal. Or let your agent do it.

heavenly skill install --agent claude-code --scope user copies the skill. The same command takes cursor or codex. Always pass --json. Do not schedule or publish unless the person explicitly approved that post.

MCP

claude mcp add --transport http heavenly https://lenslab.dev/api/mcp. heavenly mcp install writes that entry for Claude Code, Cursor, Codex, or Claude Desktop and does not write a token into a project file.

Changelog

Editor

From the repository, not a launch list.

  • The video agent landed on /video, then the public door moved to /heavenly.
  • Revisions: each agent turn is numbered. Undo and redo move the head.
  • Gemini chat proposes edits beside the workspace, with preview, apply, and undo.
  • Private media jobs produce transcript-linked clips and supported MP4 downloads.
  • Export checks capabilities before writing MP4, EDL, FCPXML, or a shot sheet.
  • Plans are Hobby, Creator, Arena, and Arena 2×. Enterprise goes to sales.

Hosted media processing requires deployment and provider configuration. Full color grading, layered composition, multicam, and a public clipping API remain unfinished.