Automotion docs
Guides

Data-driven video from a template

Declare {{variables}} once, save the document as a template, and render personalized videos per row of data.

Render the same video a thousand times with different data: product cards, personalized outreach, daily stats, localized ads. Declare {{variables}} in the document, save it as a template, then create renders from the template with per-request values.

1. Declare variables

Any string value may contain {{name}} placeholders. Declare each variable in movie.variables with a type (string, number, boolean, url, color, enum) and an optional default:

movie.json
{
  "schema": "v1",
  "preset": "1:1",
  "variables": {
    "product": { "type": "string", "description": "Product name shown as the headline." },
    "price": { "type": "string", "description": "Formatted price, e.g. \"$49\"." },
    "image_url": { "type": "url", "description": "Public product image URL." },
    "accent": { "type": "color", "default": "#22d3ee", "description": "Accent color." }
  },
  "scenes": [
    {
      "id": "card",
      "duration": 4,
      "background": "#0b1120",
      "elements": [
        {
          "id": "card-image",
          "type": "image",
          "src": "{{image_url}}",
          "width": 640,
          "height": 640,
          "position": { "x": 540, "y": 420 },
          "fit": "contain",
          "animation": { "preset": "zoom-in", "duration": 0.5 }
        },
        {
          "id": "card-title",
          "type": "text",
          "text": "{{product}}",
          "fontSize": 64,
          "fontWeight": 700,
          "position": { "x": 540, "y": 800 }
        },
        {
          "id": "card-price",
          "type": "text",
          "text": "Now {{price}}",
          "fontSize": 48,
          "color": "{{accent}}",
          "position": { "x": 540, "y": 900 }
        }
      ]
    }
  ]
}

▶ Try it in the playground

Validation is strict in your favor: referencing an undeclared variable fails with VARIABLE_UNDECLARED, a missing value with no default fails with VARIABLE_MISSING, and a value that can't be losslessly coerced to the declared type fails with VARIABLE_TYPE_MISMATCH — each at the first reference site's exact JSON path.

2. Save it as a template

curl -X POST https://api.automotion.dev/v1/templates \
  -H "Authorization: Bearer $AUTOMOTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Product card", "body": <the document above> }'

The response echoes the template with its variables manifest (derived from body.variables — there is no separate manifest input to drift). Declared but unused variables come back as non-fatal warnings.

3. Render it per data row

POST /v1/templates/{id}/renders with the values:

curl -X POST https://api.automotion.dev/v1/templates/$TEMPLATE_ID/renders \
  -H "Authorization: Bearer $AUTOMOTION_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: product-42-v1" \
  -d '{
    "variables": {
      "product": "Trail Runner XT",
      "price": "$129",
      "image_url": "https://assets.example.com/products/trail-runner.png"
    }
  }'

Defaults apply for anything you omit (accent above). Template renders go through exactly the same pipeline as raw renders — same credit balance, same idempotency, same webhooks — and answer with the same render resource.

Batch pattern

For a spreadsheet/DB batch, loop the rows client-side and use a deterministic Idempotency-Key per row (e.g. order-{{id}}-v1). Crashed and resumed jobs then never double-render: replays return the original render with 200. Respect the X-RateLimit-Remaining header while looping, and watch CONCURRENCY_LIMIT — queue locally when you hit your plan's concurrent-render cap.

Wire it into n8n, Make or Zapier with the automation recipes.

On this page