ClipTaps for developers & AI agents

ClipTaps is drivable programmatically. Developers use the REST API to generate and publish faceless short-form videos — and, on paid plans, to render with a recurring AI persona. A machine-readable plan catalogue, a model catalogue and an A2A agent card make pricing and capabilities discoverable without a human. The OpenAPI spec is served at /api/docs.json (Swagger UI at /api/docs). An MCP server is planned — see Roadmap.

Authentication

Authenticated requests use a Bearer token. Create a long-lived API key under Settings → API Keys (recommended for scripts and agents) — or use the short-lived session JWT the web app issues. A key acts as your account, so plan quotas and capability gates apply unchanged. You may hold up to 10 active keys, each with an optional expiry of 1–730 days; the plaintext key is shown once. Keys are minted and revoked only from a logged-in browser session — an API key calling those endpoints gets 403. Public endpoints — the plan catalogue, the model catalogue, the stock voice list and the agent card — need no auth. OAuth 2.1 is on the roadmap.

Authorization: Bearer ct_live_…   # API key (valid until revoked or expired)

POST   /api/api-keys        { "name": "ci", "expiresInDays": 90 }   # session JWT only → 201, key shown once
GET    /api/api-keys                                                 # masked list (key or JWT)
DELETE /api/api-keys/{id}                                            # session JWT only

# Error envelope on every 4xx/5xx from the API:
# { "message": "…", "code": "PLAN_UPGRADE_REQUIRED", ...details }   # code + details only when present

Plans & entitlements

Every tier’s limits come from one source of truth. Each plan defines videos per period (Free resets daily; paid tiers reset monthly), a scene cap per video, whether output is watermarked, three capability flags — socialPublishing, premiumVideoModels, aiPersonas — and whether an AI agent may purchase it. Read the live catalogue:

GET https://cliptaps.com/api/payments/plans   # public, no auth

# → { "plans": [ { "id": "free", "tier": "FREE", "label": "Free", "priceUsd": 0,
#       "currency": "usd", "interval": null, "period": "day", "videosPerPeriod": 1,
#       "maxScenesPerVideo": 3, "watermark": true, "socialPublishing": false,
#       "premiumVideoModels": false, "aiPersonas": false, "agentPurchasable": false,
#       "stripePriceId": null }, ... ] }
TierVideosScenesWatermarkSocial publishingPaid-only modelsAI personasAgent buy
Free1 / day3YesNoNoNoNo
Starter10 / mo5NoYesYesYesYes
Pro30 / mo10NoYesYesYesYes
Enterprise50 / mo50NoYesYesYesNo

A paid feature the plan lacks is refused with 403 and a machine-readable code, so a client branches on code and never on message text. A lapsed Stripe subscription (cancelled / unpaid / incomplete) falls back to Free; a past-due one keeps its video limits and paid-only models but loses social publishing.

# 403 on any gated endpoint:
{ "message": "AI personas are available on paid plans. Upgrade to Starter, Pro or Enterprise …",
  "code": "PLAN_UPGRADE_REQUIRED",
  "capability": "personas",            # or "socialPublishing" | "premiumVideoModels"
  "upgradeUrl": "/settings?tab=subscription",
  "modelLabel": "Kling 2.6" }          # only for premiumVideoModels

Quota & usage

Check how much of the current window is left, and which capabilities the account holds right now (these reflect the entitled plan, not the tier string on the row). Generation is charged one video credit per project — re-generating the same project is free, and mock runs are free — and enforced server-side; when the window is exhausted, generation returns 402.

GET https://cliptaps.com/api/payments/status        # auth

# → { "tier": "PRO", "status": "active", "currentPeriodEnd": "2026-10-01T00:00:00.000Z",
#     "videoCreditsUsed": 4, "videoCreditsLimit": 30, "videoCreditsRemaining": 26,
#     "quotaResetAt": "2026-10-01T00:00:00.000Z", "quotaPeriod": "month",
#     "maxScenesPerVideo": 10, "watermark": false,
#     "socialPublishing": true, "premiumVideoModels": true, "aiPersonas": true }

# 402 from POST /api/generate/video-start when exhausted (message only, no fields):
# { "message": "Monthly video limit reached (30/30). Resets 2026-10-01. Upgrade your plan for more videos." }

Video models & cost

One catalogue drives the UI selectors, the API validation and the render workers. Read it to build your own model picker; every model lists its modes, durations, supportedAspects, resolutionsByDuration and pricing. Models flagged paidOnly need a paid plan (see the 403 above); speaksScript marks a model that voices the narration itself; refusesRealFaces marks one that renders from text only and rejects face pictures.

GET https://cliptaps.com/api/video/models   # public, no auth
# → { "version": 16, "defaultProvider": "veo",
#     "providers": [ { "id", "label", "models": [ { "id", "label", "modes", "durations",
#       "resolutionsByDuration", "supportedAspects", "pricing", "paidOnly"?, "speaksScript"?, "refusesRealFaces"? } ] } ] }

Catalogue v16, rendered from the same file the API serves. Prices are the catalogue’s pricing values (per second of output unless noted). The last column is how a talking / interview persona keeps one face on that model.

Provider / modelPlanPricingDurations · aspectsFlagsPersona face
Google Veo 3.1
veo / veo-3.1-fast-generate-preview
Veo 3.1 Fast
all$0.1 (720p) · $0.12 (1080p) · $0.3 (4k) / s4–8 s
16:9, 9:16
defaultReference images: carrier + wardrobe / sheet ¾ + location (≤ 3)
Google Veo 3.1
veo / veo-3.1-lite-generate-preview
Veo 3.1 Lite
all$0.05 (720p) · $0.08 (1080p) / s4–8 s
16:9, 9:16
—Image-to-video from the carrier
Google Veo 3.1
veo / veo-3.1-generate-preview
Veo 3.1 Standard
all$0.4 (720p, 1080p) · $0.6 (4k) / s · with references $0.4 (720p, 1080p) · $0.6 (4k) / s4–8 s
16:9, 9:16
—Reference images: carrier + wardrobe / sheet ¾ + location (≤ 3)
MiniMax Hailuo
hailuo / MiniMax-H3
MiniMax H3 — T2V/I2V/FL2V/references
all$0.08 (720p, 768p) · $0.13 (1080p, 2k, 4k) / s4–15 s
16:9, 9:16, 1:1
defaultReference images: carrier + wardrobe / sheet ¾ + location (≤ 3)
MiniMax Hailuo
hailuo / MiniMax-Hailuo-2.3
Hailuo 2.3 — T2V/I2V
all$0.28 – $0.49 / video6–10 s
16:9, 9:16, 1:1
—Image-to-video from the carrier
MiniMax Hailuo
hailuo / MiniMax-Hailuo-2.3-Fast
Hailuo 2.3 Fast — I2V only
all$0.19 – $0.33 / video6–10 s
16:9, 9:16, 1:1
—Image-to-video from the carrier
MiniMax Hailuo
hailuo / MiniMax-Hailuo-02
Hailuo 02 — T2V/I2V/FL2V
all$0.1 – $0.56 / video6–10 s
16:9, 9:16, 1:1
—Image-to-video from the carrier
MiniMax Hailuo
hailuo / S2V-01
S2V-01 — subject-reference
allno catalogue price6–6 s (fixed)
16:9
fixedDurationCannot hold a face → 422 for talking / interview
Kling AI
kling / kling-2.6
Kling 2.6
paid$0.042 (720p) · $0.07 (1080p) / s5–10 s
16:9, 9:16
default, paidOnlyImage-to-video from the carrier
Kling AI
kling / kling-3.0
Kling 3.0
paid$0.084 (720p) · $0.112 (1080p) / s4–15 s
16:9, 9:16
paidOnlyImage-to-video from the carrier
Kling AI
kling / kling-avatar
Kling Avatar — talking head
paid$0.056 (720p) · $0.112 (1080p) / s4–15 s
16:9, 9:16, 1:1
paidOnly, speaksScriptCarrier still + narration audio — the model lip-syncs the line
ByteDance Seedance
seedance / dreamina-seedance-2-5-260628
Seedance 2.5
paid$0.231 (720p) / s4–15 s
16:9, 9:16
default, paidOnly, refusesRealFacesCannot hold a face → 422 for talking / interview
Image scenes (Gemini 3.1 Flash Lite Image)
image / gemini-3.1-flash-lite-image
Gemini 3.1 Flash Lite Image
all$0.0336 / still (one per scene)4–12 s
16:9, 9:16, 1:1
defaultCannot hold a face → 422 for talking / interview

Set the model on the project with PATCH /api/projects/{id}/settings { "video_provider": "kling", "video_model": "kling-2.6" }. A model that cannot produce the project’s video_format is refused with 400 before any credit is charged.

Generate & publish a video

Generation is a short pipeline. Create a project (it must belong to the caller — 401 without a bearer), set its settings, attach source text, build the storyboard (which writes the scenes and, on a persona project, runs the sensitive-script guardrail), queue the scenes and poll until they complete, then assemble the final video and publish it.

POST  /api/projects                                      → 201 { "projectId" }
PATCH /api/projects/{projectId}/settings                 { "video_format": "9:16", "video_quality": "720p",
                                                           "num_scenes": 5, "scene_duration": 6,
                                                           "video_provider": "veo", "video_model": "veo-3.1-fast-generate-preview" }
                                                         # partial merge — send only the keys you change
POST  /api/projects/{projectId}/source-data/text         { "text_prompt": "..." }
POST  /api/projects/{projectId}/storyboard/script        { "num_scenes": 5, "scene_duration": 6, "use_mock": false }
                                                         → { scenes: [{ id, orderIndex, scriptText, promptVisual, promptAction, promptAudio }],
                                                             meta, sceneClamp?, durationSnap?, guardrail? }
                                                         # 422 PERSONA_GUARDRAIL_BLOCKED on a persona project
POST  /api/generate/video-start                          { "sceneIds": ["…","…"], "use_mock": false }
                                                         → 202 { "status": "QUEUED", "queuedCount": 5 }
                                                         # use_mock defaults to false; true renders placeholders and
                                                         # charges no credit. 400 aspect · 403 PLAN_UPGRADE_REQUIRED ·
                                                         # 422 PERSONA_IDENTITY_UNAVAILABLE · 402 quota — all before the credit
GET   /api/generate/video-status/{projectId}             → { "status", "scenes": [{ id, status, videoUrl, thumbnailUrl, error }] }
POST  /api/generate/video-stop/{projectId}               # cancel queued scenes
POST  /api/projects/{projectId}/assemble                 { "selectedSceneIds": ["…"], "tts": { "enabled": true },
                                                           "music": { "enabled": true }, "hookText": "…" }   → 202
GET   /api/projects/{projectId}/assemble/status          → { "status": "IDLE|PROCESSING|COMPLETED|FAILED", "finalVideoUrl", "error", "voiceFallbackScenes" }
POST  /api/posting/create                                { "projectId", "platform": "tiktok|instagram|youtube",
                                                           "mode": "manual|auto", "title", "description",
                                                           "hashtags"?, "scheduledTime"?, "accountId"?,
                                                           "platformSettings"?: { "tiktokSettings": { "privacy_level", "express_consent_given": true, "content_preview_confirmed": true } } }
                                                         # paid plans only (403 PLAN_UPGRADE_REQUIRED · socialPublishing)

GET /api/projects lists your projects with status, title, thumbnailUrl, finalVideoUrl and per-scene status. A title is required to publish; TikTok additionally requires a privacy level and both consent flags. Connected accounts come from GET /api/social/accounts. When the project renders a photoreal persona, publishing appends the AI-disclosure line to the description and sets the platform’s AI-content label (see below) regardless of what the request sends.

AI personas

A persona is a recurring on-camera character with a locked look and a voice. Every /api/personas endpoint except GET /api/personas/niches is behind the plan gate (403 PLAN_UPGRADE_REQUIRED, capability personas). Reads, updates and deletes are owner-scoped: another account’s persona answers 404. Up to 50 personas per account. Health, finance, legal and political niches are marked sensitive because YouTube will not monetise an AI persona presenting as an expert on them; the niche catalogue returns the policy text.

1. Create the profile

GET  /api/personas/niches            → { niches, sensitive, policy: { url, heading, quote, examples } }
POST /api/personas                   { "name": "Mara", "age": 34, "niche": "nature",
                                       "backstory"?, "usp"?, "pov"?, "seriesFormats"?: ["Five levels of …"],
                                       "signatureMoves"?: [{ "name", "description", "maxPerVideo", "allowedBeats" }],
                                       "cameraStyle"?: "propped|handheld_selfie|placement_open|companion|vehicle",
                                       "imageQuality"?: "clean_flagship|retro_phone",
                                       "forbidden"?: [...], "photoreal"?: true,
                                       "voiceNotes"?, "stockVoice"? }        → 201 PersonaDTO (409 at the limit)
GET  /api/personas · GET|PUT|DELETE /api/personas/{id}
PUT  /api/personas/{id}/references   { "references": [{ "url", "title"?, "platform"?, "views"? }] }   # ≤ 30, proven-demand refs
DELETE /api/personas/{id}/references/{referenceId}

2. Lock the look

Render portrait candidates until one is right, approve it — that locks the persona’s identityAnchors — then optionally render the three-angle sheet, wardrobe variants and locations from the locked portrait. Each render is billed per still from the image model and counted against a per-persona image budget (402 when reached).

GET  /api/personas/{id}/budget             → { spentUsd, capUsd, perImageUsd }
POST /api/personas/{id}/portrait           { "appearance": "…10–2000 chars…" }  → 201 { candidateId, url, prompt, mock, costUsd }
POST /api/personas/{id}/portrait/approve   { "candidateId" }                    → PersonaDTO   (409 if already locked)
POST /api/personas/{id}/sheet              → 201 { persona, assets: [frontal, front_three_quarter, side_profile] }  (400 without a locked portrait)
POST /api/personas/{id}/wardrobe           { "outfit": "a camel wool coat over a black turtleneck" }   → 201 { persona, asset }
POST /api/personas/{id}/locations          { "description": "a cluttered Georgian drawing room at dusk" } → 201
POST /api/personas/{id}/locations/upload   multipart file                                             → 201
DELETE /api/personas/{id}/wardrobe/{assetId} · DELETE /api/personas/{id}/locations/{assetId}

3. Give it a voice

POST   /api/personas/{id}/voice     multipart: file=<mp3|wav|m4a clip>, consent=true, voiceNotes?
                                    # 400 VOICE_CONSENT_REQUIRED without consent=true
DELETE /api/personas/{id}/voice
# or a stock voice instead of a clone: PUT /api/personas/{id} { …, "stockVoice": "<id from GET /api/tts/voices>" }

4. Link a project and pick the format

Linking copies the persona’s voice into the project’s TTS settings and stamps the render defaults (captions on; music off for the spoken formats). Formats: talking (direct to camera), interview (eyeline on an off-camera interviewer; the first line is their question) and broll (silent b-roll with upper-third text). Default talking. The storyboard then writes the persona’s own lines, and the hook stage produces variants you can choose between.

PATCH /api/projects/{projectId}/settings   { "personaId": "<persona id>", "personaFormat": "talking",
                                             "seriesFormat"?: "Five levels of …" }      # personaId: null unlinks
POST  /api/projects/{projectId}/storyboard/script        # runs the sensitive-script guardrail
      → { …, "guardrail": { "verdict": "accept|rewrite", "reasons", "flagged_sentences", "rewritten" } }
      # 422 PERSONA_GUARDRAIL_BLOCKED when a first-person expert line still fails after one third-person rewrite
POST  /api/projects/{projectId}/hook/select              { "index"?: 1, "text_hook"?: "…≤200 chars" }
      → { projectId, selected, selected_index }
      # 400 PERSONA_HOOK_TEXT_MISMATCH: on a persona project the burned text must be the persona's
      # spoken line — after normalisation, equal to it, its opening words, or an ordered subset

5. The identity-carrier rule

A talking or interview video must show one face in every scene, so POST /api/generate/video-start checks two things before charging the credit: the model can hold a face across scenes, and something carries that face. The carrier is resolved in this order — locked portrait → sheet frontal → the project’s attached start image → its first reference image → an identity frame the worker synthesizes once per project. A model flagged refusesRealFaces renders from text only, so it is refused for spoken formats outright, as is any model with no picture input; the Persona face column of the model table above shows the route per model. B-roll is never refused.

# 422 from POST /api/generate/video-start
{ "message": "<model> cannot hold Mara's face: it renders every scene from text, so each scene would show a different person. Pick … on the model step, or lock Mara's look on the Personas page …",
  "code": "PERSONA_IDENTITY_UNAVAILABLE",
  "reason": "model_cannot_hold_identity",   # or "no_identity_carrier"
  "modelLabel": "<model label>", "personaName": "Mara" }

6. AI disclosure on publish

A persona that is photoreal (or whose disclosure is always) is always disclosed. Assembly burns the footer “AI character — synthetic video” into the safe zone; POST /api/posting/create and POST /api/zernio/publish append “This video features an AI-generated character. #AIcharacter” to the description and force the platform label on — TikTok tiktokSettings.video_made_with_ai, YouTube containsSyntheticMedia, Instagram isAiGenerated. A request that sets them false is overridden. Faceless projects are untouched.

7. Measure it

GET /api/analytics/personas
# → { "personas": [{ key, personaId, name, niche, posts, scoredPosts, totalViews, medianViews,
#                    avgCompletion, totalShares, likeRate, topHookType, registers: { talking, interview, broll, faceless } }],
#     "total_posts": 42 }                       # one row per persona (zero posts included) + the "faceless" bucket

GET /api/analytics/posts?personaId=all|faceless|<persona id>      # 400 on anything else
# → { "posts": [{ postingId, projectId, platform, status, title, postUrl, persona: { id, name, niche } | null,
#                 hookType, register, metrics: { views, likes, comments, shares, engagementRate, completionRate } | null,
#                 metricsSyncStatus, metricsSyncedAt }], "personaId": "all" }
GET /api/analytics/history?personaId=<id>       # the activity log, filtered the same way

Error codes

StatuscodeMeaning and extra fields
402—Video quota exhausted for the plan window. Bare `{ message }`.
403PLAN_UPGRADE_REQUIREDFeature not on the plan. `capability`: socialPublishing | premiumVideoModels | personas; `upgradeUrl`; `modelLabel` for a paid-only model.
422PERSONA_IDENTITY_UNAVAILABLETalking / interview persona project cannot hold one face on this model. `reason`: model_cannot_hold_identity | no_identity_carrier; `modelLabel`; `personaName`.
422PERSONA_GUARDRAIL_BLOCKEDPersona script gives first-person expert advice on a sensitive topic and still failed after one rewrite. `reasons`, `flagged_sentences`, `policy_quote`, `scenes`.
400PERSONA_HOOK_TEXT_MISMATCHOn-screen hook is not the persona’s spoken line. `text_hook`, `spoken_hook`.
400VOICE_CONSENT_REQUIREDVoice reference upload without `consent=true`.
402 / 409—Persona look: image budget reached (402); portrait already locked (409).
400—Validation error (`{ message, details }`), TikTok consent missing, or the chosen model cannot produce the project’s aspect ratio.
401—Missing, expired or revoked bearer credential.
404—Not found — including another account’s project or persona (never 403).

Agent card (A2A)

For agent-to-agent discovery, ClipTaps publishes an A2A agent card describing its skills, its bearer security scheme, the public endpoints, the plan and model facts and the error codes above. It is discovery-only: the card carries status: "planned" and no JSON-RPC url, because the A2A execution endpoint is not live yet — drive ClipTaps through the REST API it describes.

GET https://cliptaps.com/.well-known/agent-card.json   # alias: /.well-known/agent.json
GET https://cliptaps.com/llms.txt                        # plain-text summary for LLM crawlers

View the live agent card →

Roadmap

  • •MCP server: planned. The @cliptaps/mcp-server package is not published to npm — npx @cliptaps/mcp-server does not work. Its source lives in the ClipTaps repository (orchestration/mcp, a private package, stdio transport, dev-grade) and can be run from a checkout; the tools it exposes today are listed below.
  • •A2A execution endpoint — the JSON-RPC url the agent card will point at once tasks can be delegated directly.
  • •Per-key rate limits & scopes — per-tier throttling and scoped keys (API keys themselves are available now under Settings → API Keys).
  • •Agent action tools — create_project, start_generation, publish_to_social as MCP tools (the REST endpoints behind them are live).
  • •Delegated purchases — real purchase_plan via scoped mandates for agent-purchasable tiers.
  • •OAuth 2.1 — scoped, short-lived tokens with audience validation.

Running the in-repo MCP server from a checkout (not an install):

cd content_factory/orchestration/mcp && npm install && npm run build
CLIPTAPS_API_BASE=https://cliptaps.com/api \
CLIPTAPS_API_KEY=ct_live_… \
node dist/index.js        # stdio transport; register this command with your MCP client
ToolBacked by
list_plansGET /api/payments/plans — tariff catalogue (public)
get_subscription_statusGET /api/payments/status — entitled plan, credits, reset, capability flags
list_projectsGET /api/projects — the user’s projects with status, thumbnail, finalVideoUrl, per-scene status
get_generation_statusGET /api/generate/video-status/{projectId} — project + per-scene status
get_analyticsGET /api/analytics/dashboard — videos created, minutes, posts per platform
create_checkoutPOST /api/payments/checkout — Stripe Checkout URL for a human to subscribe
purchase_planDelegated agent purchase — stub, returns guidance until the mandate flow ships

Changelog

  • 2026-09-25A partial PATCH /api/projects/{id}/settings no longer resets the video provider to Veo — request-body defaults were removed, so send only the keys you want changed.
  • 2026-09-25One identity carrier per persona video: the locked portrait, the sheet, the project’s attached picture or one synthesized frame holds the face across every scene; talking / interview projects that cannot are refused with 422 PERSONA_IDENTITY_UNAVAILABLE before the credit is charged.
  • 2026-09-24Persona follow-ups on the speaking-model and guardrail paths; brainstorm retries a truncated idea instead of returning a placeholder.
  • 2026-09-23AI personas (paid plans): persona profiles, locked look (portrait, angle sheet, wardrobe, locations), voice clone with consent or a stock voice, talking / interview / b-roll formats, series formats and proven-demand reference collections.
  • 2026-09-23Persona analytics: GET /api/analytics/personas and GET /api/analytics/posts?personaId= report views, completion, shares, like rate, top hook type and register per persona.
  • 2026-09-23AI disclosure: every photoreal persona video carries a burned footer, a description line and the platform AI-content label on publish; a client cannot switch them off.
  • 2026-09-23Sensitive-script guardrail: persona scripts that give first-person expert advice on health, finance, legal or political topics are rewritten to third person or refused with 422 PERSONA_GUARDRAIL_BLOCKED; the burned hook must be the persona’s own line (400 PERSONA_HOOK_TEXT_MISMATCH).
  • 2026-09-23Kling Avatar — a paid-only talking-head model that speaks the narration itself; word-level karaoke captions for cloned and Kokoro voices.
  • 2026-09-23Kling 2.6, Kling 3.0 and Seedance 2.5 as paid-only video models; every plan refusal now carries the machine-readable code PLAN_UPGRADE_REQUIRED.
  • 2026-09-19Social publishing (POST /api/posting/create, /api/social/*, /api/zernio/*) restricted to paid plans.
Questions? We’re happy to help integrate.Contact us

See also: FAQ · Use cases · Pricing · OpenAPI (Swagger)