{"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.\n\n## Versioning and deprecation\n\nThe API is versioned in the URL. `/api/v1` is the current and only version. Additive changes (new endpoints, new optional fields, new enum members) ship in place and are not breaking - clients must ignore fields they do not recognise. Anything breaking ships as a new prefix (`/api/v2`); `/api/v1` is never changed out from under you.\n\nWhen an endpoint or a version is scheduled for removal, its responses carry the standard `Deprecation` and `Sunset` HTTP headers (RFC 9745 and RFC 8594) from the moment the decision is made until it is switched off, with at least 6 months between the two dates. Nothing is deprecated today, so nothing currently emits them. Poll any endpoint for a `Sunset` header if you want to detect this automatically.\n\n## Rate limits\n\nEvery response carries rate-limit headers in two conventions: the IETF `RateLimit-Limit` / `RateLimit-Remaining` / `RateLimit-Reset` set, and the legacy `X-RateLimit-*` set. They report the same window, with one difference that matters: `RateLimit-Reset` is **seconds until reset**, while `X-RateLimit-Reset` is an **absolute Unix timestamp**. A 429 additionally carries `Retry-After`. Limits are per minute and depend on plan: 60 with no subscription, 120 on Creator, 300 on Business.","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":[]},{"OAuth2":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key","description":"API key authentication. Format: `Bearer rnd_your_api_key_here`"},"OAuth2":{"type":"oauth2","description":"OAuth 2.1 with PKCE, used by the MCP server at `/api/mcp` and by any client that would rather not hold a long-lived key. Discovery starts at `/.well-known/oauth-protected-resource` (RFC 9728), which names the authorization server; dynamic client registration is supported. Scopes are declared per operation below. They are not yet enforced - a valid credential currently receives all of them - so treat a scope as a statement of what an operation touches rather than as a boundary.","flows":{"authorizationCode":{"authorizationUrl":"https://renderly.video/.well-known/oauth-protected-resource","tokenUrl":"https://renderly.video/.well-known/oauth-protected-resource","scopes":{"account:read":"Read your credit balance, plan and usage.","templates:read":"List public templates and read their variable contracts.","projects:read":"List your projects and read their variable contracts.","renders:read":"Poll render jobs and read their output URLs.","renders:write":"Create render jobs. Consumes credits.","previews:write":"Resolve a payload into a preview. Free, renders nothing.","uploads:write":"Upload media assets to your account.","ai:write":"Generate captions and voiceover audio. Consumes credits.","webhooks:write":"Register, list and remove webhook endpoints."}}}}},"headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer","example":120}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer","example":118}},"RateLimit-Reset":{"description":"Seconds until the window resets. NOTE: this is a delta, unlike `X-RateLimit-Reset`.","schema":{"type":"integer","example":42}},"X-RateLimit-Limit":{"description":"Legacy alias of `RateLimit-Limit`.","schema":{"type":"integer","example":120}},"X-RateLimit-Remaining":{"description":"Legacy alias of `RateLimit-Remaining`.","schema":{"type":"integer","example":118}},"X-RateLimit-Reset":{"description":"Unix timestamp (seconds) at which the window resets. NOTE: absolute, unlike `RateLimit-Reset`.","schema":{"type":"integer","example":1789619015}},"Retry-After":{"description":"Seconds to wait before retrying. Sent on 429 responses.","schema":{"type":"integer","example":42}}},"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","FONT_WEIGHT_UNAVAILABLE","MEDIA_URL_REWRITTEN","TEXT_MAY_CLIP","FONT_SIZE_UNIT_IGNORED","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. `FONT_WEIGHT_UNAVAILABLE` - the family is resolvable but does not ship the requested `fontWeight` (or `fontStyle`), so the nearest available face is used instead; the `detail` names the weights the family does have. `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). `FONT_SIZE_UNIT_IGNORED` - a text overlay's `styles.fontSize` is set to a unit the renderer cannot use (only px and unitless numbers are accepted; rem, em, vw, %, and pt all fall back), so the overlay was auto-fit instead. `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://renderly-video.s3.ap-east-1.amazonaws.com/templates/ecommerce-drop-sale-v2-1.jpg"},"previewVideoUrl":{"type":"string","nullable":true,"example":"https://renderly-video.s3.ap-east-1.amazonaws.com/templates/news-weather-updates.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 track index, which also decides stacking order.\n\n**⚠️ `row` is the REVERSE of CSS `z-index`. A LOWER `row` number renders IN FRONT of a higher one.** The renderer computes `zIndex = 100 - row * 10`, so `row: 0` → z-index 100 (frontmost), `row: 1` → 90, `row: 4` → 60 (further back). `row: 0` covers `row: 1`, which covers `row: 2`, and so on.\n\nPut background video and full-frame effects on the HIGHEST row numbers (e.g. `row: 4`), and headlines, logos, or anything that must sit in front on `row: 0`. If your text is invisible, it is almost always on a higher row than the video behind it - lower its `row` number.\n\nA safe convention for a full composition: `row: 5` audio · `row: 4` background video/effect · `row: 3` scrim/dim shape · `row: 2` decorative graphics · `row: 1` body text · `row: 0` headline.\n\nGive every visually-stacked overlay its own row. Two overlays on the same row share a z-index and fall back to array order, which is fragile. Note that `styles.zIndex` cannot lift an overlay past another row: each overlay is wrapped in its own stacking context, so `zIndex` only orders content inside that one overlay.","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":"Upper bound for the rendered text size, e.g. \"48px\". The renderer uses exactly this size when it fits the layer, and shrinks it only as far as needed to stay inside the layer - so text never overflows. Accepts px and unitless numbers only (both taken as pixels). rem, em, vw, %, pt and every other unit are NOT supported and are ignored in favor of auto-fit - use px. (Caption overlays are the exception: their fontSize is raw CSS and does accept rem.) Omit it to let the renderer auto-fit from the layer's width, height and content length. When both fontSize and fontSizeScale are set, the explicit fontSize wins and fontSizeScale is ignored.","example":"48px"},"fontSizeFixed":{"type":"boolean","description":"Render at exactly fontSize even when the text does not fit, clipping the overflow. Use it to match a fixed design; size the layer to fit the text yourself. Defaults to false, where fontSize is an upper bound instead.","example":false},"fontSizeScale":{"type":"number","description":"Multiplier applied to the auto-fit size. Only takes effect when fontSize is omitted. Stay between 0.8 and 1.4.","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://renderly-video.s3.ap-east-1.amazonaws.com/templates/news-weather-updates.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://renderly-video.s3.ap-east-1.amazonaws.com/templates/ecommerce-drop-sale-v2-1.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","description":"3D tilt. Image overlays only. On its own the pose is static; set `settle: true` to have the image arrive at the full angle and rotate to flat over its enter animation (timed by animation.enterDuration / enterEasing).","properties":{"layout":{"type":"string","enum":["none","tilt-up","tilt-down","card-left","card-right","book","floating","billboard","skewed"],"example":"card-right"},"settle":{"type":"boolean","description":"Rotate the pose to flat across the enter ramp instead of holding it. Defaults to false.","example":true}}},"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://renderly-video.s3.ap-east-1.amazonaws.com/templates/promo-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, resolved against the composition's own 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","description":"Caption text size, applied as raw CSS. Any CSS length works, including rem and em (resolved at 16px per unit) - captions are not auto-fit, so the value is used exactly as written. Note this differs from a TEXT overlay's fontSize, which is parsed for auto-fit and accepts px or unitless numbers only.","example":"48px"},"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","security":[{"BearerAuth":[]},{"OAuth2":[]}],"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","security":[{"BearerAuth":[]},{"OAuth2":["uploads:write"]}],"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","security":[{"BearerAuth":[]},{"OAuth2":["projects:read"]}],"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","security":[{"BearerAuth":[]},{"OAuth2":["projects:read"]}],"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","security":[{"BearerAuth":[]},{"OAuth2":["templates:read"]}],"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","security":[{"BearerAuth":[]},{"OAuth2":["templates:read"]}],"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","security":[{"BearerAuth":[]},{"OAuth2":["renders:write"]}],"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` decides which overlay is in front - and it works the OPPOSITE way to CSS `z-index`. **A LOWER `row` number renders IN FRONT of a higher one.** The renderer computes `zIndex = 100 - row * 10`, so `row: 0` covers `row: 1`, which covers `row: 2`. Background video belongs on a high row (e.g. `row: 4`); the headline belongs on `row: 0`. `styles.zIndex` does not change this - each overlay sits in its own stacking context, so `zIndex` only orders content inside a single overlay.\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\"?: \"henry|ruby|...\", \"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. When the source is narration generated by a `tts` replacement in the same request (or by a previous /ai/voiceover call), the word timings come back with the audio and the captions are free - no transcription charge.\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://renderly-video.s3.ap-east-1.amazonaws.com/templates/news-weather-updates.mp4","from":0,"durationInFrames":90,"row":1,"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":0,"top":800,"left":100,"width":880,"height":200,"rotation":0,"isDragging":false,"styles":{"fontSize":"48px","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://renderly.video/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://renderly-video.s3.ap-east-1.amazonaws.com/templates/promo-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://renderly-video.s3.ap-east-1.amazonaws.com/templates/ecommerce-drop-sale-v2-1.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://renderly-video.s3.ap-east-1.amazonaws.com/templates/news-weather-updates.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":"henry"}},"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. It excludes AI charges, and does not account for a composition that generated narration would have lengthened.\n- `warnings` - payload problems, most severe first.\n- `aiSkipped` - present when AI replacements were skipped; lists the keys and explains the cost. `null` otherwise.\n\n**AI replacements are skipped by default.** A `tts` value is not synthesized and a `transcribe` value is not run, so the preview stays free: narration overlays are silent here and caption overlays stay empty. Synthesis bills per call, so running it inside the endpoint that exists to help you avoid spending would invert its purpose. Send `confirmAiCost: true` to generate them for real and be billed; renders always run AI regardless.\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","security":[{"BearerAuth":[]},{"OAuth2":["previews:write"]}],"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":{"confirmAiCost":{"type":"boolean","default":false,"description":"Opt in to paid AI generation. When false or absent, `tts` and `transcribe` replacement values are skipped and reported in `aiSkipped` instead of being charged."},"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","security":[{"BearerAuth":[]},{"OAuth2":["previews:write"]}],"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","security":[{"BearerAuth":[]},{"OAuth2":["renders:read"]}],"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","security":[{"BearerAuth":[]},{"OAuth2":["ai:write"]}],"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. The response also includes `words`, an array of per-word `{ word, startMs, endMs }` timings - reuse them to caption this narration for free instead of paying to transcribe it.\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","security":[{"BearerAuth":[]},{"OAuth2":["ai:write"]}],"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","description":"Voice id. The twelve listed here are the curated set; any Speechify catalogue voice id is accepted, including non-English ones across 36 locales.","example":"henry","enum":["claudette","emily","geffenv1","henry","hugh_32","jacob","jordan","julie","monica","nick","phil","ruby"],"default":"henry"},"speed":{"type":"number","minimum":0.5,"maximum":4,"default":1,"description":"Speech speed multiplier."},"provider":{"type":"string","enum":["speechify","openai"],"default":"speechify","description":"TTS provider. `speechify` (default) returns per-word timings and is used for free caption reuse. `openai` is a fallback for languages Speechify cannot speak, but returns no timings, so captions transcribed from its narration are not free."}}}}}},"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},"words":{"type":"array","nullable":true,"description":"Per-word timings for the generated audio, in ascending order. Reuse these to caption this narration for free instead of paying to transcribe it (see the `transcribe` replacement in POST /renders).","items":{"type":"object","properties":{"word":{"type":"string","example":"Welcome"},"startMs":{"type":"integer","example":0},"endMs":{"type":"integer","example":320}}}}}}}}}}},"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","security":[{"BearerAuth":[]},{"OAuth2":["ai:write"]}],"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","security":[{"BearerAuth":[]},{"OAuth2":["account:read"]}],"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","security":[{"BearerAuth":[]},{"OAuth2":["webhooks:write"]}],"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","security":[{"BearerAuth":[]},{"OAuth2":["webhooks:write"]}],"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","security":[{"BearerAuth":[]},{"OAuth2":["webhooks:write"]}],"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"}]}