{
  "name": "Create Videotok carousels from a social content calendar",
  "nodes": [
    {
      "parameters": {
        "content": "## Social calendar carousel machine\n\nUse this when approved content calendar rows should become branded carousels or slideshow videos automatically.\n\nReplace the sample row with Google Sheets, Airtable, Notion, or your planning tool. The workflow writes final URLs back to your review process.\n\nRequired variables: VIDEOTOK_API_KEY, OPENAI_API_KEY, VIDEOTOK_BRAND_KIT_ID.",
        "height": 300,
        "width": 500,
        "color": 6
      },
      "id": "note-overview",
      "name": "Overview and setup",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [-700, -260]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 */6 * * *"
            }
          ]
        }
      },
      "id": "schedule",
      "name": "Check approved calendar rows",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [-620, 120]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "topic",
              "name": "topic",
              "type": "string",
              "value": "Five mistakes killing your paid social creative"
            },
            {
              "id": "campaign",
              "name": "campaign",
              "type": "string",
              "value": "Q3 creative engineering campaign"
            },
            {
              "id": "product",
              "name": "product_image_url",
              "type": "string",
              "value": "https://example.com/product.png"
            }
          ]
        }
      },
      "id": "calendar-row",
      "name": "Replace with calendar row",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [-360, 120]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/responses",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{$vars.OPENAI_API_KEY}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\"model\":\"gpt-4.1-mini\",\"input\":\"Turn this content calendar row into JSON for a 6-slide social carousel. Include title, prompt, product_context, caption, hashtags. Row: \" + JSON.stringify($json)}"
      },
      "id": "openai-carousel",
      "name": "OpenAI writes carousel brief",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [-80, 120]
    },
    {
      "parameters": {
        "jsCode": "const row = $('Replace with calendar row').first().json;\nconst raw = $input.first().json.output_text || '{}';\nlet brief;\ntry { brief = JSON.parse(raw); } catch { brief = { title: row.topic, prompt: raw, product_context: row.campaign, caption: raw.slice(0, 220), hashtags: '#videotok #socialmedia' }; }\nreturn [{ json: { ...brief, product_image_url: row.product_image_url, idempotency_key: `calendar-carousel-${$execution.id}` } }];"
      },
      "id": "prepare",
      "name": "Prepare slideshow payload",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [200, 120]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://videotok.app/api/v1/slideshows",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{$vars.VIDEOTOK_API_KEY}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Idempotency-Key",
              "value": "={{$json.idempotency_key}}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\"title\":$json.title,\"prompt\":$json.prompt,\"slide_count\":6,\"aspect_ratio\":\"4:5\",\"product_image_urls\":[$json.product_image_url],\"brand_kit_id\":$vars.VIDEOTOK_BRAND_KIT_ID,\"language\":\"English\",\"product_context\":$json.product_context,\"generate_text_overlays\":false,\"idempotency_key\":$json.idempotency_key}"
      },
      "id": "create-slideshow",
      "name": "Create Videotok carousel",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [500, 120]
    },
    {
      "parameters": {
        "amount": 120,
        "unit": "seconds"
      },
      "id": "wait",
      "name": "Wait before polling",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [760, 120]
    },
    {
      "parameters": {
        "url": "=https://videotok.app/api/v1/slideshows/{{$json.slideshow_id}}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{$vars.VIDEOTOK_API_KEY}}"
            }
          ]
        }
      },
      "id": "poll",
      "name": "Poll slideshow status",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [1020, 120]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "caption",
              "name": "suggested_caption",
              "type": "string",
              "value": "={{$('Prepare slideshow payload').first().json.caption}}"
            },
            {
              "id": "url",
              "name": "videotok_url",
              "type": "string",
              "value": "={{$json.url}}"
            }
          ]
        }
      },
      "id": "handoff",
      "name": "Write back to calendar",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [1280, 120]
    }
  ],
  "connections": {
    "Check approved calendar rows": {
      "main": [
        [{ "node": "Replace with calendar row", "type": "main", "index": 0 }]
      ]
    },
    "Replace with calendar row": {
      "main": [
        [{ "node": "OpenAI writes carousel brief", "type": "main", "index": 0 }]
      ]
    },
    "OpenAI writes carousel brief": {
      "main": [
        [{ "node": "Prepare slideshow payload", "type": "main", "index": 0 }]
      ]
    },
    "Prepare slideshow payload": {
      "main": [
        [{ "node": "Create Videotok carousel", "type": "main", "index": 0 }]
      ]
    },
    "Create Videotok carousel": {
      "main": [[{ "node": "Wait before polling", "type": "main", "index": 0 }]]
    },
    "Wait before polling": {
      "main": [
        [{ "node": "Poll slideshow status", "type": "main", "index": 0 }]
      ]
    },
    "Poll slideshow status": {
      "main": [
        [{ "node": "Write back to calendar", "type": "main", "index": 0 }]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "tags": ["videotok", "openai", "content calendar", "carousel"],
  "active": false,
  "versionId": "videotok-social-calendar-carousel-machine-1"
}
