Templates & Personalization

How to render the same composition with different text, images, video, and audio per request using the isDynamic + replacements system.

A "template" in Renderly is a composition where some overlays are marked as variables. At render time you pass a replacements object and the marked overlays swap their content. One template = thousands of personalized outputs.

The two pieces

Personalization rests on two coordinated fields on an overlay:

  • isDynamic: true — flags the overlay as a variable. Without this, the overlay is static and replacements don't touch it.
  • name: "<key>" — the lookup key that links this overlay to a value in replacements.

At render time the server walks every overlay; if isDynamic is true, it looks up name in replacements and swaps the appropriate field:

Overlay typeField replaced
text, shapecontent
video, image, soundsrc

There is no {{ variable }} syntax inside content strings. The unit of replacement is a whole overlay, identified by its name.

A complete template

{
  "replacements": {
    "propertyAddress": "123 Main St",
    "agentPhoto": "https://example.com/agent-default.jpg",
    "backgroundVideo": "https://example.com/bg-default.mp4"
  },
  "overlays": [
    {
      "id": 0, "type": "video",
      "name": "backgroundVideo", "isDynamic": true,
      "src": "https://example.com/bg-default.mp4",
      "from": 0, "durationInFrames": 180,
      "row": 0, "top": 0, "left": 0, "width": 1080, "height": 1920,
      "styles": { "objectFit": "cover" }
    },
    {
      "id": 1, "type": "text",
      "name": "propertyAddress", "isDynamic": true,
      "content": "123 Main St",
      "from": 10, "durationInFrames": 170,
      "row": 1, "top": 200, "left": 60, "width": 960, "height": 200,
      "styles": { "fontSize": "4rem", "color": "#FFF", "textAlign": "center" }
    },
    {
      "id": 2, "type": "image",
      "name": "agentPhoto", "isDynamic": true,
      "src": "https://example.com/agent-default.jpg",
      "from": 30, "durationInFrames": 150,
      "row": 2, "top": 1500, "left": 390, "width": 300, "height": 300,
      "styles": { "borderRadius": "50%", "objectFit": "cover" }
    },
    {
      "id": 3, "type": "text",
      "content": "Listed by Renderly Realty",
      "from": 0, "durationInFrames": 180,
      "row": 3, "top": 1850, "left": 60, "width": 960, "height": 50,
      "styles": { "fontSize": "1rem", "color": "#FFF", "textAlign": "center" }
    }
  ],
  "durationInFrames": 180, "fps": 30,
  "width": 1080, "height": 1920
}

In this example, overlays 0–2 are variables; overlay 3 is a static watermark that every rendered video keeps unchanged.

Two important things about the replacements block:

  • It lives at the top level of the composition (next to overlays), not inside any overlay.
  • The values you put in it are defaults — they're used when the caller doesn't pass an override. This is what makes templates self-rendering for preview purposes.

Rendering a template

You can store a template centrally and call it by ID, or you can pass the whole composition inline and override its variables.

Template stored by Renderly

If the composition lives in our public template gallery, send templateId:

curl -X POST https://renderly.video/api/v1/renders \
  -H "Authorization: Bearer rnd_..." \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "tpl_real_estate_v1",
    "replacements": {
      "propertyAddress": "456 Ocean Drive",
      "agentPhoto": "https://your-cdn.example.com/agents/jane.jpg",
      "backgroundVideo": "https://your-cdn.example.com/listings/456-ocean.mp4"
    }
  }'

The server loads tpl_real_estate_v1's composition, applies your replacements, and queues the render.

Your own project

If the composition is a project you built in the editor, send projectId instead. Same payload shape — replacements still match by overlay name:

curl -X POST https://renderly.video/api/v1/renders \
  -H "Authorization: Bearer rnd_..." \
  -H "Content-Type: application/json" \
  -d '{
    "projectId": "proj_my_listing_template",
    "replacements": {
      "propertyAddress": "456 Ocean Drive",
      "agentPhoto": "https://your-cdn.example.com/agents/jane.jpg"
    }
  }'

See Rendering — three modes for the full picture.

Resolution / duration overrides

Both templateId and projectId calls accept top-level overrides:

{
  "templateId": "tpl_real_estate_v1",
  "replacements": { /* ... */ },
  "width": 1080,
  "height": 1080,
  "fps": 30,
  "durationInFrames": 300
}

Use this to render a 9:16 template at 1:1 for Instagram without forking the template, or to crop its length down.

Discovering a template's variables

Two ways:

  1. Public templatesGET /templates returns every public template with its variables[] array. Each entry has name, defaultValue, and type.
  2. Your own project — open it in the editor and look at the Variables panel, which lists every overlay flagged isDynamic.

Flat-key shortcut for no-code platforms

Tools like Make, Zapier, or Pipedream often can't easily nest a replacements object inside JSON. To support them, Renderly treats any unknown top-level key as a replacement:

{
  "templateId": "tpl_real_estate_v1",
  "propertyAddress": "456 Ocean Drive",
  "agentPhoto": "https://your-cdn.example.com/agents/jane.jpg"
}

Internally these get merged into replacements. If a key appears both flat and inside replacements, the explicit replacements value wins.

Patterns for keeping templates clean

  • Pick stable namesheadline, backgroundVideo, ctaText. These are user-facing keys.
  • Always include defaults — store sensible default values in the replacements object so the template renders standalone (great for previews).
  • Lock down static elements — leave isDynamic unset on watermarks, logos, and branded chrome you don't want callers overriding.
  • Group related variables — when one logical thing needs two fields (e.g. an avatar URL and a name caption), use related names like agentPhoto / agentName so consumers can spot the pairing.

Where to next