AdflowDocs

Adflow is an AI-native ad creative builder. Your agent connects via MCP (Model Context Protocol) to create design systems, build multi-scene projects, and generate AI imagery — all from natural language. This page documents every endpoint, schema, and skill your agent needs, plus a reference for the editor's creative features — vector expressions, guided physics, and the full capability map.

Quickstart MCP Setup MCP Endpoints Design System Schema Project Schema Scene Schema Format Specs Vector Expressions Animate & Guide Physics Editor Feature Map Agent Skills Templates API Fallback Critical Rules

Quickstart

The typical agent workflow in five steps:

1list_design_systems — check if the client already has a design system
2create_design_system or update_design_system — set up the brand (all fields required)
3create_project — create scenes with headlines, copy, and CTA text
4get_projectupdate_project — patch scene-level styling fields (textColor, ctaBgColor, etc.)
5generate_image — create AI backgrounds, then attach to scenes via update

MCP Server Setup

Add the Adflow MCP server to your agent's configuration. You'll need an API key from the Admin panel.

Hosted server (recommended — zero-install)

Nothing to clone, no npm, no npx. Point your agent at the hosted endpoint and pass your API key as a header. This is the easiest way to connect — the server runs on adflow.design.

claude mcp add --transport http adflow https://adflow.design/api/mcp \
  --header "X-API-Key: adk_your_api_key_here"

Or as JSON (Claude Desktop / any MCP client that speaks the Streamable HTTP transport):

{
  "mcpServers": {
    "adflow": {
      "type": "http",
      "url": "https://adflow.design/api/mcp",
      "headers": {
        "X-API-Key": "adk_your_api_key_here"
      }
    }
  }
}

Your key authenticates every request and attributes usage/credits to your account — a missing or invalid key returns 401. The hosted server exposes the exact same tools as the local methods below.

Local server (offline / dev only)

Only needed if you're running offline or hacking on the server itself — most people should use the hosted URL above. You'll need the mcp-server/ folder (from the repo or a shared tarball) and Node 18+.

First install its dependencies:

cd mcp-server && npm install

Then point your agent at the server's absolute path and pass your key via the environment. ADFLOW_API_KEY is required; ADFLOW_URL is optional (defaults to https://adflow.design).

{
  "mcpServers": {
    "adflow": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-server/index.js"],
      "env": {
        "ADFLOW_API_KEY": "adk_your_api_key_here",
        "ADFLOW_URL": "https://adflow.design"
      }
    }
  }
}

Replace /absolute/path/to/mcp-server/index.js with the real path on your machine (e.g. /Users/you/adflow/mcp-server/index.js). A published npx adflow-mcp package is coming soon — it is not on npm yet, so don't rely on it today.

Authentication reference

Every request is authenticated with your Adflow API key. Create keys in Admin → API Keys. How you supply the key depends on the method:

X-API-Key headerHosted server (and any direct REST call). The key travels as an HTTP header on each request.
ADFLOW_API_KEY envLocal stdio server — required. The mcp-server/index.js process reads it from the environment.
ADFLOW_URL envLocal stdio server — optional. Overrides the API base URL (defaults to https://adflow.design).
Authorization: BearerSupabase JWT token for browser sessions.

MCP Endpoints

27 tools available via the MCP server. All return JSON. You call these by tool name (not by HTTP verb) — the coloured badge below each tool indicates the HTTP method of the REST endpoint it wraps, for reference when using the Direct API Fallback.

GET list_design_systems

List all design systems you have access to.

No parameters. Returns an array of full design system objects.

POST create_design_system

Create a new design system. All scenes inherit colours, fonts, logo, and layout from here.
ParamTypeDescription
namestringrequiredDisplay name (e.g. "Acme Corp")
primaryColorhexrequiredPrimary brand colour
secondaryColorhexoptionalSecondary brand colour
darkColorhexoptionalDark colour (default #111111)
lightColorhexoptionalLight colour (default #ffffff)
fontHeadlinestringoptionalHeadline font stack
fontBodystringoptionalBody font stack
fontImportURLoptionalGoogle Fonts import URL
fontH1fontCtastringoptionalPer-element font overrides
h1WeightctaWeightstringoptionalFont weights (e.g. "800")
h1TransformctaTransformstringoptional"none" or "uppercase"
h1LetterSpacingctaLetterSpacingstringoptionalCSS letter-spacing
ctaRadiusstringoptionalCTA border-radius multiplier
logoSvgURLoptionalLogo SVG URL
logoPositionstringoptional9-grid position (e.g. "top-left")
logoScalenumberoptionalLogo size (100 = default)
contentPositionstringoptional"top", "middle", or "bottom"
contentAlignstringoptional"left" or "center"
ctaAlignstringoptional"left" or "center"
disclaimerstringoptionalLegal disclaimer text
globalGenPromptstringoptionalDefault AI prompt prefix

PATCH update_design_system

Update any properties on an existing design system.
ParamTypeDescription
idstringrequiredDesign system slug (e.g. "acme-corp")
dataobjectrequiredKey-value pairs to update (see full schema below)

GET list_projects

List all projects with metadata.

No parameters. Returns id, filename, name, designSystemId, sceneCount, savedAt.

GET get_project

Get full project data including all scenes.
ParamTypeDescription
idstringrequiredProject UUID

POST create_project

Create a new project with scenes. Colours, layout, and logo position are inherited from the design system automatically.
ParamTypeDescription
namestringrequiredProject name (e.g. "June Campaign")
designSystemstringrequiredDesign system slug
formatenumoptionalActive format: square, portrait, vertical, landscape, wide
scenesarrayrequiredArray of scene objects (see below)

Scene object

FieldTypeDescription
h1stringrequiredHeadline text
h2stringoptionalSupporting text
h3stringoptionalThird line of text
ctastringoptionalCTA button text
bgTypeenumoptionalcolour, gradient, or image
bgColorhexoptionalBackground colour (default: DS Dark)
bgImageURLoptionalBackground image URL
bgGradientFromhexoptionalGradient start colour
bgGradientTohexoptionalGradient end colour
bgGradientAnglenumberoptionalGradient angle degrees (default 180)
textColorhexoptionalText colour (default: DS Light)
ctaBgColorhexoptionalCTA bg (default: DS Primary)
ctaTextColorhexoptionalCTA text (default: DS Light)
contentPositionenumoptionaltop, middle, bottom
contentAlignenumoptionalleft or center
logoPositionstringoptional9-grid position (e.g. "top-left")
overlayTypeenumoptionalnone, fill, gradient
overlayColourhexoptionalOverlay colour
overlayOpacitynumberoptionalOverlay opacity 0–1
notesstringoptionalDescription / AI image prompt
durationnumberoptionalSeconds (default 3)
audioUrlURLoptionalAudio URL (use upload_audio)
audioFileNamestringoptionalAudio filename for display
audioPlayModeenumoptionalloop, once, fade
audioVolumenumberoptionalVolume 0–100 (default 100)
audioOffsetnumberoptionalStart offset in ms
audioCropStartnumberoptionalTrim start in ms
audioCropEndnumberoptionalTrim end in ms
audioFadeOutnumberoptionalFade out duration in ms

PUT update_project

Update an existing project. Fetch the project first, modify it, then pass the full object back.
ParamTypeDescription
idstringrequiredProject UUID
filenamestringrequiredProject filename
projectobjectrequiredFull project object

DELETE delete_project

Permanently delete a project.
ParamTypeDescription
idstringrequiredProject UUID

POST generate_image

Generate an AI image for use as a scene background.
ParamTypeDescription
promptstringrequiredImage generation prompt
filenamestringoptionalOutput filename
modelenumoptionalAI model (see table below)
widthnumberoptionalWidth in px (default 2160)
heightnumberoptionalHeight in px (default 2160)

Available models

gpt-image-1ChatGPT Image v2 (default, best quality)
flux-devFlux Dev (fast)
flux-proFlux Pro
imagen4Google Imagen 4
imagen4-fastGoogle Imagen 4 Fast
nano-banana-proNano Banana Pro
ideogram-v3Ideogram v3
recraft-v3Recraft V3
recraft-v3-transparentRecraft V3 (transparent background PNG)

GET list_generations

Browse generated images, optionally scoped to a design system (client). Non-admins always see only images they have access to.
ParamTypeDescription
designSystemIdstringrequiredDesign system slug (e.g. "beau-brummell")

POST upload_image

Upload an image from a URL to Blob storage for use as a scene background.
ParamTypeDescription
urlstringrequiredPublic URL of the image
filenamestringoptionalFilename for the stored image

POST outpaint_image

Expand an image's edges using AI outpainting. The source image is downloaded and sent as image data. Default model is fal-ai/bria/expand (fal-ai/flux-2-pro/outpaint also available). At least one expand_* value must be greater than 0.
ParamTypeDescription
image_urlstringrequiredPublic URL of the source image (downloaded and sent as image data)
expand_topnumberoptionalPixels to expand at top
expand_bottomnumberoptionalPixels to expand at bottom
expand_leftnumberoptionalPixels to expand at left
expand_rightnumberoptionalPixels to expand at right
promptstringoptionalHint for what to paint into the expanded area
modelenumoptionalfal-ai/bria/expand (default) or fal-ai/flux-2-pro/outpaint
filenamestringoptionalOutput filename (default: extended.png)
design_system_idstringoptionalDesign system slug to scope storage to a client

POST generate_vector

Generate a clean editable SVG icon/shape via fal.ai (Recraft V4.1).
ParamTypeDescription
promptstringrequiredDescription of the SVG to generate
modelstringoptionalfal.ai model (default: recraft-v4.1)
filenamestringoptionalOutput filename

POST duplicate_project

Duplicate a project (creates a copy with "(Copy)" suffix).
ParamTypeDescription
idstringrequiredProject UUID to duplicate

POST upload_audio

Upload an audio file from a URL for use as scene audio. Returns a Blob URL.
ParamTypeDescription
urlstringrequiredPublic URL of audio file (mp3, wav, ogg, aac, m4a)
filenamestringoptionalFilename for stored audio

DELETE delete_design_system

Delete a design system (cannot delete "default").
ParamTypeDescription
idstringrequiredDesign system slug to delete

POST render_video

Start a server-side MP4 render of a composition. Renders the supplied HTML via AWS and returns a renderId + executionArn — poll get_render_progress with the executionArn until status is SUCCEEDED to get the video URL.
ParamTypeDescription
htmlstringrequiredFull composition HTML to render
widthnumberrequiredOutput width in pixels
heightnumberrequiredOutput height in pixels
fpsnumberoptionalFrames per second (default 30)
qualitystringoptionalRender quality (default "high")
formatstringoptionalOutput format (default "mp4")

GET get_render_progress

Poll the progress of a video render started with render_video. When status is SUCCEEDED, videoUrl holds a signed download URL (valid ~1 hour).
ParamTypeDescription
executionArnstringrequiredexecutionArn returned by render_video
filenamestringoptionalDownload filename for the signed URL (default: output.mp4)

POST render_screenshot

Render composition HTML to a PNG and return the image. Max dimension 4096px. To persist it to a public URL, pass a data URI to save_render.
ParamTypeDescription
htmlstringrequiredFull composition HTML to screenshot
widthnumberrequiredOutput width in pixels (max 4096)
heightnumberrequiredOutput height in pixels (max 4096)

POST save_render

Save a rendered image (base64 data URI) to storage and return a public URL. Use to persist a PNG produced by render_screenshot or an in-app export.
ParamTypeDescription
dataUristringrequiredBase64 data URI, e.g. "data:image/png;base64,..."
filenamestringoptionalOutput filename (default: render.png)

POST inpaint_image

AI-fill a masked region of an image from a prompt (generative fill). Provide the source image URL and a mask URL where white marks the area to regenerate.
ParamTypeDescription
image_urlstringrequiredPublic URL of the source image
mask_urlstringrequiredPublic URL of the mask image (white = region to fill)
promptstringoptionalWhat to paint into the masked region
modelstringoptionalInpaint model (default: fal-ai/bria/genfill)
filenamestringoptionalOutput filename (default: retouched.png)
design_system_idstringoptionalDesign system slug to scope storage to a client

POST retouch_image

Retouch an image by erasing/removing content within a masked region (object removal). Use mode "inpaint" to regenerate the region from a prompt instead.
ParamTypeDescription
image_urlstringrequiredPublic URL of the source image
mask_urlstringrequiredPublic URL of the mask image (white = region to retouch)
modeenumoptionalerase removes content (default); inpaint fills from the prompt
promptstringoptionalFill prompt (used when mode is "inpaint")
modelstringoptionalOverride model (default depends on mode)
filenamestringoptionalOutput filename (default: retouched.png)
design_system_idstringoptionalDesign system slug to scope storage to a client

POST edit_image

Edit an image with a natural-language instruction (e.g. "remove the background", "make it night-time"). Some models (e.g. ideogram-rembg) need no prompt.
ParamTypeDescription
image_urlstringrequiredPublic URL of the source image
promptstringoptionalEdit instruction (required for most models)
modelenumoptionalEdit model (see table below)
filenamestringoptionalOutput filename (default: edited.png)
design_system_idstringoptionalDesign system slug to scope storage to a client

Available models

nano-banana-proNano Banana Pro (default)
nano-banana-2Nano Banana 2
nano-bananaNano Banana
flux-kontextFlux Kontext
qwen-editQwen Edit
seedream-editSeedream Edit
ideogram-rembgIdeogram background removal (no prompt needed)

POST enhance_image

Upscale / enhance an image's resolution and detail.
ParamTypeDescription
image_urlstringrequiredPublic URL of the source image
modelenumoptionalaura-sr (default), creative-upscaler, or clarity-upscaler
strengthnumberoptionalCreative strength 0–1 (0 = faithful upscale, higher invents more detail; default 0)
filenamestringoptionalOutput filename (default: enhanced.png)
design_system_idstringoptionalDesign system slug to scope storage to a client

POST crop_image

Crop an image by percentage from each edge. At least one crop value must be greater than 0.
ParamTypeDescription
image_urlstringrequiredPublic URL of the source image
crop_topnumberoptionalPercent to crop from the top (0–100)
crop_bottomnumberoptionalPercent to crop from the bottom (0–100)
crop_leftnumberoptionalPercent to crop from the left (0–100)
crop_rightnumberoptionalPercent to crop from the right (0–100)
filenamestringoptionalOutput filename (default: cropped.png)
design_system_idstringoptionalDesign system slug to scope storage to a client

POST delete_generation

Delete a generated or uploaded image from storage by its Blob URL. Only image-gen/ and uploads/ URLs are deletable, and you must own the generation (or be an admin).
ParamTypeDescription
urlstringrequiredThe Blob URL of the image to delete

POST capture_url

Capture a screenshot of a public web page and return the PNG. Private/local hosts are blocked.
ParamTypeDescription
urlstringrequiredPublic http(s) URL to capture
widthnumberoptionalViewport width in px (1–4096, default 1280)
heightnumberoptionalViewport height in px (1–4096, default 720)
fullPagebooleanoptionalCapture the full scrollable page height (default false)

Design System Schema

The complete field reference for design systems. All fields are required for correct rendering.

FieldTypeDescription
idstringURL-safe slug (e.g. beau-brummell)
namestringDisplay name
clientNamestringClient display name
colorVarsarray4 named colour swatches: [{hex, name}] — Primary, Secondary, Dark, Light
fontHeadlinestringHeadline font stack (e.g. "Montserrat, sans-serif")
fontBodystringBody font stack (e.g. "Inter, sans-serif")
fontImportURLGoogle Fonts import URL
customFontsarrayUploaded fonts: [{name, url, fileName}]
fontH1fontH3stringPer-element font overrides (blank = inherit)
fontCtastringCTA button font (blank = inherit fontBody)
h1WeightctaWeightstringCSS font-weight values
h1TransformctaTransformstringnone or uppercase
h1LetterSpacingctaLetterSpacingstringCSS letter-spacing
ctaRadiusstringButton radius multiplier (e.g. 0.5)
ctaAlignstringleft or center
contentAlignstringleft or center
contentPositionstringtop, middle, or bottom
logoPositionstring9-grid position (e.g. top-left, bottom-center)
logoScalenumberLogo size percentage (100 = default)
logoSvgstringLogo SVG blob URL
logoFileNamestringLogo filename
disclaimerstringLegal disclaimer text (blank if none)
disclaimerAlignstringleft or center
globalGenPromptstringDefault AI image generation prompt prefix
globalLocksobjectLocked fields applied across all scenes

Project Schema

The full project object structure returned by get_project and expected by update_project.

{
  "version": 2,
  "designSystemId": "client-slug",
  "preset": { /* full design system snapshot */ },
  "activeFormat": "square",
  "activeSceneId": "scene-uuid",
  "totalDuration": 12,
  "scenes": [ /* array of scene objects */ ]
}
FieldTypeDescription
versionnumberSchema version (always 2)
designSystemIdstringLinked design system slug
presetobjectFull design system embedded in project
activeFormatenumsquare, portrait, vertical, landscape, wide
activeSceneIdstringCurrently selected scene
totalDurationnumberSum of all scene durations
scenesarrayOrdered array of scene objects

Scene Schema

A scene may contain any of these fields. Almost all are optional — missing fields fall back to design-system and editor defaults, so a scene only needs the fields it actually sets.

FieldTypeDescription
Content
idstringUnique scene identifier
namestringScene display name
h1stringHeadline text
h2stringSupporting text
h3stringThird line
ctaTextstringCTA button label
disclaimerOverridestringPer-scene disclaimer (blank = use preset)
durationnumberScene duration in seconds
Layout
logoPositionstring9-grid logo placement
logoScalenumberPer-scene logo scale (100 = default)
contentPositionstringtop, middle, bottom
contentAlignstringleft or center
ctaAlignstringleft or center
paddingPerFormatobjectPer-format padding: {square: {left, right}, ...}
layoutPerFormatobjectPer-format layout overrides (position/align/scale), inherited leftward across formats
Layout Template & Grid
layoutTemplatestringApplied layout-template id — one of classic, centered, hero, topLead, bottomBar, footer, logoBottom, cornerMark, minimal, grid, cinematic (Centered logo), none, logoBottomLeft, logoBottomRight. Sets the slot fields above; a template is a starting point, not a constraint.
gridEnabledbooleanOpt-in free-placement mode. When true, slot typography is suppressed and freeEls is rendered instead.
freeElsarrayFreely placed elements: [{id, role, text, cx, cy, w, h, angle, rotated}]cx/cy/w/h are fractions of the frame (frame-fractional poses), so they hold across formats.
gridColsnumberEditor snap-lattice columns for placing/dragging grid elements
gridRowsnumberEditor snap-lattice rows
gridPerFormatobjectPer-aspect-ratio grid: {square: {freeEls, gridCols, gridRows}, vertical: {...}} — swapped in/out on format switch (like bgPerFormat)
Colours
bgColorhexSolid background colour
textColorhexText colour
ctaBgColorhexCTA background
ctaTextColorhexCTA text
themeModestring"", dark, light, brand
colorOverridesobjectPer-element overrides: {logo: "light", h1: "brand"}
textOpacitynumberText layer opacity (0–1)
Scale
h1ScalectaScalenumberElement size percentage (default 100)
disclaimerScalenumberDisclaimer size percentage (default 100)
Background
bgTypeenumcolour, gradient, image, video, folder
bgImageURLBackground image URL
bgImageFileNamestringBackground image filename
bgVideoURLBackground video URL
bgVideoFileNamestringBackground video filename
bgVideoTrimStartnumberVideo trim start (0–1 normalised)
bgVideoTrimEndnumberVideo trim end (0–1 normalised)
bgGradientFromhexGradient start colour
bgGradientTohexGradient end colour
bgGradientAnglenumberGradient angle in degrees
bgScalenumberBackground zoom (100 = default)
bgFlipHbooleanFlip background horizontally
bgFlipVbooleanFlip background vertically
bgCropTopbgCropRightnumberBackground crop pixels per side
focusPinobject{x: 50, y: 50} — image focal point
focusPinsobjectPer-format focus: {square: {x,y}, ...}
bgPerFormatobjectPer-format bg overrides: {square: {bgType, bgColor, ...}}
bgFolderIndexnumberActive index in folder bg mode
Overlay
overlayTypeenumnone, fill, gradient
overlayColourhexOverlay colour
overlayOpacitynumberOverlay opacity (0–1)
overlayGradientDirstringtop, bottom, both, radial
overlayGradientColourhexGradient overlay colour
SVG Layers & Glyphs
svgLayersarray[{id, svg, fileName, scale, offsetX, offsetY, colorOv}]
glyphsarray[{id, char, font, x, y, scale, rotation, colorOv}]
maskLayerIdstringId of the canvas layer used as a mask for the layer stack (layer-as-mask)
layers[].contrastnumberLayer-global contrast (100 = default) — applied to the whole layer, not keyframed
layers[].saturationnumberLayer-global saturation (100 = default) — applied to the whole layer, not keyframed
AI Generation
genAiPromptstringAI image prompt for this scene
genAiModelstringAI model key
genAiCamerastringCamera angle (e.g. "eye-level")
genAiLensstringLens type (e.g. "standard")
genAiShotSizestringShot size (e.g. "medium-shot")
genAiLightingstringLighting style
genAiBackgroundstringBackground description
genAiStylestringVisual style (e.g. "photo-realistic")
genAiMoodstringMood/atmosphere
genAiImagesobjectPer-format generated images
bgImageHistoryobjectPer-format image history arrays
bgImageHistoryIndexobjectPer-format history index
Cinematic
cinematicbooleanEnable cinematic mode (hides text)
cinematicShowLogobooleanShow logo in cinematic mode
cinematicLogoScalenumber|nullLogo scale override for cinematic
cinematicLogoPositionstring|nullLogo position override for cinematic
Audio
audioUrlURLAudio file URL
audioFileNamestringAudio filename
audioOffsetnumberAudio offset in ms
audioPlayModestringloop, once, fade
audioVolumenumberVolume 0–100
audioCropStartaudioCropEndnumberAudio trim in ms
audioFadeOutnumberFade out duration in ms
Capture
captureEnabledbooleanEnable capture mode (overrides imaging)
captureUrlURLURL to capture
captureFilterstringCSS filter preset name
captureAnimModestringnone or animate
captureEasingstringEasing function name
captureKeyframesobjectPer-format keyframes
Advanced
htmlOverridestringCustom HTML content
useHtmlOverridebooleanEnable HTML override mode
notesstringInternal notes
animationNotesstringAnimation description
elementAnimationsobjectPer-format element animation data

Format Specifications

Font sizes are calculated by the renderer, not stored. The root unit determines all text sizes via multipliers.

FormatDimensionsRootH1 (3x)H2 (2x)H3 (1.25x)CTA (1x)
Square1080 × 108024px72px48px30px24px
Portrait1080 × 135028px84px56px35px28px
Vertical1080 × 192028px84px56px35px28px
Landscape1920 × 108022px66px44px28px22px
Wide1200 × 62822px66px44px28px22px

Per-scene scale overrides: h1Scale, h2Scale, h3Scale, ctaScale (percentage, default 100).

Vector Expressions

Vector text layers accept inline expression tokens wrapped in curly braces. Each token evaluates live against the scene clock, so a single text string can count a number, cycle through words, or print a stable random digit — with no keyframes. Tokens evaluate identically in preview and in server render, and a token persists across linked scenes (the linked clips share one timeline window, so a {0-100} ramps once across the whole chain rather than restarting at each cut).

Where to type them: Canvas mode → a Vector layer → the text field (placeholder Type text…). Any glyph/string entry in the layer's text stack accepts tokens. There is no MCP parameter — expressions live inside the text content itself.

TokenTypeWhat it does
{A-B}NumberTweens linearly from A to B over the layer's timeline window. {0-100} counts up; {100-3} counts down. A and B may be negative and/or decimal.
{w1, w2, …}WordsSplits the window into N equal segments and shows each word in turn. {monkey, cat} = 50 / 50; the last word holds to the end. Whitespace around commas is trimmed.
{random}RandomA single stable digit 0–9. It is seeded once (never flickers) and is identical in preview and every render chunk.

Number animation — decimals & direction

The decimal precision of the output matches the inputs: the result is shown to max(decimals of A, decimals of B) places. Both-integer inputs stay integer (rounded). Descending ranges (B < A) animate backwards.

You typeAt 0%At 50%At 100%
Score: {0-100}Score: 0Score: 50Score: 100
{100-3} left100 left52 left3 left
{0-1.5}x0.0x0.8x1.5x
{-20-20}°-20°20°

Examples

Raised {0-100}% this quarter      → counts 0 → 100 over the scene
Only {50-0} left!                    → counts down 50 → 0
{Save big, Move fast, Win}          → three words, ~33% each, last holds
Gate {random}, Seat {random}          → two independent stable digits
{0-100}% — {loading, almost, done}     → a counter and a word cycle in one line

Behaviour & gotchas

Multiple tokensEvery {…} in the string is evaluated. Each {random} gets its own stable digit (seeded by its position).
Plain text untouchedAnything outside braces is printed verbatim — $, %, punctuation, spaces all survive.
Unknown tokensA brace group that matches none of the three forms (e.g. {hello}) is left as the literal text {hello} — no error, no blank.
Nested bracesNot supported. {{0-100}} won't tween; keep tokens flat.
Number vs wordsA comma inside the braces makes it a word cycle; a single dash between two numbers makes it a tween. {0,1,2} is words; {0-2} is a number.
Start / endValues clamp at the window edges — a number holds its first value before the window and its last value after it; a word cycle holds the final word.

Verified against app.html — the token runtime vexprEvalCore (parse + interpolation) and the live updater __vectorExprRuntime.

Animate & Guide Physics

Animate Physics turns a Vector layer's glyphs / SVGs into a particle system driven by force fields — no keyframing required. It lives in Canvas mode in the Animate Physics panel that appears when a vector layer is selected. An untouched panel is a no-op (all forces 0); dial in forces, or hit Randomize for a coherent recipe.

Modes

Goo (Organic)A natural motion field — leaves in a breeze, a tornado, ripples, orbits. Forces act as continuous flow.
Inorganic (Geometry)Points snap onto an angular, symmetrical form (grid, star, hexagon…) scaled to the canvas aspect so it stays proportionate at any size. Forces still animate them.

Force dials & layout

All forces default to 0 (the inert pose) and run 0–100. Forces and the global modulators can be keyframed across start / mid / end; layout values (Count, Fill, Fan Out) are structural — read once so particle identity stays locked across a linked chain.

GroupControlsRangeDefault
Nature forceswind, turbulence, gravity, swirl, orbit, wave, float, spring0–1000
Layoutcount (how many)1–4001
Layoutfill (cluster → edge-to-edge → overshoot)0–50060
LayoutfanOut0–1000
Globalspeed0–100100
Globalintensity0–200100
Globalrandomness0–10050
Globalstagger0–10020

Randomize & per-value locks

Randomize rolls a coherent natural recipe (leaves-in-breeze, tornado, planetary orbits, water ripple, rain) and re-rolls the seed. Per-value locks (the amber padlock on each slider) pin a value so Randomize leaves it untouched — useful for fixing Count or a single force while you reroll everything else. A value can be both keyframed (green) and locked (amber) at once.

Guide Physics prompt

The Guide Physics field (e.g. "form a ring · make particles rotate 360 · pulse") biases the running simulation toward a shape and/or motion. It calls the AI animate endpoint in guide mode and returns a { formation, modifier } spec — a formation settles particles into a shape, a modifier adds per-particle motion. Physics still runs underneath the guide, and clearing the prompt returns to pure physics. The guide is baked so preview matches export.

Form Sources flag-gated · beta

Form Sources let you supply the formation from your own asset instead of a text prompt — particles assemble into the shape. It is off by default; live customers see no change. Enable it without a deploy (sticky, then reload):

# URL param (sticky)
https://adflow.design/app?formSources=on      # disable: ?formSources=off

# or localStorage
localStorage.setItem('adflow_form_sources','on')
SourceYou supplyHow particles assemblePoints (default)
ImageDrop a PNG / JPG / WebP / GIFSampled into dots — silhouette (filled) or edge (outline) via a blue-noise spread, deterministic per seed.12–400 (160)
SVGDrop an .svgPoints distributed along the combined path length (a path trace). Optional draw-on reveals the path as it plays.12–400 (80)
DataPaste numbers (e.g. 3, 7, 4, 9, 6)Plotted as a chart polyline (x = index, larger values sit higher). Optional draw-on.12–400 (64)

A→B morph across linked scenes: each member of a linked physics chain can hold its own formation, and particles melt from shape A in one scene to shape B in the next across the cut (count is locked across the chain so particle i maps consistently).

Other flag-gated creative features. Gradient FX (animated multi-stop / conic / mesh / WebGL-aurora gradients with a Randomize) is enabled the same way: ?gradientFx=on or localStorage.setItem('adflow_gradient_fx','on'). Both default OFF.

Verified against app.htmlPHYSICS_DEFAULTS, the FORM_SOURCES / GRADIENT_FX flag blocks, and the Guide Physics + Randomize handlers.

Editor Feature Map

The editor's user-facing creative surface, beyond the API/MCP layer above. Most features are always visible; a few are flag-gated (enable via URL param or localStorage as shown above). This map reflects the shipping product — see the dedicated sections above for expressions and physics.

AreaWhat's thereStatus
Modes & layers
ModesInput · Layout · Canvas · Output, plus contextual Imaging (Retouch) & Sound.Live
Layer sourcesMedia (image/video), Gradient, Stream (capture), Vector (glyph/SVG). Per-layer visibility, duplicate, reorder, bring-to-top, blend modes, lock.Live
Motion & effects
Vector expressionsInline {A-B} / {w1,w2} / {random} tokens. → full referenceLive
Animate PhysicsForce-field particle motion, Goo / Inorganic modes, Randomize, per-value locks, Guide Physics. → full referenceLive
Form SourcesImage / SVG / Data formations + A→B morph.Flag (formSources)
Shader FXBrightness, Hue Rotate, Grain, plus Glitch, Burn, Chromatic, Warp, Fluorescent, Pixelate, Threshold, Pixel Sort, Dissolve, Halftone (each with params) on media/video/stream layers. Randomise / Clear.Live (CSS tier)
Gradient FXAnimated linear / conic / mesh / aurora gradients with Randomize.Flag (gradientFx)
KeyframesStart / Mid / End poses — position, scale, rotation, opacity, crop, colour, effects, and physics forces. Loop / boomerang. Copy & paste a pose.Live
Linked scenesCarry a layer's end pose into the next scene's start (LINK); melt physics formations A→B across cuts; expressions span the chain.Live (default on)
Colour & type
Per-object colourMaster and per-glyph colour overrides with an eyedropper; right-click to reset to inherit; colour is keyframeable.Live
TypographyGoogle Fonts + custom uploads, per-element font / weight / transform / letter-spacing; alignment (H & V).Live
Capture & imaging
Stream captureScreen / camera / tab capture. Snap a still or press-and-hold to record (held duration = scene length).Live
AI imagingRetouch (erase / inpaint), AI Fill (extend), enhance / upscale, background removal, poster-frame edit. (Also via MCP — see endpoints.)Live
Sound
Sound modeDrag a track in → waveform with crop; threshold slider; Detect transients → auto-scenes on the beat; voice-over track + mix; master volume. Per-scene fades preview today.Live (default on)
Formats & output
Formats & safe-zoneSquare, Portrait, Vertical, Landscape, Wide + Custom; safe-zone overlay (Instagram / TikTok / Custom presets); snap-to-guides; apply-to-all-formats.Live
ExportPNG (active scene / all-scenes ZIP), MP4 (server render), self-contained HTML ZIP, HyperFrames scene/timeline for Figma embed, project JSON. Campaign grouping.Live
Systems & account
Design systemsLoad / create / edit / delete; auto-save brand changes; shared asset library & cloud folder; DS-scoped project lists.Live
Billing & creditsCredit balance, top-up now, auto-top-up (threshold + daily cap, min $5), card management via Stripe.Live

Agent Skills

Skills are prompt modules your agent can invoke to get context-aware guidance for specific tasks. Install the adflow skill in your agent to unlock all MCP tools and workflow knowledge.

Core Skill

adflow

Primary skill for all Adflow operations. Covers MCP tool usage, design system creation, project workflows, scene field requirements, and AI image generation. Triggers on adflow, design system, create a project, build scenes, generate ad creative.

JSON Templates

Download these templates, fill in your client's brand details, and import via the API or MCP tools.

design-system.json project.json

Design System Template

{
  "id": "your-client-slug",
  "name": "Your Client Name",
  "uiAccent": "#2563eb",
  "colorVars": [
    { "hex": "#2563eb", "name": "Primary" },
    { "hex": "#1e40af", "name": "Secondary" },
    { "hex": "#0a0a0a", "name": "Dark" },
    { "hex": "#f5f5f5", "name": "Light" }
  ],
  "fontHeadline": "'Inter', sans-serif",
  "fontBody": "'Inter', sans-serif",
  "fontH1": "'Inter', sans-serif",
  "fontH2": "'Inter', sans-serif",
  "fontH3": "'Inter', sans-serif",
  "fontCta": "'Inter', sans-serif",
  "fontImport": "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap",
  "h1Weight": "900",
  "h2Weight": "400",
  "h3Weight": "400",
  "ctaWeight": "700",
  "h1Transform": "none",
  "h2Transform": "none",
  "ctaTransform": "uppercase",
  "h1LetterSpacing": "-0.02em",
  "h2LetterSpacing": "0",
  "ctaLetterSpacing": "0.05em",
  "ctaRadius": "4px",
  "ctaAlign": "center",
  "contentAlign": "left",
  "contentPosition": "middle",
  "logoPosition": "top-left",
  "logoScale": 100,
  "logoSvg": "",
  "logoFileName": "",
  "disclaimer": "",
  "disclaimerAlign": "left"
}

Project Template

{
  "name": "My Campaign Name",
  "designSystem": "your-client-slug",
  "scenes": [
    {
      "h1": "Your Main Headline",
      "h2": "Supporting copy goes here",
      "h3": "",
      "cta": "Call To Action",
      "bgType": "colour",
      "bgColor": "#0a0a0a",
      "notes": "Scene description or AI image prompt",
      "duration": 6
    },
    {
      "h1": "Second Scene",
      "h2": "Another supporting message",
      "h3": "",
      "cta": "Learn More",
      "bgType": "gradient",
      "bgColor": "#1e40af",
      "notes": "",
      "duration": 6
    }
  ]
}

Direct API Fallback

When MCP tools aren't available, use the REST API directly with your API key.

# List design systems
curl -s -H "X-API-Key: YOUR_KEY" "https://adflow.design/api/design-systems/list"

# Save / update design system
curl -s -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  "https://adflow.design/api/design-systems/save" \
  -d '{"id": "client-slug", "data": { ... }}'

# List projects
curl -s -H "X-API-Key: YOUR_KEY" "https://adflow.design/api/projects/list"

# Get project by UUID
curl -s -H "X-API-Key: YOUR_KEY" "https://adflow.design/api/projects/PROJECT_UUID"

# Create new project
# project blob MUST include version, designSystemId, preset.name, activeFormat
curl -s -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  "https://adflow.design/api/projects/save" \
  -d '{"filename": "project-slug", "project": {"version":2, "designSystemId":"client-slug", "preset":{"name":"Client Name"}, "activeFormat":"square", "scenes":[...]}}'

# Update existing project
curl -s -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  "https://adflow.design/api/projects/save" \
  -d '{"filename": "project-slug", "projectId": "UUID", "project": {"version":2, "designSystemId":"client-slug", "preset":{"name":"Client Name"}, "activeFormat":"square", "scenes":[...]}}'

# ─── AI generation (genai/*) — all POST, all return a blob URL ───

# Generate an image from a text prompt
# model: flux-dev (default), flux-pro, imagen4, imagen4-fast, gpt-image-1, nano-banana-pro, ideogram-v3, recraft-v3, recraft-v3-transparent
curl -s -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  "https://adflow.design/api/genai/generate" \
  -d '{"model":"flux-dev", "prompt":"a red sports car at sunset", "width":2160, "height":2160, "filename":"car.png", "design_system_id":"client-slug"}'

# Edit an existing image with an instruction (the editing suite)
# Same shape applies to /api/genai/inpaint, /retouch, and /enhance
curl -s -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  "https://adflow.design/api/genai/edit" \
  -d '{"model":"nano-banana-pro", "prompt":"make the sky stormy", "image_url":"https://.../car.png", "filename":"car-edited.png"}'

# Outpaint / extend an image (fill new pixels around the frame)
curl -s -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  "https://adflow.design/api/genai/outpaint" \
  -d '{"image_data":"data:image/png;base64,...", "expand_top":0, "expand_bottom":256, "expand_left":0, "expand_right":0, "prompt":"more road", "filename":"car-extended.png"}'

# Animate an element / scene into a video clip
curl -s -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  "https://adflow.design/api/genai/animate" \
  -d '{"prompt":"slow pan across the car", "duration":6, "format":"mp4", "width":1080, "height":1080}'

# List your past generations
curl -s -H "X-API-Key: YOUR_KEY" "https://adflow.design/api/genai/list"

# ─── Renders (renders/*) ───

# Rasterise composition HTML to a PNG screenshot (server-side Puppeteer)
curl -s -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  "https://adflow.design/api/renders/screenshot" \
  -d '{"html":"<!doctype html>...", "width":1080, "height":1080}'

# Render composition HTML to a video on AWS Lambda (returns an executionArn)
curl -s -X POST -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  "https://adflow.design/api/renders/video" \
  -d '{"html":"<!doctype html>...", "width":1080, "height":1080, "fps":30, "quality":"high", "format":"mp4"}'

# Poll render progress (executionArn returned by /renders/video)
curl -s -H "X-API-Key: YOUR_KEY" "https://adflow.design/api/renders/progress?executionArn=ARN"

Critical Rules

Font names must use SINGLE QUOTES.
Font values are injected into HTML style="" attributes. Double quotes break the attribute, silently dropping all CSS properties after the font declaration. Text renders at 16px with no styling.

WRONG: "Inter", sans-serif
RIGHT: 'Inter', sans-serif
All design system fields are required.
Missing fields cause the renderer to fall back to DEFAULT_PRESET values (system font, black text, white background). Always provide the complete field set.
Scenes inherit from the design system.
The create_project tool automatically inherits textColor, ctaBgColor, ctaTextColor, contentAlign, contentPosition, and logoPosition from the design system. You can override any field per-scene in the create_project call or via update_project afterwards.
Font sizes are read-only.
Font sizes are calculated by the renderer from format dimensions. Do not attempt to set font sizes in design systems or scenes.
Projects MUST include designSystemId in the project blob.
The /api/projects/save endpoint reads project.designSystemId to populate the design_system_id DB column. Projects are filtered by design system in the UI — if this field is missing or null, the project will not appear in the project list. You must also include version: 2, preset: {name: "..."}, and activeFormat for the project to load correctly.

WRONG: {"project": {"designSystem": "slug", "scenes": [...]}}
RIGHT: {"project": {"version": 2, "designSystemId": "slug", "preset": {"name": "Client"}, "activeFormat": "square", "scenes": [...]}}
Autosave can overwrite API updates.
The frontend autosaves to the server every 2 seconds. If a user has the project open while your agent updates via API, the frontend's next autosave will overwrite your changes. Coordinate with the user to close the project before making API updates.

Adflow — AI-native ad creative builder — adflow.design