{"openapi":"3.0.0","info":{"title":"Renderly API","version":"1.0.0","description":"Programmatic video creation at scale. Create personalized videos using templates and variable replacements.","contact":{"name":"API Support","email":"support@renderly.video"}},"servers":[{"url":"https://renderly.video/api/v1","description":"Production"},{"url":"http://localhost:3000/api/v1","description":"Development"}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key","description":"API key authentication. Format: `Bearer rnd_your_api_key_here`"}},"schemas":{"Error":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Error message"},"details":{"type":"string","example":"Additional error details"}}},"PreviewWarning":{"type":"object","description":"A problem found while resolving a preview payload. Triage on `severity`: `error` means the render will be wrong or fail; `warning` means it will render but not as authored; `info` is advisory and estimated.","properties":{"code":{"type":"string","enum":["UNKNOWN_REPLACEMENT","UNKNOWN_FIELD","FONT_NOT_RESOLVED","MEDIA_URL_REWRITTEN","TEXT_MAY_CLIP","MEDIA_UNREACHABLE"],"description":"`UNKNOWN_REPLACEMENT` — the key matches no dynamic variable, so it was ignored and the authored default rendered. `UNKNOWN_FIELD` — a field inside `inputProps` is not part of the schema and was discarded silently; the render ignores it. `FONT_NOT_RESOLVED` — the family is not a resolvable Google font and will render as Roboto. `MEDIA_URL_REWRITTEN` — a media URL was normalized. `TEXT_MAY_CLIP` — text is estimated to overflow its box, which the renderer hides rather than shrinking (estimated from average character width; confirm on the preview). `MEDIA_UNREACHABLE` — a supplied media URL did not respond.","example":"UNKNOWN_REPLACEMENT"},"severity":{"type":"string","enum":["error","warning","info"],"example":"error"},"message":{"type":"string"},"variable":{"type":"string","description":"The replacement key this concerns, when it maps to one.","example":"venu_name"},"hint":{"type":"string","example":"Did you mean \"venue_name\"?"},"detail":{"type":"object","additionalProperties":true}}},"ProjectVariable":{"type":"object","description":"A field definition from `GET /projects/{projectId}/variables` and `GET /templates/{templateId}/variables` — both endpoints return this exact shape. Note it differs from the variable shape returned by the project and template LIST endpoints, which carry `defaultValue` instead of `default`, no label or help, and no capacity fields.","properties":{"name":{"type":"string","description":"The key to use in `replacements`.","example":"guest_name"},"type":{"type":"string","enum":["text","url"],"description":"`url` for video, image and sound overlays (replaces `src`); `text` for everything else (replaces `content`).","example":"text"},"label":{"type":"string","example":"guest_name"},"help":{"type":"string","example":"text overlay — replaces content"},"overlayType":{"type":"string","enum":["text","video","image","sound","shape","sticker","caption","effect"],"description":"The overlay type this variable drives.","example":"text"},"field":{"type":"string","enum":["content","src"],"description":"The overlay field your value is written to.","example":"content"},"maxCharacters":{"type":"integer","description":"Roughly how many characters fit before the renderer clips the tail. Present only for fixed-size text — auto-fit text shrinks instead, so it has no fixed capacity. Estimated from average character width, so treat it as a budget rather than a hard limit; the same estimate drives the `TEXT_MAY_CLIP` warning, so staying at or under it means no warning.","example":5},"linesAvailable":{"type":"integer","description":"How many lines the overlay's box can actually show.","example":1},"overflowBehavior":{"type":"string","enum":["clipped"],"description":"What happens to text that does not fit. `clipped` means the renderer hides the overflow — it does not shrink the type or add an ellipsis."},"default":{"type":"string","description":"The authored value. If your replacement key does not match, this is what renders.","example":"Sarah"}}},"TemplateVariable":{"type":"object","description":"The variable shape returned by the template and project LIST endpoints.","properties":{"name":{"type":"string","example":"propertyAddress"},"defaultValue":{"oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}],"example":"123 Main St"},"type":{"type":"string","example":"string"}}},"Template":{"type":"object","properties":{"id":{"type":"string","example":"clx123abc"},"name":{"type":"string","example":"Real Estate Showcase"},"description":{"type":"string","example":"Beautiful property showcase template"},"category":{"type":"string","enum":["BRANDING","REAL_ESTATE","SOCIAL_MEDIA","EDUCATIONAL","ECOMMERCE","SPORTS","PERSONALIZED","EVENTS","TRAVEL"],"example":"REAL_ESTATE"},"thumbnailUrl":{"type":"string","nullable":true,"example":"https://example.com/thumbnail.jpg"},"previewVideoUrl":{"type":"string","nullable":true,"example":"https://example.com/preview.mp4"},"creditsPerMinute":{"type":"integer","example":1,"description":"Credits cost per minute of video (1080p)"},"createdAt":{"type":"string","format":"date-time","example":"2025-01-15T10:00:00Z"},"variables":{"type":"array","items":{"$ref":"#/components/schemas/TemplateVariable"}}}},"RenderJob":{"type":"object","properties":{"jobId":{"type":"string","example":"clx456def"},"projectId":{"type":"string","nullable":true,"description":"Project ID if associated with a project, null otherwise","example":"clx789ghi"},"inputProps":{"type":"object","nullable":true,"description":"Complete render input props including overlays array (available for jobs created after the flexible rendering update)","example":{"overlays":[],"durationInFrames":90,"fps":30,"width":1080,"height":1920}},"status":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED"],"example":"COMPLETED"},"progressPercentage":{"type":"integer","minimum":0,"maximum":100,"example":100},"creditsUsed":{"type":"integer","example":1},"width":{"type":"integer","example":1920},"height":{"type":"integer","example":1080},"fps":{"type":"integer","example":30},"durationInFrames":{"type":"integer","example":1800},"outputUrl":{"type":"string","nullable":true,"example":"https://s3.amazonaws.com/bucket/renders/job.mp4"},"outputSize":{"type":"integer","nullable":true,"example":12458960,"description":"File size in bytes"},"errorMessage":{"type":"string","nullable":true,"example":null},"estimatedTimeRemaining":{"type":"integer","nullable":true,"example":null,"description":"Estimated time remaining in seconds"},"duration":{"type":"integer","nullable":true,"example":45000,"description":"Render duration in milliseconds"},"createdAt":{"type":"string","format":"date-time","example":"2025-01-15T10:00:00Z"},"updatedAt":{"type":"string","format":"date-time","example":"2025-01-15T10:01:30Z"},"completedAt":{"type":"string","format":"date-time","nullable":true,"example":"2025-01-15T10:01:30Z"},"project":{"type":"object","nullable":true,"description":"Project details if associated with a project, null otherwise","properties":{"id":{"type":"string","example":"clx789ghi"},"name":{"type":"string","example":"API Render - Real Estate Showcase"},"templateId":{"type":"string","nullable":true,"description":"Template ID if project was created from a template","example":"clx123abc"}}}}},"Transaction":{"type":"object","properties":{"id":{"type":"string","example":"clxtxn123"},"type":{"type":"string","enum":["CREDIT_PURCHASE","SUBSCRIPTION","ADDITIONAL_CREDITS"],"example":"CREDIT_PURCHASE"},"status":{"type":"string","enum":["PENDING","COMPLETED","FAILED","REFUNDED"],"example":"COMPLETED"},"credits":{"type":"integer","example":1000},"amountInCents":{"type":"integer","example":10000},"currency":{"type":"string","example":"USD"},"createdAt":{"type":"string","format":"date-time","example":"2025-01-10T00:00:00Z"}}},"Upload":{"type":"object","properties":{"id":{"type":"string","example":"asset_123"},"uploadUrl":{"type":"string","example":"https://bucket.s3.region.amazonaws.com/key?signature..."},"fileUrl":{"type":"string","example":"https://bucket.s3.region.amazonaws.com/key"},"expiresAt":{"type":"string","format":"date-time","example":"2025-01-15T10:15:00Z"}}},"AnimationConfig":{"type":"object","description":"Enter/exit/during animation. Use `enterDuration`/`exitDuration` (seconds) to control how long the in/out tween takes — omit duration to keep the legacy 15-frame ramp. `during` adds a continuous motion that runs across the overlay's entire lifetime, composed with the enter and exit ramps (so e.g. a Ken Burns zoom is already drifting during the fade-in and keeps zooming while a fade exit fades it out).","properties":{"enter":{"type":"string","description":"Enter animation preset name (e.g. 'fade', 'slideUp', 'zoomIn', 'bounceIn'). Must match a registered animation template.","example":"fade"},"exit":{"type":"string","description":"Exit animation preset name (e.g. 'fade', 'slideDown', 'zoomOut').","example":"fade"},"enterDuration":{"type":"number","description":"Enter animation duration in seconds.","example":0.5},"exitDuration":{"type":"number","description":"Exit animation duration in seconds.","example":0.5},"during":{"type":"string","enum":["ken-burns-in","ken-burns-out","pan-left","pan-right","drift-diagonal","pulse","float","sway","breathe","spin"],"description":"During animation preset — runs across the overlay's entire lifetime, composed with the enter and exit ramps. Drifts (`ken-burns-in`, `ken-burns-out`, `pan-left`, `pan-right`, `drift-diagonal`) progress once across the overlay's full duration; loops (`pulse`, `float`, `sway`, `breathe`, `spin`) cycle continuously. Supported on image, video, text, and shape overlays.","example":"ken-burns-in"},"duringSpeed":{"type":"number","description":"Speed multiplier for the during animation (default 1). Loops cycle faster; drifts reach their end state sooner, then hold.","default":1,"example":1},"duringIntensity":{"type":"number","description":"Intensity multiplier for the during animation (default 1). Scales how far the motion travels (zoom amount, bob height, rotation angle).","default":1,"example":1}}},"StaggeredMotionConfig":{"type":"object","description":"Coordinated entrance animation across split children (words/chars/cells).","properties":{"enabled":{"type":"boolean","example":true},"stagger":{"type":"number","description":"Frames between each child.","example":3},"staggerDirection":{"type":"string","enum":["forward","reverse","center","random"],"example":"forward"},"y":{"type":"array","items":{"type":"number"},"example":[20,0]},"x":{"type":"array","items":{"type":"number"},"example":[-20,0]},"scale":{"type":"array","items":{"type":"number"},"example":[0,1]},"opacity":{"type":"array","items":{"type":"number"},"example":[0,1]},"rotate":{"type":"array","items":{"type":"number"},"example":[-10,0]},"blur":{"type":"array","items":{"type":"number"},"example":[5,0]},"duration":{"type":"number","example":0.6},"delay":{"type":"number","example":0},"easing":{"type":"string","example":"easeOut"}}},"GreenscreenConfig":{"type":"object","description":"Chroma-key removal for video/image overlays. Defaults target a green backdrop.","required":["enabled"],"properties":{"enabled":{"type":"boolean","example":true},"sensitivity":{"type":"number","description":"0–255. Higher = more aggressive removal.","example":100},"threshold":{"type":"object","properties":{"red":{"type":"number","example":100},"green":{"type":"number","example":100},"blue":{"type":"number","example":100}}},"smoothing":{"type":"number","description":"0–10. Edge smoothing amount.","example":0},"spill":{"type":"number","description":"0–1. Green-spill removal amount.","example":0}}},"OverlayMediaSegment":{"type":"object","description":"An ordered slice of the source media to play. Frames are ABSOLUTE source frames.","required":["startFrame","endFrame"],"properties":{"startFrame":{"type":"number","description":"Source frame (inclusive).","example":0},"endFrame":{"type":"number","description":"Source frame (exclusive).","example":120},"speed":{"type":"number","example":1}}},"GradientTransitionConfig":{"type":"object","description":"Animated gradient transition for shape overlays.","properties":{"enabled":{"type":"boolean","example":true},"gradients":{"type":"array","description":"CSS gradient strings to crossfade between.","items":{"type":"string"},"example":["linear-gradient(45deg, #ff0080, #7928ca)","linear-gradient(45deg, #7928ca, #2563eb)"]},"duration":{"type":"number","description":"Duration in frames.","example":60},"easing":{"type":"string","example":"easeInOut"}}},"CaptionWord":{"type":"object","required":["word","startMs","endMs","confidence"],"properties":{"word":{"type":"string","example":"Hello"},"startMs":{"type":"number","example":0},"endMs":{"type":"number","example":320},"confidence":{"type":"number","example":0.98}}},"Caption":{"type":"object","required":["text","startMs","endMs","words"],"properties":{"text":{"type":"string","example":"Hello world"},"startMs":{"type":"number","example":0},"endMs":{"type":"number","example":1200},"timestampMs":{"type":"number","nullable":true,"example":null},"confidence":{"type":"number","nullable":true,"example":0.97},"words":{"type":"array","items":{"$ref":"#/components/schemas/CaptionWord"}}}},"BaseOverlayFields":{"type":"object","description":"Fields shared by every overlay. Position is in pixels relative to the top-left of the composition canvas. Timing is in frames.","required":["id","from","durationInFrames","row","top","left","width","height"],"properties":{"id":{"type":"number","description":"Unique numeric id within the composition.","example":1},"from":{"type":"number","description":"Start frame on the timeline.","example":0},"durationInFrames":{"type":"number","description":"How long the overlay is visible, in frames.","example":60},"row":{"type":"number","description":"Timeline row / track index. Higher rows render above lower rows (combined with `styles.zIndex`).","example":0},"top":{"type":"number","description":"Y position in pixels from the top of the canvas.","example":100},"left":{"type":"number","description":"X position in pixels from the left of the canvas.","example":50},"width":{"type":"number","description":"Layer width in pixels.","example":800},"height":{"type":"number","description":"Layer height in pixels.","example":200},"rotation":{"type":"number","description":"Rotation in degrees.","default":0,"example":0},"isDragging":{"type":"boolean","example":false},"isDynamic":{"type":"boolean","description":"Marks the overlay as a template variable. Combined with `name`, the matching key in `replacements` swaps `content` (text/shape) or `src` (video/image/sound).","example":false},"name":{"type":"string","description":"Variable key used to match `replacements` when `isDynamic` is true.","example":"titleText"}}},"TextOverlay":{"allOf":[{"$ref":"#/components/schemas/BaseOverlayFields"},{"type":"object","required":["type","content"],"properties":{"type":{"type":"string","enum":["text"]},"content":{"type":"string","description":"The text to render. Supports newlines. When `isDynamic` is true this is replaced by the matching `replacements` entry.","example":"Hello World"},"styles":{"type":"object","properties":{"fontSize":{"type":"string","description":"CSS font-size. Note: when the renderer auto-fits text to the layer, prefer `fontSizeScale` over `fontSize`.","example":"3rem"},"fontSizeScale":{"type":"number","description":"Multiplier applied to the auto-fit font size. 1.0 = 100%.","example":1},"fontWeight":{"type":"string","example":"900"},"color":{"type":"string","example":"#FFFFFF"},"backgroundColor":{"type":"string","example":"transparent"},"fontFamily":{"type":"string","example":"font-sans"},"fontStyle":{"type":"string","example":"normal"},"textDecoration":{"type":"string","example":"none"},"lineHeight":{"type":"string","example":"1.2"},"letterSpacing":{"type":"string","example":"0px"},"textAlign":{"type":"string","enum":["left","center","right"],"example":"center"},"textShadow":{"type":"string"},"padding":{"type":"string","example":"8px 12px"},"paddingBackgroundColor":{"type":"string"},"borderRadius":{"type":"string","example":"8px"},"boxShadow":{"type":"string"},"background":{"type":"string"},"WebkitBackgroundClip":{"type":"string"},"WebkitTextFillColor":{"type":"string"},"backdropFilter":{"type":"string"},"border":{"type":"string"},"textTransform":{"type":"string"},"opacity":{"type":"number","example":1},"zIndex":{"type":"number","example":10},"transform":{"type":"string"},"animation":{"$ref":"#/components/schemas/AnimationConfig"},"staggeredMotion":{"$ref":"#/components/schemas/StaggeredMotionConfig"},"animatedText":{"type":"object","description":"remotion-bits AnimatedText configuration — split-and-stagger entrance.","properties":{"enabled":{"type":"boolean"},"split":{"type":"string","enum":["none","word","character","line"]},"splitStagger":{"type":"number"},"y":{"type":"array","items":{"type":"number"}},"x":{"type":"array","items":{"type":"number"}},"blur":{"type":"array","items":{"type":"number"}},"opacity":{"type":"array","items":{"type":"number"}},"scale":{"type":"array","items":{"type":"number"}},"rotate":{"type":"array","items":{"type":"number"}},"duration":{"type":"number"},"delay":{"type":"number"},"easing":{"type":"string"},"glitch":{"type":"array","items":{"type":"number"}}}},"typeWriter":{"type":"object","description":"remotion-bits TypeWriter configuration — character-by-character typing.","properties":{"enabled":{"type":"boolean"},"typeSpeed":{"type":"number","description":"Frames per character."},"cursor":{"type":"boolean"},"errorRate":{"type":"number","description":"0–1. Probability of mistyping."},"blinkSpeed":{"type":"number","description":"Cursor blink cycle in frames."},"loop":{"type":"boolean"},"deleteBeforeNext":{"type":"boolean"},"pauseAfterType":{"type":"number"},"pauseAfterDelete":{"type":"number"}}},"counterMode":{"type":"object","description":"remotion-bits AnimatedCounter — tweens a number from `from` → `to`.","properties":{"enabled":{"type":"boolean"},"from":{"type":"number"},"to":{"type":"number"},"prefix":{"type":"string"},"postfix":{"type":"string"},"toFixed":{"type":"number"},"duration":{"type":"number"},"easing":{"type":"string"}}},"codeBlock":{"type":"object","description":"remotion-bits CodeBlock — renders `content` as syntax-highlighted code.","properties":{"enabled":{"type":"boolean"},"language":{"type":"string","example":"typescript"},"theme":{"type":"string","enum":["dark","light"],"example":"dark"},"showLineNumbers":{"type":"boolean"},"lineStagger":{"type":"number"},"fontSize":{"type":"number"}}}}}}}]},"VideoOverlay":{"allOf":[{"$ref":"#/components/schemas/BaseOverlayFields"},{"type":"object","required":["type","src"],"properties":{"type":{"type":"string","enum":["video"]},"src":{"type":"string","format":"uri","description":"Public URL of the source video. When `isDynamic` is true this is replaced by the matching `replacements` entry.","example":"https://example.com/background.mp4"},"content":{"type":"string","description":"Optional thumbnail."},"videoStartTime":{"type":"number","description":"In-point of the source video, in SECONDS. Frames before this are skipped. Combine with `durationInFrames` to trim the clip.","example":0},"mediaSrcDuration":{"type":"number","description":"Total duration of the source media in seconds. Used by the editor to compute trim boundaries.","example":30},"speed":{"type":"number","description":"Playback speed multiplier. 1 = normal, 2 = 2x, 0.5 = half.","default":1,"example":1},"segments":{"type":"array","description":"Ordered list of source slices to play in order. Each segment plays a region of the source media. When provided, `videoStartTime` is ignored.","items":{"$ref":"#/components/schemas/OverlayMediaSegment"}},"greenscreen":{"$ref":"#/components/schemas/GreenscreenConfig"},"styles":{"type":"object","properties":{"objectFit":{"type":"string","enum":["contain","cover","fill","none","scale-down"],"example":"cover"},"objectPosition":{"type":"string","example":"center"},"volume":{"type":"number","description":"0–1.","example":1},"borderRadius":{"type":"string","example":"12px"},"filter":{"type":"string","description":"CSS filter string (e.g. 'brightness(1.1) contrast(1.05)')."},"boxShadow":{"type":"string"},"border":{"type":"string"},"padding":{"type":"string"},"paddingBackgroundColor":{"type":"string"},"opacity":{"type":"number","example":1},"zIndex":{"type":"number","example":1},"transform":{"type":"string"},"animation":{"$ref":"#/components/schemas/AnimationConfig"},"staggeredMotion":{"$ref":"#/components/schemas/StaggeredMotionConfig"},"cropEnabled":{"type":"boolean"},"cropX":{"type":"number","description":"Crop X position as percentage (0–100)."},"cropY":{"type":"number","description":"Crop Y position as percentage (0–100)."},"cropWidth":{"type":"number","description":"Crop width as percentage (0–100)."},"cropHeight":{"type":"number","description":"Crop height as percentage (0–100)."},"clipPath":{"type":"string","description":"Generated CSS clip-path value."}}}}}]},"ImageOverlay":{"allOf":[{"$ref":"#/components/schemas/BaseOverlayFields"},{"type":"object","required":["type","src"],"properties":{"type":{"type":"string","enum":["image"]},"src":{"type":"string","format":"uri","description":"Public URL of the image. When `isDynamic` is true this is replaced by the matching `replacements` entry.","example":"https://example.com/image.jpg"},"content":{"type":"string","description":"Optional thumbnail."},"greenscreen":{"$ref":"#/components/schemas/GreenscreenConfig"},"styles":{"type":"object","properties":{"filter":{"type":"string"},"borderRadius":{"type":"string","example":"50%"},"objectFit":{"type":"string","enum":["contain","cover","fill","none","scale-down"],"example":"cover"},"objectPosition":{"type":"string"},"boxShadow":{"type":"string"},"border":{"type":"string"},"padding":{"type":"string"},"paddingBackgroundColor":{"type":"string"},"opacity":{"type":"number","description":"0–1. Combine with a top-level `backgroundColor` on inputProps to layer a translucent image over a solid background.","example":1},"zIndex":{"type":"number","example":5},"transform":{"type":"string"},"animation":{"$ref":"#/components/schemas/AnimationConfig"},"staggeredMotion":{"$ref":"#/components/schemas/StaggeredMotionConfig"},"layout3D":{"type":"object","properties":{"layout":{"type":"string"}}},"cropEnabled":{"type":"boolean"},"cropX":{"type":"number"},"cropY":{"type":"number"},"cropWidth":{"type":"number"},"cropHeight":{"type":"number"},"clipPath":{"type":"string"}}}}}]},"SoundOverlay":{"allOf":[{"$ref":"#/components/schemas/BaseOverlayFields"},{"type":"object","description":"Audio overlay. Note: `width`/`height`/`top`/`left` are required by BaseOverlay but ignored at render time.","required":["type","src"],"properties":{"type":{"type":"string","enum":["sound"]},"src":{"type":"string","format":"uri","description":"Public URL of the audio file. When `isDynamic` is true this is replaced by the matching `replacements` entry.","example":"https://example.com/music.mp3"},"content":{"type":"string"},"startFromSound":{"type":"number","description":"In-point of the source audio, in SECONDS. Use this to skip an intro or to trim from the start of the file. Shrinks `durationInFrames` automatically when set via the editor.","example":5.2},"videoDurationInFrames":{"type":"number","description":"Total duration of the source audio in frames. Used to clamp trims."},"mediaSrcDuration":{"type":"number","description":"Total duration of the source audio in seconds.","example":180},"speed":{"type":"number","description":"Playback speed multiplier.","default":1,"example":1},"styles":{"type":"object","properties":{"volume":{"type":"number","description":"0–1.","example":1},"fadeIn":{"type":"number","description":"Fade-in duration in SECONDS.","example":1},"fadeOut":{"type":"number","description":"Fade-out duration in SECONDS.","example":1},"opacity":{"type":"number"},"zIndex":{"type":"number"},"transform":{"type":"string"}}}}}]},"ShapeOverlay":{"allOf":[{"$ref":"#/components/schemas/BaseOverlayFields"},{"type":"object","required":["type","content"],"properties":{"type":{"type":"string","enum":["shape"]},"content":{"type":"string","description":"Shape identifier (e.g. 'rectangle', 'circle', 'triangle').","example":"rectangle"},"styles":{"type":"object","properties":{"fill":{"type":"string","example":"#000000"},"stroke":{"type":"string"},"strokeWidth":{"type":"number"},"borderRadius":{"type":"string"},"boxShadow":{"type":"string"},"gradient":{"type":"string","description":"Single CSS gradient string.","example":"linear-gradient(45deg, #ff0080, #7928ca)"},"gradientTransition":{"$ref":"#/components/schemas/GradientTransitionConfig"},"opacity":{"type":"number"},"zIndex":{"type":"number"},"transform":{"type":"string"},"animation":{"$ref":"#/components/schemas/AnimationConfig"},"staggeredMotion":{"$ref":"#/components/schemas/StaggeredMotionConfig"}}}}}]},"StickerOverlay":{"allOf":[{"$ref":"#/components/schemas/BaseOverlayFields"},{"type":"object","required":["type","content"],"properties":{"type":{"type":"string","enum":["sticker"]},"content":{"type":"string","description":"Sticker identifier from the sticker library."},"category":{"type":"string","enum":["Shapes","Discounts","Emojis","Reviews","Default"]},"styles":{"type":"object","properties":{"fill":{"type":"string"},"stroke":{"type":"string"},"strokeWidth":{"type":"number"},"scale":{"type":"number","example":1},"filter":{"type":"string"},"opacity":{"type":"number"},"zIndex":{"type":"number"},"transform":{"type":"string"},"animation":{"$ref":"#/components/schemas/AnimationConfig"},"staggeredMotion":{"$ref":"#/components/schemas/StaggeredMotionConfig"}}}}}]},"CaptionOverlay":{"allOf":[{"$ref":"#/components/schemas/BaseOverlayFields"},{"type":"object","description":"Caption / subtitle overlay. Caption timing is in milliseconds and is interpreted at 30 FPS regardless of the composition fps.","required":["type","captions"],"properties":{"type":{"type":"string","enum":["caption"]},"captions":{"type":"array","items":{"$ref":"#/components/schemas/Caption"}},"template":{"type":"string","description":"Optional caption style template name."},"styles":{"type":"object","properties":{"fontFamily":{"type":"string"},"fontSize":{"type":"string"},"lineHeight":{"type":"number"},"textAlign":{"type":"string","enum":["left","center","right"]},"color":{"type":"string"},"backgroundColor":{"type":"string"},"background":{"type":"string"},"backdropFilter":{"type":"string"},"padding":{"type":"string"},"fontWeight":{"oneOf":[{"type":"number"},{"type":"string"}]},"letterSpacing":{"type":"string"},"textShadow":{"type":"string"},"borderRadius":{"type":"string"},"transition":{"type":"string"},"highlightStyle":{"type":"object","description":"Style applied to the currently-spoken word for karaoke-style emphasis.","properties":{"backgroundColor":{"type":"string"},"color":{"type":"string"},"scale":{"type":"number"},"fontWeight":{"type":"number"},"textShadow":{"type":"string"},"padding":{"type":"string"},"borderRadius":{"type":"string"},"transition":{"type":"string"},"background":{"type":"string"},"border":{"type":"string"},"backdropFilter":{"type":"string"}}}}}}}]},"EffectOverlay":{"allOf":[{"$ref":"#/components/schemas/BaseOverlayFields"},{"type":"object","description":"Particle / visual effect overlay.","required":["type","content"],"properties":{"type":{"type":"string","enum":["effect"]},"content":{"type":"string","enum":["snow","confetti","rain","fireflies","sparkles","bubbles","matrix","grid-stagger","fracture","scrolling-columns"],"example":"confetti"},"styles":{"type":"object","properties":{"color":{"type":"string"},"particleCount":{"type":"number"},"speed":{"type":"number"},"size":{"type":"number"},"opacity":{"type":"number"},"zIndex":{"type":"number"},"transform":{"type":"string"},"matrixFontSize":{"type":"number"},"matrixColor":{"type":"string"},"matrixDensity":{"type":"number"},"matrixCharset":{"type":"string"},"gridCols":{"type":"number"},"gridRows":{"type":"number"},"cellColors":{"type":"array","items":{"type":"string"}},"cellImages":{"type":"array","items":{"type":"string"}},"staggerDelay":{"type":"number"},"staggerDir":{"type":"string","enum":["forward","reverse","center","random"]},"animDuration":{"type":"number"},"columns":{"type":"array","items":{"type":"object","properties":{"images":{"type":"array","items":{"type":"string"}},"speed":{"type":"number"},"direction":{"type":"string","enum":["up","down"]}}}},"columnGap":{"type":"number"},"imageHeight":{"type":"number"},"staggeredMotion":{"$ref":"#/components/schemas/StaggeredMotionConfig"}}}}}]}}},"paths":{"/auth/verify":{"post":{"summary":"Verify API Key","description":"Verify that your API key is valid and get basic account information.","operationId":"verifyApiKey","tags":["Authentication"],"responses":{"200":{"description":"API key is valid","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"userId":{"type":"string","example":"clxuser123"},"email":{"type":"string","example":"user@example.com"},"name":{"type":"string","example":"John Doe"},"credits":{"type":"integer","example":1000},"apiKeyPrefix":{"type":"string","example":"rnd_12345678..."},"apiKeyCreatedAt":{"type":"string","format":"date-time","example":"2025-01-15T10:00:00Z"},"apiKeyLastUsed":{"type":"string","format":"date-time","nullable":true,"example":"2025-01-15T12:30:00Z"}}}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/uploads":{"post":{"summary":"Upload Media (Direct Upload)","description":"Get a pre-signed URL to upload media files directly to our storage. Requires Content-Type to enforce file type.","operationId":"uploadMedia","tags":["Uploads"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contentType"],"properties":{"contentType":{"type":"string","example":"video/mp4","description":"MIME type of the file to upload (e.g. video/mp4, image/png)"}}}}}},"responses":{"200":{"description":"Upload URL generated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Upload"}}}}}},"400":{"description":"Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/projects":{"get":{"summary":"List Your Projects","description":"List the projects on your account, each with the variables you can personalize.\n\nUse `?dynamic=true` to return only projects that actually have dynamic variables — those are the ones worth rendering at scale, since a project with no variables produces the same video every time.\n\nPass a returned `id` as `projectId` to `POST /renders` or `POST /previews`.","operationId":"listProjects","tags":["Projects"],"parameters":[{"name":"dynamic","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]},"description":"Set to `true` to return only projects that have at least one dynamic variable."}],"responses":{"200":{"description":"List of your projects","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"projects":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"clx1234567890"},"name":{"type":"string","example":"Wedding invite"},"description":{"type":"string","nullable":true},"width":{"type":"integer","example":1080},"height":{"type":"integer","example":1920},"fps":{"type":"integer","example":30},"durationInFrames":{"type":"integer","nullable":true,"example":900},"hasDynamicOverlays":{"type":"boolean","description":"False means the project has no variables, so every render is identical."},"variables":{"type":"array","description":"The keys you may send in `replacements`.","items":{"type":"object","properties":{"name":{"type":"string","example":"guest_name"},"defaultValue":{"description":"The authored value. This is what renders if your replacement key does not match."},"type":{"type":"string","example":"string"}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"total":{"type":"integer","example":12}}}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/projects/{projectId}/variables":{"get":{"summary":"Get Project Variables","description":"Field definitions for one project's dynamic variables — name, type, label, help text and default value.\n\nRead this before sending `replacements`. A replacement key that matches no variable here is **silently ignored** and the authored default renders instead, so this endpoint is how you learn the correct key names.","operationId":"getProjectVariables","tags":["Projects"],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"},"example":"clx1234567890"}],"responses":{"200":{"description":"Variable definitions","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"variables":{"type":"array","items":{"$ref":"#/components/schemas/ProjectVariable"}}}}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Project not found, or not owned by you","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates":{"get":{"summary":"List Templates","description":"Get all available public templates with their variables and metadata.","operationId":"listTemplates","tags":["Templates"],"responses":{"200":{"description":"List of templates","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"templates":{"type":"array","items":{"$ref":"#/components/schemas/Template"}},"total":{"type":"integer","example":5}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates/{templateId}/variables":{"get":{"summary":"Get Template Variables","description":"The replacement contract for one public template — the exact key names, the overlay field each value is written to, the authored default, and how many characters fit before the renderer clips the tail.\n\nRead this before sending `replacements`. A replacement key that matches no variable here is **silently ignored** and the authored default renders instead, so this endpoint is how you learn the correct key names.\n\nReturns the same shape as `GET /projects/{projectId}/variables`. Prefer it over `GET /templates`, which lists every template but carries no capacity fields.","operationId":"getTemplateVariables","tags":["Templates"],"parameters":[{"name":"templateId","in":"path","required":true,"schema":{"type":"string"},"example":"clx123abc"}],"responses":{"200":{"description":"Variable definitions","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"variables":{"type":"array","items":{"$ref":"#/components/schemas/ProjectVariable"}}}}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Template not found, or not public","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/renders":{"post":{"summary":"Create Render Job","description":"Create a new video render job. Supports three modes: (1) **Direct** — send `inputProps` with the full composition; (2) **Template** — send `templateId` (public templates) plus optional `replacements`; (3) **Project** — send `projectId` (your own saved project) plus optional `replacements`.","operationId":"createRender","tags":["Rendering"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"**Three rendering modes** — exactly one of `inputProps`, `templateId`, or `projectId` is required:\n\n- **Direct** (`inputProps`) — you supply the full composition (overlays + canvas settings).\n- **Template** (`templateId`, optional `replacements`) — the server loads a public template's config and applies your replacements.\n- **Project** (`projectId`, optional `replacements`) — the server loads one of your own projects and applies your replacements.\n\n**Top-level `width` / `height` / `fps` / `durationInFrames` are optional overrides.** The server first builds the final composition from `inputProps`, the template, or the project; then any of these four top-level fields, if present, replaces the corresponding value on that composition. They exist mainly so template/project renders can change resolution or duration without forking. In **direct mode** set these inside `inputProps` and omit them at the top level — duplicating them is allowed but redundant, and if the two disagree the top-level value wins.","properties":{"inputProps":{"type":"object","description":"MODE 1: Complete render configuration with overlays. Use this for direct rendering without a template.\n\n**Positioning**: every overlay positions itself with `top`/`left` (pixels from the top-left of the canvas) and `width`/`height`. To pin an image to the bottom-right of a 1080×1920 canvas, e.g.: `top: 1920 - height - margin`, `left: 1080 - width - margin`.\n\n**Layering**: `row` controls timeline track order and base z-ordering; `styles.zIndex` fine-tunes within a row.\n\n**Canvas background**: set the top-level `backgroundColor` field to fill the canvas behind all overlays (useful with translucent images via `styles.opacity`).","required":["overlays","durationInFrames"],"properties":{"overlays":{"type":"array","description":"Array of overlay elements. Each overlay is one of: text, video, image, sound, shape, sticker, caption, or effect.","items":{"oneOf":[{"$ref":"#/components/schemas/TextOverlay"},{"$ref":"#/components/schemas/VideoOverlay"},{"$ref":"#/components/schemas/ImageOverlay"},{"$ref":"#/components/schemas/SoundOverlay"},{"$ref":"#/components/schemas/ShapeOverlay"},{"$ref":"#/components/schemas/StickerOverlay"},{"$ref":"#/components/schemas/CaptionOverlay"},{"$ref":"#/components/schemas/EffectOverlay"}],"discriminator":{"propertyName":"type","mapping":{"text":"#/components/schemas/TextOverlay","video":"#/components/schemas/VideoOverlay","image":"#/components/schemas/ImageOverlay","sound":"#/components/schemas/SoundOverlay","shape":"#/components/schemas/ShapeOverlay","sticker":"#/components/schemas/StickerOverlay","caption":"#/components/schemas/CaptionOverlay","effect":"#/components/schemas/EffectOverlay"}}}},"durationInFrames":{"type":"integer","description":"Total video duration in frames.","example":90},"fps":{"type":"integer","description":"Frames per second.","default":30,"example":30},"width":{"type":"integer","description":"Video width in pixels.","default":1920,"example":1080},"height":{"type":"integer","description":"Video height in pixels.","default":1080,"example":1920},"backgroundColor":{"type":"string","description":"CSS color fill for the canvas behind every overlay. Use any CSS color (`'#000000'`, `'rgb(0,0,0)'`, `'transparent'`). Defaults to white. Combine with `styles.opacity` on an image/video overlay to layer translucent media over a solid color.","default":"white","example":"#000000"},"src":{"type":"string","description":"Optional source URL.","default":"","example":""}}},"templateId":{"type":"string","description":"MODE 2: ID of a **public template** to render. The server loads the template's `templateConfig` as the composition; combine with `replacements` to swap values on overlays flagged `isDynamic: true`.","example":"clx123abc"},"projectId":{"type":"string","description":"MODE 3: ID of one of **your own saved projects** to render. The server loads the project's `customizationData` as the composition; combine with `replacements` to swap values on overlays flagged `isDynamic: true`. The project must belong to the authenticated API key's user.","example":"clx789ghi"},"replacements":{"type":"object","description":"Variable replacements applied to overlays flagged `isDynamic: true`. Used with `templateId` or `projectId` (ignored in direct mode). Each key matches an overlay's `name`; the value replaces the overlay's `content` (text/shape) or `src` (video/image/sound). No-code platforms (Make, Zapier, etc.) may also send replacement keys as flat top-level fields — any unknown top-level key is merged into `replacements`.\n\n**AI replacement values** (cost AI credits in addition to render credits):\n\n- A **sound** variable accepts `{\"tts\": {\"text\": \"...\", \"voice\"?: \"alloy|nova|...\", \"speed\"?: 1}}` — Renderly generates the narration audio, swaps in its URL, and extends the overlay/composition to the audio's real length. 0.5 credits per started 1,000 characters, max 3 per render.\n- A **caption** variable accepts `{\"transcribe\": {\"source\": \"<overlay name>\"}}` (or `{\"url\": \"https://...\"}`, optional `language`, `startTime`, `endTime`) — Renderly transcribes that overlay's audio (`source` may reference a TTS-generated narration from the same request) and fills the caption overlay with word-synced captions. When the source is a **trimmed video clip** (`videoStartTime` + `durationInFrames`), only the portion that plays in the composition is transcribed and billed — timings are auto-aligned to the clip, including `speed` changes; pass `startTime`/`endTime` (seconds into the source file) to override the window. 0.25 credits per started minute of transcribed audio, max 3 per render.\n\nCombine both to produce a narrated, word-synced-captioned video in a single call.","additionalProperties":true,"example":{"propertyAddress":"456 Ocean Drive","price":"$1,200,000","agentName":"John Williams"}},"width":{"type":"integer","description":"Optional override. If set, replaces `inputProps.width` on the final composition. Useful for re-rendering a template at a different resolution. In direct mode, prefer setting `width` inside `inputProps` and omit this.","example":1920},"height":{"type":"integer","description":"Optional override. If set, replaces `inputProps.height` on the final composition. Useful for re-rendering a template at a different resolution. In direct mode, prefer setting `height` inside `inputProps` and omit this.","example":1080},"fps":{"type":"integer","description":"Optional override. If set, replaces `inputProps.fps` on the final composition. In direct mode, prefer setting `fps` inside `inputProps` and omit this.","example":30},"durationInFrames":{"type":"integer","description":"Optional override. If set, replaces `inputProps.durationInFrames` on the final composition — useful for cropping a template's length without editing it. In direct mode, prefer setting `durationInFrames` inside `inputProps` and omit this.","example":1800},"createProject":{"type":"boolean","description":"Whether to create a project for this render. Defaults to false for direct mode, true for template mode. Works with both modes.","default":false,"example":false},"projectName":{"type":"string","description":"Name for the created project. Only used if createProject is true. Works with both modes.","example":"My Custom Video"},"webhookUrl":{"type":"string","format":"uri","description":"Optional one-off webhook URL. When provided, Renderly will POST a notification to this URL when the render job completes or fails. For persistent webhooks, use the /webhooks endpoint instead.","example":"https://your-app.com/callback"}}},"examples":{"directMode":{"summary":"Direct Rendering (No Template)","value":{"inputProps":{"overlays":[{"id":0,"type":"video","src":"https://example.com/background.mp4","from":0,"durationInFrames":90,"row":0,"top":0,"left":0,"width":1080,"height":1920,"rotation":0,"isDragging":false,"styles":{"objectFit":"cover","zIndex":1,"opacity":1}},{"id":1,"type":"text","content":"Hello World!","from":10,"durationInFrames":60,"row":1,"top":800,"left":100,"width":880,"height":200,"rotation":0,"isDragging":false,"styles":{"fontSize":"3rem","color":"#FFFFFF","fontFamily":"font-sans","fontWeight":"900","textAlign":"center"}}],"durationInFrames":90,"fps":30,"width":1080,"height":1920,"backgroundColor":"#000000"}}},"directModeAdvanced":{"summary":"Direct Rendering (audio trim + fade, animations, image opacity, bottom-right position)","description":"Demonstrates: canvas background fill, image positioned at bottom-right with translucency, image enter/exit animation with explicit durations (seconds), a continuous `during` animation (gentle float loop) running throughout, and a soundtrack trimmed to start 5s into the source with fade-in/out.","value":{"inputProps":{"backgroundColor":"#000000","overlays":[{"id":0,"type":"image","src":"https://example.com/logo.png","from":0,"durationInFrames":180,"row":0,"top":1580,"left":740,"width":300,"height":300,"rotation":0,"styles":{"objectFit":"contain","opacity":0.7,"zIndex":5,"animation":{"enter":"fade","exit":"fade","enterDuration":0.5,"exitDuration":0.5,"during":"float","duringIntensity":1}}},{"id":1,"type":"sound","src":"https://example.com/music.mp3","from":0,"durationInFrames":180,"row":1,"top":0,"left":0,"width":0,"height":0,"startFromSound":5,"mediaSrcDuration":180,"speed":1,"styles":{"volume":0.8,"fadeIn":1,"fadeOut":1.5}}],"durationInFrames":180,"fps":30,"width":1080,"height":1920}}},"realEstate":{"summary":"Template-Based (Real Estate)","value":{"templateId":"clx123abc","replacements":{"propertyAddress":"456 Ocean Drive","price":"$1,200,000","agentName":"John Williams","agentPhoto":"https://example.com/agent.jpg"}}},"socialMedia":{"summary":"Template-Based (Social Media)","value":{"templateId":"clx456def","replacements":{"title":"New Product Launch","description":"Check out our latest innovation","ctaText":"Shop Now"},"width":1080,"height":1080}},"projectMode":{"summary":"Project-Based (Your Own Saved Project)","description":"Render one of your own saved projects with replacements applied to its `isDynamic` overlays.","value":{"projectId":"clx789ghi","replacements":{"headline":"Q2 Recap","background":"https://example.com/q2-recap-background.mp4"}}},"aiNarrationAndCaptions":{"summary":"AI Voiceover + Auto-Captions (one call)","description":"Generate per-record narration from text and word-synced captions from that narration, then render — all in one request. Requires the project to have an `isDynamic` sound overlay (here `narration`) and an `isDynamic` caption overlay (here `subtitles`). AI credits are charged in addition to render credits.","value":{"projectId":"clx789ghi","replacements":{"headline":"42 Oak Street","narration":{"tts":{"text":"This three bedroom home features a renovated kitchen and a large garden.","voice":"nova"}},"subtitles":{"transcribe":{"source":"narration"}}}}}}}}},"responses":{"200":{"description":"Render job created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"jobId":{"type":"string","example":"clx456def"},"projectId":{"type":"string","nullable":true,"description":"Project ID if a project was created, null for direct renders","example":"clx789ghi"},"status":{"type":"string","example":"PROCESSING"},"creditsUsed":{"type":"integer","example":1},"estimatedDurationMinutes":{"type":"integer","example":1},"mode":{"type":"string","enum":["direct","template","project"],"description":"Rendering mode the server resolved to: 'direct' (inputProps), 'template' (templateId), or 'project' (projectId).","example":"direct"}}}}}}}},"400":{"description":"Invalid request data. Also returned when a media overlay (image/video/sound) ends up with an empty `src` after replacements are applied — `details.reason` is `MISSING_MEDIA_SOURCE` and `details.missing` lists each offending overlay by name. No credits are charged for a rejected request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Insufficient credits"},"details":{"type":"object","properties":{"creditsNeeded":{"type":"integer","example":5},"creditsAvailable":{"type":"integer","example":2},"creditsShort":{"type":"integer","example":3}}}}}}}},"404":{"description":"Template not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/previews":{"post":{"summary":"Preview a Render (free)","description":"Resolve a render payload **without rendering it**. Costs no credits and creates no render job.\n\nAccepts exactly the same body as `POST /renders` — the same three modes (`inputProps`, `templateId`, `projectId`), the same `replacements`, the same optional `width`/`height`/`fps`/`durationInFrames` overrides — and resolves it through the identical code path, so what you see is what a render produces.\n\nReturns:\n- `previewUrl` — a **public** page that plays the composition in the browser. No login, nothing rendered, no credits. Append `?mode=grid` for a labelled contact sheet of frames spanning the whole video, or `?frame=N` for a single frame; both render as ordinary images, so an automated viewer can screenshot them. Wait for `data-preview-ready=\"true\"` on the page before capturing. Expires after 24 hours.\n- `editUrl` — opens the same composition in the Renderly editor as a throwaway copy, so editing it never affects the project your code renders. Requires signing in as the account that created the preview.\n- `resolved` — the value that actually landed on each dynamic variable. **A replacement whose key matches no variable is silently ignored and the authored default is rendered**, so this is how you confirm your keys are right.\n- `quote` — what the real render would cost.\n- `warnings` — payload problems, most severe first.\n\n`warnings` and `resolved` are complete on their own, so a CI job can use this endpoint purely as a preflight check without opening any page. Anyone holding `previewUrl` can view it — treat it as a share link.","operationId":"createPreview","tags":["Rendering"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Identical to the `POST /renders` request body. Exactly one of `inputProps`, `templateId` or `projectId` is required. `webhookUrl` is accepted but unused (nothing is rendered, so nothing fires).","properties":{"inputProps":{"type":"object","description":"MODE 1: complete composition, same shape as `POST /renders`."},"templateId":{"type":"string","description":"MODE 2: a public system template id."},"projectId":{"type":"string","description":"MODE 3: one of your own project ids."},"replacements":{"type":"object","description":"Variable overrides keyed by variable name, same as `POST /renders`.","additionalProperties":true},"width":{"type":"integer"},"height":{"type":"integer"},"fps":{"type":"integer"},"durationInFrames":{"type":"integer"}}},"examples":{"project":{"summary":"Preview a project with replacements","value":{"projectId":"clx1234567890","replacements":{"guest_name":"Rebecca","venue_name":"Casa Mira"}}}}}}},"responses":{"200":{"description":"Preview created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"previewUrl":{"type":"string","description":"Public, read-only player page. Supports `?mode=grid` (contact sheet) and `?frame=N` (single frame). No authentication — anyone with the link can view it.","example":"https://renderly.video/p/8fK2nQ7xM3vBpL0sYzWc1dRt"},"editUrl":{"type":"string","description":"Opens the composition in the editor as a throwaway copy. Owner-only; creates the editable project on first visit.","example":"https://renderly.video/p/8fK2nQ7xM3vBpL0sYzWc1dRt/edit"},"previewId":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"},"mode":{"type":"string","enum":["direct","template","project"]},"quote":{"type":"object","description":"What a real render of this payload would cost. Nothing is charged by this endpoint.","properties":{"credits":{"type":"number","example":1.5},"durationMinutes":{"type":"number","example":1.2}}},"resolved":{"type":"object","description":"Variable name -> the value actually applied.","additionalProperties":true},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/PreviewWarning"}}}}}}}}},"400":{"description":"Invalid request data or unresolved media","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Project or template not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/previews/{previewId}":{"put":{"summary":"Update a Preview (free)","description":"Replace a preview's payload **without changing its URL**. Costs no credits.\n\nThis is the iteration endpoint. `POST /previews` mints a new token on every call, so fixing something that way produces a new link and leaves the old one stale — the reviewer's open tab goes dead and an automated viewer has to re-navigate. Here the loop is: `PUT` → reload the same `previewUrl` → look again.\n\nTakes the same body as `POST /previews`. The payload is **replaced, not merged**, so send the complete request each time.\n\n`revision` increments on every update, and the page carries it as `data-preview-revision`. Wait for `[data-preview-revision=\"N\"]` before screenshotting to be sure you are not looking at a cached earlier render.\n\nAn invalid payload is rejected before anything is written, so a failed iteration leaves the existing preview intact and its link working. If someone has already opened the preview in the editor, that copy is left untouched — they may be mid-edit; the player shows the latest payload while the editor copy stays a snapshot from when it was opened.","operationId":"updatePreview","tags":["Rendering"],"parameters":[{"name":"previewId","in":"path","required":true,"schema":{"type":"string"},"description":"The `previewId` returned by `POST /previews`. Note this is the preview id, not the URL token.","example":"cmsodgddy0001110ij7j93een"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Identical to the `POST /previews` request body. Exactly one of `inputProps`, `templateId` or `projectId` is required.","properties":{"inputProps":{"type":"object","description":"MODE 1: complete composition, same shape as `POST /renders`."},"templateId":{"type":"string","description":"MODE 2: a public system template id."},"projectId":{"type":"string","description":"MODE 3: one of your own project ids."},"replacements":{"type":"object","description":"Variable overrides keyed by variable name.","additionalProperties":true},"width":{"type":"integer"},"height":{"type":"integer"},"fps":{"type":"integer"},"durationInFrames":{"type":"integer"}}},"examples":{"corrected":{"summary":"Re-send with the misspelled key fixed","value":{"templateId":"vacation-rental-v1","replacements":{"property_name":"Villa Aurora","property_tagline":"Now spelled correctly."}}}}}}},"responses":{"200":{"description":"Preview updated. Same shape as `POST /previews`, with `previewUrl` unchanged and `revision` incremented.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"previewUrl":{"type":"string","description":"Unchanged from the original preview — reload it rather than navigating somewhere new.","example":"https://renderly.video/p/8fK2nQ7xM3vBpL0sYzWc1dRt"},"editUrl":{"type":"string"},"previewId":{"type":"string"},"revision":{"type":"integer","description":"Incremented on every update. Also on the page as `data-preview-revision`.","example":2},"expiresAt":{"type":"string","format":"date-time"},"mode":{"type":"string","enum":["direct","template","project"]},"quote":{"type":"object","properties":{"credits":{"type":"number","example":1.5},"durationMinutes":{"type":"number","example":1.2}}},"resolved":{"type":"object","description":"Variable name -> the value actually applied.","additionalProperties":true},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/PreviewWarning"}}}}}}}}},"400":{"description":"Invalid request data or unresolved media. The existing preview is left unchanged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such preview — it may have expired (previews last 24 hours) or belong to another account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/renders/{jobId}":{"get":{"summary":"Get Render Status","description":"Get the current status and details of a render job. Poll this endpoint to check when your video is ready.","operationId":"getRenderStatus","tags":["Rendering"],"parameters":[{"name":"jobId","in":"path","required":true,"description":"The ID of the render job","schema":{"type":"string","example":"clx456def"}}],"responses":{"200":{"description":"Render job details","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/RenderJob"}}}}}},"403":{"description":"No permission to access this render job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Render job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/captions":{"post":{"summary":"Generate Captions (AI)","description":"Transcribe an audio or video URL into captions with real word-level timings (up to 5 GB / 3 hours of audio). Returns both a `captions` array and a ready-to-use `captionOverlay` JSON you can drop into a composition's `overlays`.\n\nMost files complete within the request (`status: COMPLETED`). Very long media may return `status: PROCESSING` — poll `GET /ai/jobs/{jobId}` for the finished captions. Retrying the same `sourceUrl` while a job is in flight resumes it rather than charging again.\n\n**Cost: 0.25 credits per started minute of audio.** Refunded automatically if transcription fails.","operationId":"generateCaptions","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["sourceUrl"],"properties":{"sourceUrl":{"type":"string","format":"uri","description":"Public http(s) URL of the audio or video to transcribe (e.g. an uploaded Renderly media asset URL). Must be reachable from the internet.","example":"https://renderly-video.s3.amazonaws.com/user123/asset.mp4"},"language":{"type":"string","description":"Optional ISO-639-1 language hint (e.g. 'en', 'es'). Auto-detected when omitted.","example":"en"},"fps":{"type":"integer","description":"Frames per second of the target composition — used for the returned caption overlay's frame timings.","default":30,"example":30},"startTime":{"type":"number","description":"Seconds into the source file to start transcribing. With `endTime`, only that clip is transcribed — word timings come back clip-relative (0 = clip start) and **billing covers only the window**, not the full file. Use this when your composition plays a cut of a longer source (e.g. a podcast highlight).","example":76},"endTime":{"type":"number","description":"Seconds into the source file to stop transcribing.","example":85}}}}}},"responses":{"200":{"description":"Captions generated (or still processing for very long media)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"jobId":{"type":"string","example":"clxai123"},"status":{"type":"string","enum":["COMPLETED","PROCESSING"],"example":"COMPLETED"},"captions":{"type":"array","description":"Caption pages with word-level timings. Absent while status is PROCESSING.","items":{"$ref":"#/components/schemas/Caption"}},"captionOverlay":{"description":"Ready-to-paste caption overlay JSON (absent while PROCESSING).","$ref":"#/components/schemas/CaptionOverlay"},"durationSeconds":{"type":"number","example":62.4},"creditsUsed":{"type":"number","example":0.5}}}}}}}},"400":{"description":"Invalid source URL, file too large, or no speech detected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"AI features not configured on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/voiceover":{"post":{"summary":"Generate Voiceover (AI)","description":"Turn text into narration audio (text-to-speech). The generated mp3 is stored in your media library and returned as a public URL you can use as a sound overlay `src`, a replacement value, or a captions source.\n\nFor per-record narration inside a render, you can skip this endpoint and pass `{\"tts\": {\"text\": ...}}` directly as a replacement value on an `isDynamic` sound overlay in `POST /renders`.\n\n**Cost: 0.5 credits per started 1,000 characters.** Refunded automatically on failure.","operationId":"generateVoiceover","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string","maxLength":4096,"description":"The narration text to speak.","example":"Welcome to 42 Oak Street, a beautiful three bedroom home."},"voice":{"type":"string","enum":["alloy","ash","coral","echo","fable","nova","onyx","sage","shimmer"],"default":"alloy","description":"Voice to use."},"speed":{"type":"number","minimum":0.25,"maximum":4,"default":1,"description":"Speech speed multiplier."}}}}}},"responses":{"200":{"description":"Voiceover generated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"jobId":{"type":"string","example":"clxai456"},"status":{"type":"string","example":"COMPLETED"},"url":{"type":"string","format":"uri","description":"Public URL of the generated mp3.","example":"https://renderly-video.s3.amazonaws.com/user123/narration.mp3"},"durationMs":{"type":"integer","example":10320},"mediaAssetId":{"type":"string","description":"The created media asset (visible in your library).","example":"clxasset789"},"creditsUsed":{"type":"number","example":0.5}}}}}}}},"400":{"description":"Missing/too-long text or unknown voice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"AI features not configured on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/jobs/{jobId}":{"get":{"summary":"Get AI Job Status","description":"Fetch the status and result of an AI job (captions or voiceover). Captions jobs that returned `PROCESSING` are checked against the transcription provider on each read and settle automatically when finished — poll this endpoint every few seconds until `status` is `COMPLETED` or `FAILED`.","operationId":"getAiJob","tags":["AI"],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"},"description":"The AI job id returned by /ai/captions or /ai/voiceover."}],"responses":{"200":{"description":"AI job details","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"jobId":{"type":"string"},"type":{"type":"string","enum":["CAPTIONS","TTS"]},"status":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED"]},"provider":{"type":"string","example":"assemblyai"},"creditsUsed":{"type":"number"},"result":{"type":"object","nullable":true,"description":"For CAPTIONS: { captions, captionOverlay, language }. For TTS: { url, durationMs, mediaAssetId }."},"resultUrl":{"type":"string","nullable":true},"errorMessage":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time","nullable":true}}}}}}}},"403":{"description":"Job belongs to another user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/account":{"get":{"summary":"Get Account Information","description":"Get account details, credits balance, and usage statistics.","operationId":"getAccount","tags":["Account"],"responses":{"200":{"description":"Account information","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string","example":"clxuser123"},"email":{"type":"string","example":"user@example.com"},"name":{"type":"string","example":"John Doe"},"credits":{"type":"integer","example":950},"role":{"type":"string","example":"USER"},"createdAt":{"type":"string","format":"date-time","example":"2025-01-01T00:00:00Z"}}},"apiKey":{"type":"object","properties":{"prefix":{"type":"string","example":"rnd_12345678..."},"createdAt":{"type":"string","format":"date-time","example":"2025-01-15T10:00:00Z"},"lastUsed":{"type":"string","format":"date-time","nullable":true,"example":"2025-01-15T12:30:00Z"}}},"usage":{"type":"object","properties":{"totalRenderJobs":{"type":"integer","example":50},"completedJobs":{"type":"integer","example":45},"failedJobs":{"type":"integer","example":3},"pendingJobs":{"type":"integer","example":2},"totalCreditsUsed":{"type":"integer","example":150}}},"recentTransactions":{"type":"array","items":{"$ref":"#/components/schemas/Transaction"}}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks":{"post":{"summary":"Create Webhook","description":"Register a webhook endpoint to receive notifications when render jobs complete or fail. Returns a signing secret (shown only once).","operationId":"createWebhook","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"The URL to receive webhook events","example":"https://your-app.com/webhooks/renderly"},"events":{"type":"array","description":"Events to subscribe to. Defaults to all events.","items":{"type":"string","enum":["render.completed","render.failed"]},"default":["render.completed","render.failed"],"example":["render.completed","render.failed"]}}}}}},"responses":{"200":{"description":"Webhook created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"id":{"type":"string","example":"clxwh123"},"url":{"type":"string","example":"https://your-app.com/webhooks/renderly"},"secret":{"type":"string","description":"HMAC signing secret. Only returned at creation time.","example":"whsec_abc123..."},"events":{"type":"array","items":{"type":"string"},"example":["render.completed","render.failed"]},"isActive":{"type":"boolean","example":true},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Invalid request or max webhooks reached (5)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"summary":"List Webhooks","description":"List all registered webhook endpoints for your account.","operationId":"listWebhooks","tags":["Webhooks"],"responses":{"200":{"description":"List of webhooks","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"webhooks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"},"totalEvents":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"total":{"type":"integer"}}}}}}}}}},"delete":{"summary":"Delete Webhook","description":"Delete a webhook endpoint. Pass the webhook ID as a query parameter.","operationId":"deleteWebhook","tags":["Webhooks"],"parameters":[{"name":"id","in":"query","required":true,"description":"The ID of the webhook to delete","schema":{"type":"string"}}],"responses":{"200":{"description":"Webhook deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"message":{"type":"string","example":"Webhook deleted"}}}}}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"tags":[{"name":"Authentication","description":"API key verification and authentication"},{"name":"Projects","description":"Your saved projects and the variables you can personalize on them. Read the variables before sending replacements — an unmatched key is silently ignored."},{"name":"Templates","description":"Browse and discover video templates"},{"name":"Rendering","description":"Create and manage video render jobs"},{"name":"AI","description":"AI helpers that feed renders: auto-captions (transcription with word-level timings) and voiceover (text-to-speech). Also available declaratively inside POST /renders via the `tts` and `transcribe` replacement values."},{"name":"Uploads","description":"Upload media assets for use as overlay sources."},{"name":"Webhooks","description":"Register webhook endpoints to receive real-time notifications for render events"},{"name":"Account","description":"Account information and usage statistics"}]}