{
  "name": "Turn Shopify products into Veo 3.1 videos for TikTok, Instagram Reels and YouTube Shorts",
  "nodes": [
    {
      "id": "99d0f2d8-b783-4726-a853-52384f46e38d",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -647,
        -97
      ],
      "parameters": {
        "width": 520,
        "height": 833,
        "content": "## Turn every new Shopify product into a short video\nWhen a product is added to your Shopify store, Gemini looks at its photo, Veo 3.1 renders an 8-second vertical product video, and PostWire posts it to TikTok, Instagram Reels and YouTube Shorts with a caption written for each network.\n\n### How it works\n1. **New product in Shopify** fires when a product is created. **Active with a photo** drops drafts, and **Skip products already filmed** ignores one handled before.\n2. **Look at the product photo** (Gemini) describes what it sees and writes the Veo prompt and one selling point. **Build the brief** adds the name, price and product link.\n3. **Veo 3.1 render** generates the clip in 9:16.\n4. **Post to TikTok, Reels and Shorts** (PostWire) uploads the file, writes a caption per network and publishes.\n5. **Log the video** adds a row per network with its link. A refusal goes to **Log the failure**, and **Email me the fix** sends what to fix.\n\n### Setup\n1. Install the verified **PostWire** node (search \"PostWire\" in the nodes panel), create an account at postwire.io, connect your networks and paste your API key into a **PostWire API** credential.\n2. Add a Shopify access token credential to the trigger, and your store address and email in **Store settings**.\n3. Add a Google Gemini credential to both Gemini nodes. Veo needs billing enabled in Google AI Studio (about one dollar per 8-second Veo 3.1 Fast clip).\n4. Create a sheet with the columns date, product, network, status, link and note, and pick it in both Sheets nodes. Connect Gmail.\n\n### Customize\nChange the visual style in Store settings, add facebook to the PostWire node (the free PostWire plan posts to 2 networks per post), or switch the trigger to Product Updated."
      },
      "typeVersion": 1
    },
    {
      "id": "6dec91b8-86b2-495a-a35d-ff3e54555de6",
      "name": "Sticky Note 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -67,
        -97
      ],
      "parameters": {
        "color": 7,
        "width": 1155,
        "height": 271,
        "content": "## 1. New product\nOnly active products with a photo, each one filmed once."
      },
      "typeVersion": 1
    },
    {
      "id": "f982e9c5-d27e-4a43-aa2b-5ad2f87c242a",
      "name": "Sticky Note 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1123,
        -97
      ],
      "parameters": {
        "color": 7,
        "width": 520,
        "height": 271,
        "content": "## 2. Read the photo with Gemini\nGemini describes the real product and writes the Veo prompt."
      },
      "typeVersion": 1
    },
    {
      "id": "ce3a6adb-112d-4869-aae4-be1a14032c2a",
      "name": "Sticky Note 4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -16,
        431
      ],
      "parameters": {
        "color": 7,
        "width": 518,
        "height": 295,
        "content": "## 3. Render and post\nVeo 3.1 renders the clip; PostWire uploads it and writes a caption per network."
      },
      "typeVersion": 1
    },
    {
      "id": "12cee3be-de85-4d2b-a6cb-4d3df5e8a1f4",
      "name": "Sticky Note 5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        560,
        215
      ],
      "parameters": {
        "color": 7,
        "width": 786,
        "height": 751,
        "content": "## 4. Log and alert\nA row per network with its link, or the fix in the sheet and in one email."
      },
      "typeVersion": 1
    },
    {
      "id": "8e9ad634-8703-4e1a-abdc-926b5272f100",
      "name": "New product in Shopify",
      "type": "n8n-nodes-base.shopifyTrigger",
      "position": [
        0,
        0
      ],
      "parameters": {
        "topic": "products/create",
        "authentication": "accessToken"
      },
      "typeVersion": 1
    },
    {
      "id": "90f62a95-97b1-4651-ae60-4b6bfb9910d7",
      "name": "Store settings",
      "type": "n8n-nodes-base.set",
      "position": [
        300,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "fea4a2f0-e818-44d2-a743-8f8b2bdd7dc9",
              "name": "store_url",
              "type": "string",
              "value": "https://your-store.com"
            },
            {
              "id": "3861afeb-068f-4085-a7f5-43331aabfdd3",
              "name": "email",
              "type": "string",
              "value": "you@example.com"
            },
            {
              "id": "5ad69e3a-630e-4b0c-aac0-2fe0317a8bb3",
              "name": "style",
              "type": "string",
              "value": "Clean studio look, soft daylight, slow camera move, vertical 9:16, no text on screen"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "41764409-ab2f-479d-ad0c-bc480b833a51",
      "name": "Active with a photo",
      "type": "n8n-nodes-base.filter",
      "position": [
        600,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "31da078a-413a-4ed3-acfa-78a7fde8f7d7",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('New product in Shopify').item.json.status }}",
              "rightValue": "active"
            },
            {
              "id": "50be3505-924f-4e9f-a0e1-b1b12eb59d6b",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $('New product in Shopify').item.json.image ? $('New product in Shopify').item.json.image.src : '' }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ae6f284a-5ca3-47ed-acb3-66bc0428546e",
      "name": "Skip products already filmed",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        900,
        0
      ],
      "parameters": {
        "options": {},
        "operation": "removeItemsSeenInPreviousExecutions",
        "dedupeValue": "={{ $('New product in Shopify').item.json.id }}"
      },
      "typeVersion": 2
    },
    {
      "id": "6d899182-b889-4a82-a3d1-88da10110af0",
      "name": "Look at the product photo",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        1200,
        0
      ],
      "parameters": {
        "text": "={{ 'This is the photo of a product that was just added to an online store.\\nName: ' + $('New product in Shopify').item.json.title + '\\nDescription: ' + String($('New product in Shopify').item.json.body_html || '').replace(/<[^>]+>/g, ' ').replace(/\\s+/g, ' ').slice(0, 600) + '\\nVisual style: ' + $('Store settings').item.json.style + '\\n\\nReturn only JSON with two keys. veo_prompt: one paragraph for an 8-second vertical product video made with Veo: describe the product exactly as it looks in the photo (shape, colour, material), where it sits, one simple action or camera move, the light and the ambient sound; no on-screen text, no logos, no people. selling_point: one sentence a shopper would care about, using only facts from the name, the description and the photo.' }}",
        "modelId": {
          "__rl": true,
          "mode": "id",
          "value": "models/gemini-2.5-flash"
        },
        "options": {
          "maxOutputTokens": 700
        },
        "resource": "image",
        "imageUrls": "={{ $('New product in Shopify').item.json.image.src }}",
        "inputType": "url",
        "operation": "analyze"
      },
      "typeVersion": 1.2
    },
    {
      "id": "093332a3-4368-496a-a772-fabfc8870e42",
      "name": "Build the brief",
      "type": "n8n-nodes-base.set",
      "position": [
        1500,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "dbd947ff-07e6-4f0f-a735-628d4e5d9ecb",
              "name": "plan",
              "type": "object",
              "value": "={{ (() => { const t = ($json.content?.parts || []).map(p => p.text || '').join(''); const m = t.match(/\\{[\\s\\S]*\\}/); try { return JSON.parse(m ? m[0] : t); } catch (e) { return { veo_prompt: t, selling_point: '' }; } })() }}"
            },
            {
              "id": "7ec8ae12-08a8-4602-a94b-69d8b3996575",
              "name": "product",
              "type": "string",
              "value": "={{ $('New product in Shopify').item.json.title }}"
            },
            {
              "id": "6da258be-2a2f-4a60-ab59-48d49756c489",
              "name": "price",
              "type": "string",
              "value": "={{ $('New product in Shopify').item.json.variants?.[0]?.price || '' }}"
            },
            {
              "id": "e9751589-32ca-47b8-a295-bd7dfaa5b12c",
              "name": "product_url",
              "type": "string",
              "value": "={{ $('Store settings').item.json.store_url.replace(/\\/$/, '') + '/products/' + $('New product in Shopify').item.json.handle }}"
            },
            {
              "id": "e393dc09-25fe-4375-a5cc-451d88faa605",
              "name": "product_id",
              "type": "string",
              "value": "={{ $('New product in Shopify').item.json.id }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8de64bf3-b3c1-4be8-a0f2-9bf11ab16601",
      "name": "Veo 3.1 render",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        0,
        552
      ],
      "parameters": {
        "prompt": "={{ $json.plan.veo_prompt }}",
        "modelId": {
          "__rl": true,
          "mode": "id",
          "value": "models/veo-3.1-fast-generate-preview"
        },
        "options": {
          "aspectRatio": "9:16",
          "durationSeconds": 8
        },
        "resource": "video",
        "returnAs": "video",
        "operation": "generate"
      },
      "typeVersion": 1.2
    },
    {
      "id": "d2c68efc-f4e5-46a4-aea5-4b014e97b1c4",
      "name": "Post to TikTok, Reels and Shorts",
      "type": "n8n-nodes-postwire.postWire",
      "onError": "continueRegularOutput",
      "position": [
        300,
        552
      ],
      "parameters": {
        "prompt": "={{ (() => { const b = $('Build the brief').item.json; return 'New in the shop: ' + b.product + '. ' + (b.plan.selling_point || '') + (b.price ? ' Price: ' + b.price + '.' : '') + ' ' + b.product_url; })() }}",
        "options": {
          "title": "={{ String($('Build the brief').item.json.product).slice(0, 80) + ' #Shorts' }}",
          "privacy": "public",
          "mediaType": "video",
          "idempotencyKey": "={{ 'shopify-video-' + $('Build the brief').item.json.product_id }}"
        },
        "resource": "post",
        "operation": "publish",
        "platforms": [
          "tiktok",
          "instagram",
          "youtube"
        ],
        "contentMode": "smart",
        "mediaSource": "binary",
        "binaryPropertyName": "data"
      },
      "typeVersion": 2
    },
    {
      "id": "1a0e68d1-2565-4951-ab06-7cdbcb9ff196",
      "name": "Published?",
      "type": "n8n-nodes-base.if",
      "position": [
        600,
        552
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "18e405e3-9ca4-4a5d-a602-cc4c709fb336",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.ok }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "07d81dd4-9ac2-4cc8-a23a-7e53b8858f4a",
      "name": "Log the video",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        900,
        311.99999999999994
      ],
      "parameters": {
        "columns": {
          "value": {
            "date": "={{ $now.toFormat('yyyy-MM-dd HH:mm') }}",
            "link": "={{ $json.url || 'processing: the link appears in PostWire' }}",
            "note": "",
            "status": "published",
            "network": "={{ $json.platform }}",
            "product": "={{ $('Build the brief').item.json.product }}"
          },
          "schema": [
            {
              "id": "date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "product",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "product",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "network",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "network",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "link",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "note",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "note",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "f07a41aa-8130-480e-a9f9-0a9d502d555b",
      "name": "Log the failure",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        900,
        552
      ],
      "parameters": {
        "columns": {
          "value": {
            "date": "={{ $now.toFormat('yyyy-MM-dd HH:mm') }}",
            "link": "",
            "note": "={{ $json.error }} {{ $json.hint || $json.description || '' }}",
            "status": "failed",
            "network": "={{ $json.platform || 'all' }}",
            "product": "={{ $('Build the brief').item.json.product }}"
          },
          "schema": [
            {
              "id": "date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "product",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "product",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "network",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "network",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "link",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "note",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "note",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "e0b2e2d9-a0be-4e59-a9e6-b12a85aac507",
      "name": "Collect the failures",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        900,
        792
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "failures"
      },
      "typeVersion": 1
    },
    {
      "id": "5b28d98e-42cb-4d71-a584-993554586a7e",
      "name": "Email me the fix",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1200,
        792
      ],
      "parameters": {
        "sendTo": "={{ $('Store settings').first().json.email }}",
        "message": "={{ 'Product: ' + $('Build the brief').first().json.product + '\\n' + $('Build the brief').first().json.product_url + '\\n\\nWhat happened and how to fix it:\\n' + $json.failures.map(f => '- ' + (f.platform || 'all networks') + ': ' + f.error + (f.hint || f.description ? '\\n  ' + (f.hint || f.description) : '')).join('\\n') }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{ 'Product video not posted everywhere: ' + $('Build the brief').first().json.product }}",
        "emailType": "text"
      },
      "typeVersion": 2.1
    }
  ],
  "connections": {
    "Published?": {
      "main": [
        [
          {
            "node": "Log the video",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log the failure",
            "type": "main",
            "index": 0
          },
          {
            "node": "Collect the failures",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store settings": {
      "main": [
        [
          {
            "node": "Active with a photo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Veo 3.1 render": {
      "main": [
        [
          {
            "node": "Post to TikTok, Reels and Shorts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build the brief": {
      "main": [
        [
          {
            "node": "Veo 3.1 render",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Active with a photo": {
      "main": [
        [
          {
            "node": "Skip products already filmed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Collect the failures": {
      "main": [
        [
          {
            "node": "Email me the fix",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "New product in Shopify": {
      "main": [
        [
          {
            "node": "Store settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Look at the product photo": {
      "main": [
        [
          {
            "node": "Build the brief",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip products already filmed": {
      "main": [
        [
          {
            "node": "Look at the product photo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post to TikTok, Reels and Shorts": {
      "main": [
        [
          {
            "node": "Published?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "active": false
}
