Building on this API?
Point your AI at this page (or send it the raw spec). It has everything needed to integrate ClipMaxxing: auth, endpoints, and copy-paste examples.
ClipMaxxing Public API v1
This document is the integration spec for the ClipMaxxing HTTP API. Give it to an AI or a developer that needs to generate vertical shorts (voiceover, captions, or AI video) from a bot, Discord app, or backend.
- Human-readable page: https://clipmaxxing.app/docs
- Raw markdown (this file): https://clipmaxxing.app/docs/api.md
- Product: https://clipmaxxing.app
ClipMaxxing turns a story (or a prompt) plus a background clip into a 1080x1920 MP4 with optional AI voiceover and word-synced captions. Billing uses the same credit wallet as the website. There is no separate API plan.
1. Base URL and authentication
| Environment | Base URL |
|---|---|
| Production | https://clipmaxxing.app/api |
| Local API (direct) | http://localhost:3000 |
All v1 paths in this document are relative to the API origin. In production, that origin is https://clipmaxxing.app/api (the site reverse-proxies /api to the Fastify server). Locally, call port 3000 with no /api prefix.
Authenticate every request with the header:
x-api-key: cmx_<48 hex characters>
Example:
GET /v1/projects HTTP/1.1
Host: clipmaxxing.app
x-api-key: cmx_...
Rules:
- Create and revoke keys in the website: Account → API keys (
https://clipmaxxing.app/account). - The full key is shown once at creation. Store it like a password.
- A key is bound to one user. Credits, library, and projects are that user's.
- An API key cannot create or list other API keys. That requires a website session cookie.
- Send JSON with
Content-Type: application/jsonon POST bodies. - Do not send a session cookie from a bot. Header auth is enough.
2. Typical bot flow
Generation is asynchronous. One POST starts a job. Poll until READY or FAILED, then download the MP4.
1. (optional) GET /v1/library/videos → pick libraryVideoId
2. (optional) GET /v1/voices → pick voiceId (storytelling only)
3. POST /v1/generate/<flow> → { projectId, status: "GENERATING" }
4. GET /v1/projects/{id} → poll every 3–5 seconds
5. GET /v1/projects/{id}/download → MP4 (ZIP for carousels) when status is READY
Rate limit on generate POSTs: 10 requests per minute per IP. Other v1 GETs use the global IP limit.
Recommended poll interval: 3 to 5 seconds. Jobs can take from about 30 seconds to several minutes (queue + TTS + FFmpeg, or AI video).
3. Endpoints
POST /v1/generate/storytelling
Creates a project and starts the pipeline: background video + spoken story + karaoke captions.
Provide either story (final voiceover script) or prompt (ClipMaxxing writes the script with the LLM, billed separately). Provide either libraryVideoId or videoUrl or brandId for the B-roll.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
story | string | one of story/prompt | Final voiceover script. | |
prompt | string | one of story/prompt | Brief, 3–4096 characters. Ignored if story is set. | |
libraryVideoId | cuid | one of libraryVideoId/videoUrl/brandId | Id from GET /v1/library/videos. | |
videoUrl | URL | one of libraryVideoId/videoUrl/brandId | Direct HTTPS MP4. ClipMaxxing downloads it in a stream. Do not send both this and libraryVideoId. | |
brandId | cuid | one of libraryVideoId/videoUrl/brandId | Template id. Inherits kit + default B-roll tags. | |
lang | enum | no | fr | fr, en, es, de, it, pt |
targetDurationSec | int | no | 60 | 10, 15, 30, 45, 60, 90 |
title | string | no | Max 200 characters. | |
voiceId | string | no | language default | Id from GET /v1/voices. |
captionStyle | enum | no | yellow-highlight | See caption styles below. |
captionPosition | enum | no | bottom | top, center, bottom, none |
storyPreset | enum | no | standard | standard or horror |
libraryMusicId | cuid | no | Optional bed music from the music library. |
Success 200:
{ "projectId": "clxxxxxxxxxxxxxxxxxxxxxxxxx", "status": "GENERATING" }
POST /v1/generate/text-scene
Same B-roll source rules as storytelling (libraryVideoId or videoUrl, story or prompt). No voiceover. Overlay text on the clip.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
story / prompt | string | one of them | Overlay copy. Keep it short (best under 400 characters). | |
libraryVideoId / videoUrl / brandId | one of them | Same as storytelling. | ||
lang | enum | no | fr | |
targetDurationSec | int | no | 60 | Same duration set as storytelling. |
title | string | no | ||
captionPosition | enum | no | bottom | |
libraryMusicId | cuid | no |
Response: same { projectId, status }.
POST /v1/generate/video
AI video. No background clip required. Default: silent 9:16 540p text-to-video.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
prompt | string | yes | 3–5000 characters. | |
lang | enum | no | en | |
targetDurationSec | int | no | 10 | Integer from 4 to 15. |
title | string | no | ||
generationSettings | object | no | see below |
Default generationSettings:
{
"mode": "text-to-video",
"quality": "540p",
"aspectRatio": "9:16",
"generateAudio": false,
"multiClip": false,
"imageIds": []
}
mode values: text-to-video, image-to-video, reference-to-video. quality: 540p or 720p. aspectRatio: 16:9 or 9:16 (required for text-to-video and reference-to-video). Public v1 does not upload reference images. Stay on text-to-video unless you already have imageIds from the website.
Response: same { projectId, status }.
POST /v1/generate/carousel
Photo carousel (swipeable image post). No background clip, no voiceover. The deliverable is a ZIP of cards, not an MP4.
This is the endpoint to replay a template with a different brief: pass brandId plus the brief, and the template supplies the language, the number of images and the visual template. Anything you pass in the body wins over the template.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
brief | string | yes | What the carousel is about, 12–5000 characters. Internal instruction: it is never published as the caption. | |
brandId | cuid | no | Template id. Supplies lang, slideCount, referenceImageId, the kit and the publishing accounts. | |
lang | enum | no | template, else fr | fr, en, es, de, it, pt |
title | string | no | Max 200 characters. | |
slideCount | int | no | template, else auto | Integer from 2 to 10. Auto lets the model pick 4 to 8. |
referenceImageId | cuid | no | template | Visual template photo. Ids come from the website upload. |
Response: same { projectId, status }. GET /v1/projects/{id}/download then returns application/zip.
POST /v1/generate/carousel-manual
Same ZIP of vertical cards as /v1/generate/carousel, but you write each card. No LLM plans the text. Each slide is 50% photo / 50% HTML (sanitized: no scripts, no images, no url() in CSS).
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
slides | object[] | yes | 2–10 cards. See below. | |
brandId | cuid | no | Template id. Supplies lang, visual template photo, and the accent color used on the category chip. | |
lang | enum | no | template, else fr | fr, en, es, de, it, pt |
title | string | no | Max 200 characters. | |
referenceImageId | cuid | no | template | Visual template photo for AI slides. Ids come from POST /images. |
Each slides[] item:
| Field | Type | Required | Notes |
|---|---|---|---|
html | string | yes | Card layout you write. Tags: p br hr strong b em i u s small mark sub sup ul ol li h1 to h6, span div section article header footer aside main blockquote table thead tbody tfoot tr th td caption. Inline style may set layout and type (display / flex / grid / position except fixed, sizes, padding, colors hex/rgb/rgba/hsl). No scripts, images, url(), content, or event handlers. Max 4000 characters. |
imagePrompt | string | one of | English prompt for the photo (min 3 chars). Required unless sourceImageId is set. |
sourceImageId | cuid | one of | Uploaded JPEG/PNG/WebP (POST /images). Mutually exclusive with imagePrompt. |
category | string | no | Plain text, max 32. Drawn as a chip on the right, on the photo/text split. Color comes from the template accent. |
Response: same { projectId, status }. Download is application/zip. Billing uses the same CREDIT_CAROUSEL_IMAGE as the classic carousel, only for slides without an upload.
POST /v1/generate/carousel-custom
Same ZIP of vertical cards, but you write the whole card layout, not just its text half. Each card is your HTML rendered at 1080×2400 (9:20, mobile). Exactly one element must carry id="placeholder": the generated (or uploaded) photo is inserted into it, scaled to cover the slot, and cropped to that slot's rendered shape (border-radius, including a circle, or clip-path). Flat margins on the source are trimmed first, so a square photo does not leave pale bars inside the visible area. No src is ever accepted from you.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
slides | object[] | yes | 2–10 cards. See below. | |
brandId | cuid | no | Template id. Supplies lang and the visual template photo. | |
lang | enum | no | template, else fr | fr, en, es, de, it, pt |
title | string | no | Max 200 characters. | |
referenceImageId | cuid | no | template | Visual template photo. Ids come from POST /images. |
Each slides[] item:
| Field | Type | Required | Notes |
|---|---|---|---|
html | string | yes | Full card layout. Same tag and inline-style allowlist as carousel-manual, plus figure figcaption dl dt dd and gradient background-image. The only attributes kept are the filtered style and a single id="placeholder". No scripts, images, url(), or event handlers. Max 12000 characters. |
imagePrompt | string | one of | English prompt for the photo (min 3 chars). Required unless sourceImageId is set. |
imageAspect | enum | no | 9:16 (default), 3:4, 1:1, 4:3, 16:9. Pick the ratio closest to your slot. |
sourceImageId | cuid | one of | Uploaded JPEG/PNG/WebP (POST /images). Mutually exclusive with imagePrompt. |
Fonts available in the card: Montserrat, Inter, Anton, Playfair Display, Space Mono. Keep text clear of the TikTok UI: the top 400 px, the bottom 560 px and ~160 px on each side.
Response: same { projectId, status }. Download is application/zip. Billing uses the same CREDIT_CAROUSEL_IMAGE as the classic carousel, only for slides without an upload.
GET /v1/projects?limit=10
Lists the caller's latest projects, newest first. limit is an integer from 1 to 10 (default 10).
[
{
"id": "clxxx...",
"title": "My short",
"videoStyle": "storytelling",
"status": "READY",
"createdAt": "2026-08-23T12:00:00.000Z",
"downloadUrl": "https://clipmaxxing.app/api/v1/projects/clxxx.../download"
}
]
downloadUrl is null until the latest version is READY. Prefer GET /v1/projects/{id}/download with the same x-api-key header.
GET /v1/projects/:id
{
"id": "clxxx...",
"status": "GENERATING",
"steps": [
{ "label": "Script", "status": "DONE" },
{ "label": "Voice", "status": "RUNNING" },
{ "label": "Video ready", "status": "PENDING" }
],
"queuePosition": 1,
"downloadUrl": null,
"error": null
}
statuson the project:DRAFT,GENERATING,READY,FAILED.- Each step
status:PENDING,RUNNING,DONE,FAILED. queuePositionis0unless the job is waiting in the queue (QUEUED). Then it is 1-based.erroris a string when the latest version failed, otherwisenull.- Stop polling when
statusisREADY(download) orFAILED(readerror). Do not retry a failed project automatically without a new generate call.
GET /v1/projects/:id/download
Streams the MP4 of the latest READY version.
- Headers:
x-api-key(required). - Response:
Content-Type: video/mp4,Content-Disposition: attachment. 404if the project is missing, not yours, or not ready yet.
GET /v1/library/videos
Published B-roll clips you can pass as libraryVideoId.
{
"enabled": true,
"videos": [
{ "id": "clxxx...", "title": "City night", "durationSec": 12.4, "tags": ["urban"] }
]
}
If enabled is false, the library is not configured on that instance. Use videoUrl instead.
GET /v1/voices
Voice catalog for voiceId (storytelling). Each item: { id, name, languages } where languages is a subset of fr, en, es, de, it, pt.
GET /v1/brands
Templates of the key's user. Response is a JSON array (not wrapped). The webhook secret is never returned (webhookConfigured: true|false instead). Each template includes attached accounts (id, platform, alias, label, state).
Pass brandId on generate endpoints to inherit voice, captions, illustration style, B-roll tags, and narration tone. The applied kit is frozen on the ProjectVersion (brandKit). Replaying a version does not re-read the current template. Editing a template does not change jobs already generated.
GET /v1/brands/:id
One template, same object as a list item. 404 if the id is unknown or belongs to another user.
POST /v1/brands
Creates a template. Rate limit: 10 requests per minute per IP.
Only name is required. Everything else is optional and can be filled later with PATCH. Linking YouTube / TikTok / Instagram accounts is done on the website (OAuth). The accounts array on the response is empty until then.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
name | string | yes | 1–60 characters. Unique per user. 409 if taken. | |
color | hex | no | #6750A4 | Accent, #RRGGBB. |
sortOrder | int | no | 0 | 0–999. List order. |
description | string | no | Max 500 characters. | |
lang | enum | no | fr, en, es, de, it, pt. | |
videoStyle | enum | no | storytelling, text-scene, video-generation, carousel, carousel_manual, carousel_custom. | |
storyPreset | enum | no | standard or horror (storytelling script only). | |
targetDurationSec | int | no | 10, 15, 30, 45, 60, 90. | |
voiceId | string | no | Id from GET /v1/voices. | |
captionStyle | enum | no | See caption styles on generate. | |
captionPosition | enum | no | top, center, bottom, none. | |
promptAddon | string | no | Brand voice clause injected into the LLM. Max 2000. Stay positive. | |
illustrationStyle | string | no | Positive image-style suffix. Max 400. | |
slidesDirectives | string | no | Max 500. | |
palette | hex[] | no | Up to 8 #RRGGBB. | |
settingHint | string | no | Time of day, weather, light. Max 220. | |
cast | object[] | no | Up to 8 { name, lock }. | |
titleTemplate | string | no | Publish title. {title} is replaced. Max 100. | |
captionTemplate | string | no | Publish caption. {story} is a short excerpt. Max 2200. | |
hashtags | string | no | Appended to the caption. Max 400. | |
carouselSlideCount | int or null | no | 2–10. null clears: Auto (4–8). | |
carouselReferenceImageId | cuid or null | no | Visual template photo. Upload with POST /images (same x-api-key, multipart field file). null clears. 404 if the image is not yours. | |
reviewRequired | bool | no | true | |
libraryVideoTags | string[] | no | [] | Up to 12 tags. B-roll fallback when generate has no clip. |
defaultLibraryVideoId | cuid or null | no | Preferred library clip. Id from GET /v1/library/videos. | |
musicCatalogId | string or null | no | Built-in bed music catalog id. | |
sourceAudioId | cuid or null | no | User-uploaded bed. | |
webhookUrl | URL or null | no | See webhooks below. Max 500. | |
webhookSecret | string or null | no | Write-only, 8–128 characters. Never returned. |
Success 200: the created template (same shape as GET /v1/brands/:id).
PATCH /v1/brands/:id
Partial update. Send at least one field (same keys as POST). Empty body is 400. Rate limit: 10 / minute. 404 if unknown. 409 if the new name is taken.
Changing the kit does not rewrite brandKit on versions already generated.
DELETE /v1/brands/:id
Deletes the template. Linked social accounts are not deleted: they become ungrouped. Existing projects keep their frozen kit. 404 if unknown. Returns { "ok": true }.
GET /v1/review
Projects in READY + reviewStatus: PENDING. Filter later client-side by brandName.
POST /v1/projects/:id/approve
Body (all optional): { "mode": "queue", "scheduledAt": "...", "accountIds": ["..."] }.
Default mode is queue (next posting slot). now publishes immediately (TikTok still drops in the inbox unless Direct Post is enabled). Omit accountIds to use every ACTIVE YouTube / TikTok / Instagram account on the project's template.
Title and caption come from the frozen kit templates ({title}, {story}, hashtags).
POST /v1/projects/:id/reject
Marks the project REJECTED. It leaves the review queue. Does not delete the MP4.
POST /v1/posts
Same body as the website composer: { versionId, targets: [{ accountId }], mode, title?, caption?, scheduledAt? }. mode: now | queue | at | draft.
POST /v1/generate/batch
{ "brandId": "...", "prompts": ["...", "..."] } (1 to 10 prompts). Each prompt starts a storytelling job with the template kit and B-roll. Returns { projectIds, status: "GENERATING" }. Stops on the first failure (credits, missing B-roll).
POST /v1/ingest
Finished MP4, no AI pipeline. { "brandId": "...", "videoUrl": "https://...", "title": "optional" }. Creates a READY project in the review queue. Used by Discord /drop.
GET /v1/inbox
TikTok drops waiting in the creator inbox (uploadToInbox). Open the TikTok app to publish. Public TikTok posting is not automated.
GET /v1/projects?limit=10&brandId=&review=
review is PENDING | APPROVED | REJECTED. brandId filters by template.
3b. Webhooks (per template)
Set webhookUrl (and optionally webhookSecret) on the template. ClipMaxxing POSTs JSON:
{
"event": "project.ready",
"projectId": "clxxx...",
"brandId": "clxxx...",
"brandName": "Horror FR",
"title": "...",
"status": "READY",
"reviewStatus": "PENDING",
"downloadUrl": "https://clipmaxxing.app/api/v1/projects/clxxx.../download"
}
Events: project.ready, project.failed, project.approved, project.rejected.
If a secret is set, header X-ClipMaxxing-Signature: sha256=<hex> (HMAC-SHA256 of the raw body). A failing webhook never fails the pipeline.
Point webhookUrl at a Discord incoming webhook for a ping in #review-horror, or at your own bot. Never put CLIPMAXXING_API_KEY in a Discord message.
3c. Discord bot (apps/discord)
Two audiences, one process. Env: DISCORD_BOT_TOKEN, DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET (OAuth), DISCORD_BOT_SECRET (API impersonation), CLIPMAXXING_API_URL, CLIPMAXXING_API_KEY (editorial only). Optional: DISCORD_STAFF_GUILD_ID, DISCORD_ADMIN_IDS. Run npm run dev:discord.
Link Discord on Account → Connect Discord. The bot DMs you; if that is blocked, open a DM and type /start. Credits are billed to the linked ClipMaxxing user (x-discord-bot-secret + x-discord-user-id). Never put CLIPMAXXING_API_KEY in a Discord message.
Client commands (DM / user install)
| Command | Action |
|---|---|
/start | Wizard: voice-over short, text on B-roll, or AI video |
/short flow: text: | One-shot generate (optional video / image attachments) |
/status | Latest projects and credit balance |
/cancel | Abort the wizard (not a running worker job) |
When the render is ready, the bot attaches the MP4 (if small) or a 1-hour download link (/d/:token).
Editorial commands
With DISCORD_STAFF_GUILD_ID they are guild-only. CLIPMAXXING_API_KEY is still the editorial auth; staff does not need to link Discord.
| Command | Action |
|---|---|
/generate template: prompt: | Storytelling with the template kit |
/drop template: video: | Ingest a finished MP4 into review |
/review | Queue + Approve / Reject buttons |
/schedule template: | Approve latest pending into the posting queue |
/inbox | TikTok inbox drops |
/templates | List templates |
/lines | Alias of /templates |
TikTok public posting is never automated. Approve still drops to the TikTok inbox.
4. Caption styles
For captionStyle on storytelling:
yellow-highlight(default)kineticneon-outlineboxedkaraokered-markermono-terminalluxury-serifimpact-stroke
captionPosition: top, center, bottom (default), none.
5. Credits and errors
Every generation holds credits on the user wallet (same as the website), then settles the real cost when the pipeline finishes. If the balance is too low, the call fails before the job is queued.
Error body (all endpoints):
{ "error": "Human-readable message" }
| HTTP | Meaning |
|---|---|
400 | Invalid JSON / Zod validation / missing source or story. |
401 | Missing, unknown, or revoked API key. |
402 | Not enough credits. Body may include required, balance, code. |
404 | Project, clip, template, or file not found. |
409 | A generation is already running, or a template already has that name. |
422 | LLM refused the prompt (policy). |
429 | Rate limit. Wait about one minute. |
502 | Upstream (download, library cache, model) failed. Retry later. |
503 | Queue, billing, library, or provider not configured. |
Validation messages look like field : message plus an issues array.
6. Copy-paste examples
Replace BASE and KEY.
Storytelling from a library clip + prompt
curl -sS -X POST "$BASE/v1/generate/storytelling" \
-H "x-api-key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A 60-second story about a baker who finds a golden coin in the dough.",
"libraryVideoId": "REPLACE_WITH_CLIP_ID",
"lang": "en",
"targetDurationSec": 60
}'
Storytelling from a public MP4 URL + finished script
curl -sS -X POST "$BASE/v1/generate/storytelling" \
-H "x-api-key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"story": "You will not believe what happened in that bakery tonight. ...",
"videoUrl": "https://example.com/broll.mp4",
"lang": "fr",
"targetDurationSec": 45,
"voiceId": "REPLACE_WITH_VOICE_ID",
"captionStyle": "karaoke"
}'
Poll then download
PROJECT_ID="clxxx..."
while true; do
BODY=$(curl -sS -H "x-api-key: $KEY" "$BASE/v1/projects/$PROJECT_ID")
STATUS=$(printf '%s' "$BODY" | sed -n 's/.*"status":"\([^"]*\)".*/\1/p' | head -n1)
echo "$BODY"
if [ "$STATUS" = "READY" ]; then
curl -sS -H "x-api-key: $KEY" \
-o "short.mp4" \
"$BASE/v1/projects/$PROJECT_ID/download"
break
fi
if [ "$STATUS" = "FAILED" ]; then
echo "Generation failed"
exit 1
fi
sleep 4
done
Text-scene
curl -sS -X POST "$BASE/v1/generate/text-scene" \
-H "x-api-key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"story": "Open tonight. Last table.",
"libraryVideoId": "REPLACE_WITH_CLIP_ID",
"targetDurationSec": 10,
"lang": "en"
}'
Create a template then generate a carousel
BRAND=$(curl -sS -X POST "$BASE/v1/brands" \
-H "x-api-key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Breakfast carousel",
"lang": "en",
"videoStyle": "carousel",
"carouselSlideCount": 5,
"titleTemplate": "{title}",
"captionTemplate": "{story}",
"hashtags": "#breakfast #highprotein"
}')
echo "$BRAND"
BRAND_ID=$(printf '%s' "$BRAND" | sed -n 's/.*"id":"\([^"]*\)".*/\1/p' | head -n1)
curl -sS -X POST "$BASE/v1/generate/carousel" \
-H "x-api-key: $KEY" \
-H "Content-Type: application/json" \
-d "{
\"brandId\": \"$BRAND_ID\",
\"brief\": \"5 quick high-protein breakfasts, one dish photo per card with the macros under it\"
}"
PATCH /v1/brands/$BRAND_ID with { "carouselSlideCount": 6 } updates later jobs only.
To set a visual template photo, upload first (POST /images, multipart file), then PATCH carouselReferenceImageId with the returned id.
Carousel from a template (same template, new brief)
curl -sS -X POST "$BASE/v1/generate/carousel" \
-H "x-api-key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"brandId": "REPLACE_WITH_TEMPLATE_ID",
"brief": "5 quick high-protein breakfasts, one dish photo per card with the macros under it"
}'
The same call with another brief reuses the template's visual template, image count and language.
Manual carousel (HTML per card)
curl -sS -X POST "$BASE/v1/generate/carousel-manual" \
-H "x-api-key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"brandId": "REPLACE_WITH_TEMPLATE_ID",
"lang": "en",
"slides": [
{
"html": "<h1>Overnight oats</h1><p>150 g oats</p>",
"imagePrompt": "overhead photo of overnight oats in a glass jar",
"category": "Breakfast"
},
{
"html": "<h1>Eggs</h1><p>2 eggs, 1 tsp oil</p>",
"imagePrompt": "sunny-side eggs in a small skillet"
}
]
}'
imagePrompt and sourceImageId are mutually exclusive per slide. The template color is the category chip background.
Custom carousel (full card layout)
curl -sS -X POST "$BASE/v1/generate/carousel-custom" \
-H "x-api-key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"lang": "en",
"slides": [
{
"html": "<div style=\"display:flex;flex-direction:column;height:100%;background-color:#101018\"><div id=\"placeholder\" style=\"height:1400px\"></div><h1 style=\"font-family:Montserrat;font-size:86px;color:#fff;padding:0 160px\">Overnight oats</h1></div>",
"imagePrompt": "overhead photo of overnight oats in a glass jar",
"imageAspect": "1:1"
},
{
"html": "<section style=\"position:relative;height:100%\"><div id=\"placeholder\" style=\"position:absolute;inset:0\"></div><p style=\"position:absolute;bottom:560px;left:160px;font-size:48px;color:#fff\">2 eggs, 1 tsp oil</p></section>",
"imagePrompt": "sunny-side eggs in a small skillet"
}
]
}'
The photo goes inside id="placeholder"; anything else you put in that element stays on top of it.
AI video (text-to-video)
curl -sS -X POST "$BASE/v1/generate/video" \
-H "x-api-key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Silent vertical shot of rain on neon pavement, cinematic, loopable, no text",
"lang": "en",
"targetDurationSec": 8
}'
7. Integration notes for AIs and bots
- Treat this file as the source of truth. Do not invent extra v1 endpoints.
- One-shot generate endpoints already create the project and enqueue the worker. Do not call the website's
/projectsthen/projects/:id/generatefrom a bot. - Always send
x-api-keyon the download request. A bare URL without the header returns401. videoUrlmust be a publicly reachable MP4 (h264/hevc/vp8/vp9/av1/mpeg4). Huge files may fail the instance upload cap.- If you pass
promptwithoutstory, ClipMaxxing bills a story-generation LLM call first, then the pipeline. - Horror preset (
storyPreset: "horror") only applies to storytelling script generation. No gore. brandIdon generate endpoints inherits the template kit (voice, captions, illustration style, B-roll tags). The applied kit is frozen on the version.- Provide either
libraryVideoIdorvideoUrlorbrandId(brand B-roll from default clip / library tags). GET /v1/brands,GET /v1/brands/:id,POST /v1/brands,PATCH /v1/brands/:id,DELETE /v1/brands/:id,GET /v1/review,POST /v1/projects/:id/approve,POST /v1/projects/:id/reject,POST /v1/posts,POST /v1/generate/batch,POST /v1/ingest,GET /v1/inboxexist for bots.GET /v1/brandsreturns a raw array. Do not call the website's/account-groupsfrom a bot.- Approve queues YouTube / Instagram and drops TikTok in the inbox. There is no public TikTok post without Direct Post audit.
- Illustration prompts stay positive. Do not send “no comic strip” style negatives; z-image has no
negative_prompt. - Projects expire after the product retention window (default 5 days on the website). Download promptly.
- Do not store the API key in client-side Discord messages. Use a server-side bot token store.
8. Key management (website only, not v1)
These routes require a logged-in browser session, not x-api-key:
GET /api-keys: list{ id, name, prefix, createdAt, lastUsedAt }POST /api-keys{ "name": "Discord bot" }: returns{ id, key, prefix }onceDELETE /api-keys/:id: revoke (soft)
Key format: cmx_ + 48 lowercase hex characters. Prefix (first 8 characters) is what the profile UI shows.