{
  "name": "Show your products on models of every age (20s → 60s+)",
  "nodes": [
    {
      "parameters": {
        "authentication": "oAuth2",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "",
          "mode": "name"
        },
        "text": "=Every-age set complete - {{ ($('Collect Results').first().json.data || []).filter(r => r.url).length }} shot(s) appended to the product in Shopify.",
        "otherOptions": {}
      },
      "id": "029e83e1-3e3b-42dd-b000-c6f864025cef",
      "name": "Post Summary to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [
        3040,
        176
      ]
    },
    {
      "parameters": {
        "authentication": "apiKey",
        "talentId": "={{ $json.talentId }}",
        "shotCodes": [
          "model_front_women"
        ],
        "frontImageUrl": "={{ $('Product Updated').first().json.images[0].src }}",
        "stylingImageUrls": {
          "imageValues": [
            {
              "url": "https://cdn.thewirecutter.com/wp-content/media/2025/05/BEST-SANDALS-_DSC0203-2.jpg"
            }
          ]
        },
        "outputFormat": "jpeg",
        "outputAspectRatio": 5,
        "callbackUrl": "={{ $execution.resumeUrl }}"
      },
      "id": "41631504-e8fc-4e6f-91b9-e2e49bebc631",
      "name": "Generate Shot",
      "type": "n8n-nodes-dreem.dreem",
      "typeVersion": 1,
      "position": [
        2624,
        464
      ]
    },
    {
      "parameters": {
        "resume": "webhook",
        "httpMethod": "POST",
        "limitWaitTime": true,
        "resumeAmount": 20,
        "resumeUnit": "minutes",
        "options": {}
      },
      "id": "ed5fe4f8-9421-4925-943e-8547ce6db066",
      "name": "Wait for Render",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        2848,
        464
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 1
          },
          "conditions": [
            {
              "leftValue": "={{ $json.body?.status ?? $json.status }}",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "rightValue": "completed"
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "b5b86ab8-d3f6-4e32-acdc-dc417f83a24c",
      "name": "Is Complete?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        3072,
        464
      ]
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "",
          "mode": "name"
        },
        "text": "=*{{ $('Loop Talents').item.json.name }}*: {{ $('Wait for Render').item.json.body.outputs[0].files[0].url }}",
        "otherOptions": {}
      },
      "id": "9ac84489-1083-4e35-80e5-259d6e6146f5",
      "name": "Post Shot to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [
        3328,
        352
      ]
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "6441721a-c75e-493c-a5ef-96bc5055d52c",
      "name": "Loop Talents",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        2384,
        368
      ]
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "",
          "mode": "name"
        },
        "text": "=*{{ $('Loop Talents').item.json.name }}* timed out - skipping.",
        "otherOptions": {}
      },
      "id": "2cc8fb2e-cbd1-4b4d-a0bc-5f093ba6e0c3",
      "name": "Post Timeout to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [
        3328,
        576
      ]
    },
    {
      "parameters": {
        "content": "## 🟡 Made for Every Age\nTag a Shopify product **`every-age`** and this workflow generates that garment on every talent you've picked — for example one per age group (20s → 60s+). Every finished shot is posted to Slack, and all shots are appended to the product's images in Shopify.\n\n**How it works, step by step**\n1. **Product Updated** — fires whenever a product changes in Shopify.\n2. **Tagged every-age?** — only products tagged `every-age` (and with at least one image — the first image is the garment input) continue.\n3. **Talents To Generate** — 👈 the only node you edit. Type the talent names you want, comma-separated.\n4. **Get Talents → Match Talents in Library** — your names are looked up in your live Dreem Library, so there are no IDs to copy. A misspelled or missing name is skipped; the rest still run.\n5. **Generate Shot → Wait for Render** — one image per talent, using the pose set on the Generate node.\n6. **Publish to Shopify** — appends the finished shots to the product and swaps the tag to `every-age-done`, so the workflow can't re-trigger itself. No manual tag removal needed.\n\n**Browse talents and poses in [Dreem.ai](https://dreem.ai)** — copy talent names into *Talents To Generate* and the pose's shot code into *Generate Shot*.\n\n### Setup — create your credentials (Settings → Credentials)\nThe template ships **without credentials** — create these three, then select them on the nodes:\n- **Dreem API** — your `dreem_pk_...` key → select on **Generate Shot** + **Get Talents**\n- **Shopify Access Token API** — your shop domain + Admin API token (`read_products` + `write_products`) → select on **Product Updated** + **Publish to Shopify**\n- **Slack** — create a **Slack OAuth2** credential (on n8n Cloud just press *Connect*) → select on **Post Shot to Slack**, **Post Summary to Slack** + **Post Timeout to Slack**, and pick **your own channel** on each. (Prefer a bot token? Use a *Slack API* credential and switch each Slack node's **Authentication** to *Access Token*.)\n\nThen: write your talent names → check the pose → toggle **Active** → tag a product `every-age`.",
        "height": 900,
        "width": 800,
        "color": 7
      },
      "id": "a686f337-aff6-43fc-8b56-f73ce2ed50fe",
      "name": "Setup — Start Here",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "authentication": "accessToken",
        "topic": "products/update"
      },
      "id": "20717b92-1be4-4664-b8a0-e0115c8d3bb0",
      "name": "Product Updated",
      "type": "n8n-nodes-base.shopifyTrigger",
      "typeVersion": 1,
      "position": [
        944,
        336
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "leftValue": "={{ ($json.tags || '').split(',').map(t => t.trim()).includes('every-age') }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "id": "03eb65b5-10a5-402c-ac37-061c369d09eb"
            },
            {
              "leftValue": "={{ ($json.images || []).length }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "id": "228f6845-8439-415a-b7f0-0dbfa9b8ae4e"
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "44a17586-558f-432f-8b79-fed586b1358b",
      "name": "Tagged every-age?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1168,
        336
      ]
    },
    {
      "parameters": {
        "authentication": "apiKey",
        "resource": "library",
        "talentKeyword": "=",
        "talentPageNumber": "={{ $json.page }}",
        "talentPageSize": 100
      },
      "id": "f7d87f16-a470-4982-bd49-6753c61fc9dc",
      "name": "Get Talents",
      "type": "n8n-nodes-dreem.dreem",
      "typeVersion": 1,
      "position": [
        1984,
        336
      ]
    },
    {
      "parameters": {
        "jsCode": "// Looks up each name from \"Talents To Generate\" in your live Dreem\n// Library (pulled by \"Get Talents\") — no talent IDs needed anywhere.\n// 👉 You normally don't edit this node — pick talents in \"Talents To Generate\".\nconst raw = $('Talents To Generate').first().json.talentNames || '';\nconst wanted = raw.split(/[,\\n]/).map(s => s.trim()).filter(Boolean);\n\nconst talents = $input.all().map(i => i.json);\nconst out = [];\nconst missing = [];\nfor (const name of wanted) {\n  const t = talents.find(x => (x.name || '').trim().toLowerCase() === name.toLowerCase());\n  if (t) out.push({ json: { name: t.name, talentId: t.talentId, gender: t.gender } });\n  else missing.push(name);\n}\nif (missing.length) console.log(`Not found in your Dreem Library (skipped): ${missing.join(', ')}`);\nif (!out.length) {\n  throw new Error('None of the names in \"Talents To Generate\" were found in your Dreem Library. Check the spelling against Dreem.ai. Available: ' + talents.map(x => x.name).join(', '));\n}\nreturn out;"
      },
      "id": "c8b208ab-02a5-4673-9c27-f7a06f98ff7b",
      "name": "Match Talents in Library",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2176,
        336
      ]
    },
    {
      "parameters": {
        "content": "### ⚙️ Generate Shot — set per use\nPick the **pose** (shot code), **aspect ratio** and **output format** on the Generate Shot node.\n\n- Browse all poses in [Dreem.ai](https://dreem.ai) and paste the pose's shot code here. Poses are gender-specific — pick one that matches the talents you selected.\n- **Styling Image URLs** (optional): add garment image URLs to give every talent the same look, which keeps the set comparable. Leave empty and Dreem styles the outfit itself.",
        "height": 364,
        "width": 532,
        "color": 3
      },
      "id": "5c35a9ef-d55a-4fed-bf36-58bf65e674dc",
      "name": "Note — Generate Settings",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2080,
        784
      ]
    },
    {
      "parameters": {
        "content": "### ✏️ Pick your talents here\nOpen **Talents To Generate** and type the names, comma-separated:\n\n`Astrid Holm, Araceli Navarro, Dominique Laurent, …`\n\n- Names must match your Dreem Library exactly — browse all talents in [Dreem.ai](https://dreem.ai) and copy the names from there.\n- Any mix of ages, as many as you like — one shot is generated per talent. Keep every talent the same gender as the pose on *Generate Shot* (the default pose is a women's pose — switch it there if you pick male talents).\n- A name that isn't found is skipped and listed in the execution log; the rest still run.",
        "height": 548,
        "width": 644,
        "color": 5
      },
      "id": "b8a0fd03-92f6-4a1f-a908-aedc06ab08dd",
      "name": "Note — Pick Talents",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1264,
        544
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "r-name",
              "name": "name",
              "value": "={{ $('Loop Talents').item.json.name }}",
              "type": "string"
            },
            {
              "id": "r-url",
              "name": "url",
              "value": "={{ $('Wait for Render').item.json.body.outputs[0].files[0].url }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "370959d0-d2d2-4752-9c62-ec5ab3226025",
      "name": "Record Result",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        3552,
        352
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "t-name",
              "name": "name",
              "value": "={{ $('Loop Talents').item.json.name }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "9b07c61e-c1e9-4417-a64a-ca63b648ee57",
      "name": "Record Timeout",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        3552,
        576
      ]
    },
    {
      "parameters": {
        "aggregate": "aggregateAllItemData",
        "options": {}
      },
      "id": "a4b722c0-cae1-4d0c-baf1-edc88362ab93",
      "name": "Collect Results",
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        2592,
        176
      ]
    },
    {
      "parameters": {
        "authentication": "accessToken",
        "resource": "product",
        "operation": "update",
        "productId": "={{ $('Product Updated').first().json.id }}",
        "updateFields": {
          "images": "={{ ($('Product Updated').first().json.images || []).map(i => ({ id: i.id })).concat(($json.data || []).filter(r => r.url).map(r => ({ src: r.url }))) }}",
          "tags": "={{ ($('Product Updated').first().json.tags || '').split(',').map(t => t.trim()).filter(t => t && t !== 'every-age').concat(['every-age-done']).join(', ') }}"
        }
      },
      "id": "cdb7a6c8-feed-48ec-aa70-88573919d0a2",
      "name": "Publish to Shopify",
      "type": "n8n-nodes-base.shopify",
      "typeVersion": 1,
      "position": [
        2816,
        176
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "e3df2527-7565-4713-886e-a4ef653d5625",
              "name": "talentNames",
              "value": "Freydis Holme, Elise Vautour, Thalia Lindquist, Margot Beaumont",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "27a30189-2bf3-4f03-b9ff-7995c08fe330",
      "name": "Talents To Generate",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1376,
        336
      ],
      "notes": "Type the talent names you want, comma-separated. Names must match your Dreem Library — browse all talents in Dreem.ai."
    },
    {
      "parameters": {
        "jsCode": "return Array.from({length: 5}, (_, i) => ({ json: { page: i + 1 } }));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1584,
        336
      ],
      "id": "e45d1223-7496-4c42-a9af-4516f619e2e7",
      "name": "Page List"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        1792,
        336
      ],
      "id": "b096e980-09c7-4b3a-9ee6-0d72b9194073",
      "name": "Loop Over Items"
    }
  ],
  "connections": {
    "Product Updated": {
      "main": [
        [
          {
            "node": "Tagged every-age?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Talents": {
      "main": [
        [
          {
            "node": "Match Talents in Library",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Match Talents in Library": {
      "main": [
        [
          {
            "node": "Loop Talents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Talents": {
      "main": [
        [
          {
            "node": "Collect Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Generate Shot",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Shot": {
      "main": [
        [
          {
            "node": "Wait for Render",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for Render": {
      "main": [
        [
          {
            "node": "Is Complete?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Complete?": {
      "main": [
        [
          {
            "node": "Post Shot to Slack",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Post Timeout to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post Shot to Slack": {
      "main": [
        [
          {
            "node": "Record Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post Timeout to Slack": {
      "main": [
        [
          {
            "node": "Record Timeout",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Record Result": {
      "main": [
        [
          {
            "node": "Loop Talents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Record Timeout": {
      "main": [
        [
          {
            "node": "Loop Talents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Collect Results": {
      "main": [
        [
          {
            "node": "Publish to Shopify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Publish to Shopify": {
      "main": [
        [
          {
            "node": "Post Summary to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tagged every-age?": {
      "main": [
        [
          {
            "node": "Talents To Generate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Talents To Generate": {
      "main": [
        [
          {
            "node": "Page List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Page List": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "Get Talents",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}
