Zolt AI
API Documentation← Back to site

REST API

The automation-friendly surface: generate and revise on-brand content, render images/video, publish to X, schedule posts, and score results. Every endpoint below is POST except where noted, requires an X-API-Key header (see Authentication), and resolves your business by name.

What each call costs

API calls spend Zolt Credits from the same pool as the app — there is no separate API meter. A call is charged when it produces a generation; calls that only move data around (scheduling, publishing, uploading, reading results) are free, and so are the automatic quality check and the video status poll.

The table is generated from the price map the meter itself uses, so it cannot drift from what you are actually billed.

EndpointCreditsNotes
/api/v1/generate30Per call, whatever the asset type.
/api/v1/idea30One idea/plan step.
/api/v1/revise30Each revision is its own generation.
/api/v1/render-image30Image generation.
/api/v1/render-video15Starts the render.
/api/v1/render-video-status0Free — polling a render you already paid for.
/api/v1/score0Free — Zolt's own quality check, not your work.
/api/v1/publish-x0Free — publishing, not generating.
/api/v1/schedule0Free — scheduling only.
/api/v1/upload-media0Free — upload only.
/api/v1/generations0Free — reads stored results.

Note that /api/v1/generate is priced per call, not per asset type — an API-generated carousel or presentation costs the same as a social post, where the in-app equivalents are priced higher.

Running out of credits returns 402. Usage, including which business spent what, is on your Credit usage page.

Resolving business and board

Most endpoints take a business name and an optional board name. Names are matched case-insensitively against a normalized slug (lowercased, non-letters/digits collapsed to -), so "Acme Co." matches a business named acme-co. If boardis omitted, your business's default board is used (or its first board, if none is marked default).

If your API key is unscoped, business must still name a business your key belongs to — a business that resolves to a different business than your key's own returns 403. A business/board that doesn't resolve at all returns 404.

Asset types and fields

asset_type accepts video, image, carousel, presentation, or landing_page — matched by prefix and case-insensitively, so a value like "Video – Short Form" from a spreadsheet dropdown still resolves to video. Each asset type generates a fixed set of string fields:

asset_typeFields
videohook, script_copy, caption_tiktok, caption_ig, caption_fb, yt_title, yt_description, yt_tags
imagehook, image_generation_prompt, caption_ig, caption_fb, caption_x (≤280), caption_linkedin, pinterest_title (≤100), pinterest_description (≤500), pinterest_board
carouselhook, slide_prompts (5 newline-separated image prompts), caption_ig, caption_fb, caption_x (≤280), caption_linkedin
presentationhook, slide_content (newline-separated "Title — body" slides), caption_linkedin, caption_x (≤280)
landing_pageheadline, subheadline, body_sections, cta_copy, seo_meta_description (≤160)

Idea

POST
/api/v1/idea
Propose a content idea grounded in this board's connected sources.
FieldTypeRequiredDescription
businessstringoptionalBusiness name to resolve.
boardstringoptionalBoard name; defaults to the business's default board.
business_contextstringoptionalExtra freeform context to steer the idea.
avoid_recentbooleanoptionalSkip ideas similar to recently generated ones on this board.
modelstringoptionalOverride the default model.

Response:

{
  "generationId": "cldx...",
  "idea": "string",
  "angle": "string",
  "why_now": "string",
  "source_tiles": ["string", "..."]
}
502 Bad Gateway{ "error": "Idea generation failed." }

Generate

POST
/api/v1/generate
Generate a new piece of content for one asset type.
FieldTypeRequiredDescription
businessstringrequiredBusiness name to resolve.
boardstringoptionalBoard name; defaults to the business's default board.
asset_typestringrequiredvideo | image | carousel | presentation | landing_page
ideastringrequiredThe idea or brief to generate from.
hookstringoptionalOptional hook to build around; the model invents one if omitted.
durationnumberoptionalTarget seconds for video scripts (~2.5 words/sec, ±15%).
variantsnumberoptional1–3. Generates multiple takes.
auto_select_bestbooleanoptionalWith variants > 1, score all variants and return only the winner.
modelstringoptionalOverride the default model.

Response shape depends on variants/auto_select_best. Default (single variant) — the asset type's fields are spread at the top level:

{
  "generationId": "cldx...",
  "provider": "anthropic",
  "model": "claude-sonnet-5",
  "hook": "string",
  "script_copy": "string",
  "caption_tiktok": "string",
  "caption_ig": "string",
  "caption_fb": "string",
  "yt_title": "string",
  "yt_description": "string",
  "yt_tags": "string"
}

variants > 1 without auto_select_best:

{
  "generationId": "cldx...",
  "provider": "anthropic",
  "model": "claude-sonnet-5",
  "variants": [ { /* fields */ }, { /* fields */ }, { /* fields */ } ]
}

variants > 1 with auto_select_best: true — every variant is scored, only the winner is returned, with its score merged in:

{
  "generationId": "cldx...",
  "provider": "anthropic",
  "model": "claude-sonnet-5",
  /* ...winning variant's fields... */
  "score": 82,
  "verdict": "string",
  "top_problem": "string",
  "fixes": "string",
  "improved_hook": "string",
  "variantsGenerated": 3,
  "variantsScored": 3
}
400 Bad RequestRequired fields: business, asset_type ('video'|'image'), idea.
502 Bad Gateway{ "error": "..." }

Revise

POST
/api/v1/revise
Rewrite a previous generation's fields given a score and feedback.
FieldTypeRequiredDescription
businessstringrequiredBusiness name to resolve.
boardstringoptionalBoard name; defaults to the business's default board.
asset_typestringrequiredvideo | image | carousel | presentation | landing_page
original_fieldsobjectoptionalThe original content fields, as an object — or flatten those same fields at the top level instead.
scorenumberrequiredNumeric score for the original (numeric strings accepted).
top_problemstringrequiredThe single biggest issue to fix.
fixesstring[] | stringrequiredArray of fixes, or a newline-separated string.
improved_hookstringoptionalA suggested replacement hook.
durationnumberoptionalTarget seconds, same as Generate.
modelstringoptionalOverride the default model.
revision_countnumberoptionalIf given, the response includes newRevisionCount = revision_count + 1.

Response — the revised fields, spread at the top level, same shape as Generate's single-variant response:

{ "generationId": "cldx...", "provider": "anthropic", "model": "claude-sonnet-5", /* ...revised fields... */ }
400 Bad RequestRequired fields: business, asset_type, score, top_problem, fixes, and either original_fields or the flattened fields.
502 Bad Gateway{ "error": "Revision failed." }

Score

POST
/api/v1/score
Score an existing piece of content against this board's brand/audience.
FieldTypeRequiredDescription
businessstringrequiredBusiness name to resolve.
boardstringoptionalBoard name; defaults to the business's default board.
asset_typestringrequiredvideo | image | carousel | presentation | landing_page
(fields)stringrequiredThe asset type's own fields (hook, script_copy, etc.), flattened at the top level — at least one must be present.
{
  "generationId": "cldx...",
  "score": 74,
  "verdict": "string",
  "top_problem": "string",
  "fixes": "string",
  "improved_hook": "string"
}
400 Bad RequestRequired fields: business, asset_type, plus the content fields.
400 Bad RequestNo recognized content fields found in the body for this asset_type.
502 Bad Gateway{ "error": "Scoring failed." }

Score a stored generation

POST
/api/v1/generations/{id}/score
Attach a score directly to an existing generation by id.
FieldTypeRequiredDescription
scorenumberrequiredThe score to store.
{ "ok": true }
400 Bad RequestRequired field: score (number).
404 Not Found{ "error": "Generation not found." }
403 Forbidden{ "error": "This API key is not authorized for this generation." }

Publish to X

POST
/api/v1/publish-x
Post an image + caption directly to X (Twitter).
FieldTypeRequiredDescription
asset_urlstringrequiredA publicly reachable image URL.
caption_xstringrequiredTweet text. Silently truncated to 277 chars + "…" if over 280.
{ "x_post_id": "string", "x_post_url": "string" }
400 Bad RequestRequired fields: asset_url, caption_x.
502 Bad Gateway{ "error": "X publish failed." }

Render image

POST
/api/v1/render-image
Render a real image from a prompt, in one or more aspect-ratio formats.
FieldTypeRequiredDescription
businessstringrequiredBusiness name to resolve.
boardstringoptionalBoard name; defaults to the business's default board.
image_generation_promptstringrequiredThe image prompt.
formatsstring[] | stringrequiredSubset of 4x5 | 2x3 | 16x9. Comma-separated string also accepted.
{
  "generationId": "cldx...",
  "images": {
    "4x5": "https://app.getzolt.ai/api/uploads/....png",
    "2x3": "https://app.getzolt.ai/api/uploads/....png"
  }
}
400 Bad RequestRequired fields: business, image_generation_prompt, formats.
400 Bad RequestUnsupported format "9x16". Use 4x5, 2x3, or 16x9.
502 Bad Gateway{ "error": "Image render failed." }

Render video

POST
/api/v1/render-video
Start an AI avatar video render job from a script.
FieldTypeRequiredDescription
businessstringrequiredBusiness name to resolve.
boardstringoptionalBoard name; defaults to the business's default board.
scriptstringrequiredThe video script.
avatar_idnumberoptionalPresenter avatar id. Omit to auto-rotate through your roster.
avatar_typenumberoptionalAvatar type id.
voice_idstringoptionalVoice id override.
aspect_ratiostringoptionalportrait | landscape | square
{ "generationId": "cldx...", "job_id": "string", "status": "processing", "presenter": "string" }
400 Bad RequestRequired fields: business, script.
400 Bad RequestUnsupported aspect_ratio "wide". Use portrait, landscape, or square.
502 Bad Gateway{ "error": "Video render start failed." }

Render video status

POST
/api/v1/render-video-status
Poll a video render job started by Render video.
FieldTypeRequiredDescription
businessstringrequiredBusiness name to resolve.
boardstringoptionalBoard name; defaults to the business's default board.
job_idstringrequiredThe job_id from Render video's response.

One of three shapes, depending on job state:

{ "job_id": "string", "status": "processing" }
{ "job_id": "string", "status": "failed" }
{ "job_id": "string", "status": "completed", "video_url": "string" }
400 Bad RequestRequired fields: business, job_id.
502 Bad Gateway{ "error": "Video status check failed." }

Schedule

POST
/api/v1/schedule
Get best-practice posting times for a given date, per platform.
FieldTypeRequiredDescription
datestringrequiredYYYY-MM-DD (or a full ISO timestamp — only the date part is used).
(fields)stringoptionalAny of the platform caption fields (caption_ig, caption_fb, etc.) — only platforms with a matching field present get a scheduled time back.

Only keys for platforms whose field was present in the request are returned, e.g.:

{
  "tiktok_scheduled_at": "2026-08-03T14:00:00.000Z",
  "instagram_scheduled_at": "2026-08-03T19:00:00.000Z"
}

All times are UTC and reflect fixed best-practice defaults per platform, not audience-specific analytics.

400 Bad RequestRequired fields: date, plus the generated content fields.

Upload media

POST
/api/v1/upload-media
Host a file and get back a public URL — useful for feeding asset_url into Publish to X.

Accepts either multipart/form-data with a file field, or JSON:

FieldTypeRequiredDescription
image_base64stringrequiredBase64-encoded file contents (JSON mode only).
mime_typestringoptionale.g. image/png, image/jpeg, audio/mpeg (JSON mode only).
{ "url": "https://app.getzolt.ai/api/uploads/abc123.png" }
400 Bad RequestRequired field: file (multipart).
400 Bad RequestRequired field: image_base64 (or send multipart/form-data with 'file').