{
  "name": "Show every product on every body type - size-inclusive product imagery",
  "nodes": [
    {
      "parameters": {
        "jsCode": "// Looks up each talent name from \"Body Builds 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 — set body builds + talents in \"Body Builds To Generate\".\nconst config = $('Body Builds To Generate').first().json;\nconst talents = $('Get Talents').all().map(i => i.json);\nconst out = [];\nconst missing = [];\nfor (const [label, rawName] of Object.entries(config)) {\n  const name = String(rawName || '').trim();\n  if (!name) continue; // empty row = skip this body build\n  const t = talents.find(x => (x.name || '').trim().toLowerCase() === name.toLowerCase());\n  if (t) out.push({ json: { label, name: t.name, talentId: t.talentId, gender: t.gender } });\n  else missing.push(`${label} (${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 \"Body Builds 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": "0271173f-9e86-42b2-b5f0-5775fd1b168e",
      "name": "Match Talents in Library",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        672,
        0
      ]
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "text": "=Every-size set complete - {{ ($('Collect Results').first().json.data || []).filter(r => r.url).length }} shot(s) appended to the product in Shopify.",
        "otherOptions": {}
      },
      "id": "a35fff3d-50b7-4fae-ba45-b40f845f83dd",
      "name": "Post Summary to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [
        1120,
        -160
      ]
    },
    {
      "parameters": {
        "authentication": "apiKey",
        "talentId": "={{ $json.talentId }}",
        "shotCodes": [
          "model_front_women"
        ],
        "frontImageUrl": "={{ $('Product Updated').first().json.images[0].src }}",
        "outputFormat": "jpeg",
        "outputAspectRatio": 5,
        "callbackUrl": "={{ $execution.resumeUrl }}",
        "operation": "generateModelShots"
      },
      "id": "594bc36f-e3ec-409a-a4c8-810443d8eb65",
      "name": "Generate Shot",
      "type": "n8n-nodes-dreem.dreem",
      "typeVersion": 1,
      "position": [
        1120,
        128
      ]
    },
    {
      "parameters": {
        "resume": "webhook",
        "httpMethod": "POST",
        "limitWaitTime": true,
        "resumeAmount": 20,
        "resumeUnit": "minutes",
        "options": {}
      },
      "id": "b76f6ebe-f2c2-43e4-bc82-21d8be037cd0",
      "name": "Wait for Render",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1344,
        128
      ]
    },
    {
      "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": "8132f373-3fbb-457a-8199-930c8c067e4c",
      "name": "Is Complete?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1568,
        128
      ]
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "text": "=*{{ $('Loop Body Builds').item.json.label }}* ({{ $('Loop Body Builds').item.json.name }}): {{ $('Wait for Render').item.json.body.outputs[0].files[0].url }}",
        "otherOptions": {}
      },
      "id": "5b8032ee-e030-4dbe-b585-cdcffef17747",
      "name": "Post Shot to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [
        1792,
        48
      ]
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "2582c5de-fe3a-4bd3-ab6c-c7c3f841dd1d",
      "name": "Loop Body Builds",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        880,
        0
      ]
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "text": "=*{{ $('Loop Body Builds').item.json.label }}* timed out — skipping.",
        "otherOptions": {}
      },
      "id": "d120a623-132a-4658-9cad-a0eb47eb25e7",
      "name": "Post Timeout to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [
        1792,
        224
      ]
    },
    {
      "parameters": {
        "content": "## 🟡 See it in every size\nTag a Shopify product **`every-size`** and this workflow generates that garment on a talent for every body build you've picked (Very Slim → Plus). Every finished shot is posted to Slack — one post per body build — 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-size?** — only products tagged `every-size` (and with at least one image — the first image is the garment input) continue.\n3. **Body Builds To Generate** — 👈 the only node you edit. One row per body build (the same builds you can filter by in Dreem.ai): the field name is the label shown in Slack, the value is the talent name.\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 body build, 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-size-done`, so the workflow can't re-trigger itself. No manual tag removal needed. (Each shot also posts to Slack as it completes, plus a summary at the end.)\n\n**Browse talents and poses in [Dreem.ai](https://dreem.ai)** — filter talents by *Body Build*, copy their names into *Body Builds To Generate*, and paste 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: set your body builds + talents → check the pose → toggle **Active** → tag a product `every-size`.",
        "height": 880,
        "width": 760,
        "color": 7
      },
      "id": "f25436e5-0838-4f3b-9c9c-692616082227",
      "name": "Setup — Start Here",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -336,
        -960
      ]
    },
    {
      "parameters": {
        "authentication": "accessToken",
        "topic": "products/update"
      },
      "id": "caad39a9-35ed-47bf-8c1e-0cfd984e99af",
      "name": "Product Updated",
      "type": "n8n-nodes-base.shopifyTrigger",
      "typeVersion": 1,
      "position": [
        -224,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": false,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 1
          },
          "conditions": [
            {
              "leftValue": "={{ ($json.tags || '').split(',').map(t => t.trim()).includes('every-size') }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            },
            {
              "leftValue": "={{ ($json.images || []).length }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "609d9d8c-ee5c-4198-a853-27c6b652061a",
      "name": "Tagged every-size?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "content": "### ✏️ Pick your body builds & talents here\nOpen **Body Builds To Generate** — one row per body build:\n\n- **Field name** = the body build (shown in Slack): Very Slim, Slim, Lean, Athletic, Average, Plus — the same values as the *Body Build* filter in [Dreem.ai](https://dreem.ai)\n- **Value** = the talent name, e.g. `Ana Ferreira`\n\nThe *Athletic* row ships empty — add a talent name to include it, or leave any row empty to skip that build. Names must match your Dreem Library exactly. A name that isn't found is skipped and listed in the execution log; the rest still run.",
        "height": 360,
        "width": 440,
        "color": 5
      },
      "id": "21a14a83-b03f-4d8c-b2aa-1855beb14f3e",
      "name": "Note — Pick Sizes & Talents",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        160,
        288
      ]
    },
    {
      "parameters": {
        "authentication": "apiKey",
        "resource": "library",
        "talentPageSize": 500,
        "operation": "getAvailableTalents"
      },
      "id": "6f0f5f05-fc36-469b-9460-13ef9d0b3a05",
      "name": "Get Talents",
      "type": "n8n-nodes-dreem.dreem",
      "typeVersion": 1,
      "position": [
        448,
        0
      ]
    },
    {
      "parameters": {
        "content": "### ⚙️ Generate Shot — set per use\nPick the **pose** (shot code), **aspect ratio** and **output format** on this 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 style every body build the same way, which keeps the set comparable. Leave empty and Dreem styles the outfit itself.",
        "height": 300,
        "width": 460,
        "color": 3
      },
      "id": "d421e288-e0f0-4df2-b2dd-2cf346cf8843",
      "name": "Note — Generate Settings",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1040,
        416
      ]
    },
    {
      "id": "14243520-aeb2-4981-8bbc-fc32374d6555",
      "name": "Body Builds To Generate",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        220,
        0
      ],
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "bb-veryslim",
              "name": "Very Slim",
              "value": "Elsa Blomqvist",
              "type": "string"
            },
            {
              "id": "bb-slim",
              "name": "Slim",
              "value": "Astrid Holm",
              "type": "string"
            },
            {
              "id": "bb-lean",
              "name": "Lean",
              "value": "Ana Ferreira",
              "type": "string"
            },
            {
              "id": "bb-athletic",
              "name": "Athletic",
              "value": "",
              "type": "string"
            },
            {
              "id": "bb-average",
              "name": "Average",
              "value": "Camila Ortiz",
              "type": "string"
            },
            {
              "id": "bb-plus",
              "name": "Plus",
              "value": "Ebba Nordström",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": false,
        "options": {}
      },
      "notes": "One row per body build (same values as the Body Build filter in Dreem.ai): field name = build label (shown in Slack), value = talent name. Leave a row empty to skip that build."
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "rs-label",
              "name": "label",
              "value": "={{ $('Loop Body Builds').item.json.label }}",
              "type": "string"
            },
            {
              "id": "rs-name",
              "name": "name",
              "value": "={{ $('Loop Body Builds').item.json.name }}",
              "type": "string"
            },
            {
              "id": "rs-url",
              "name": "url",
              "value": "={{ $('Wait for Render').item.json.body.outputs[0].files[0].url }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "export-record-result",
      "name": "Record Result",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2016,
        48
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "ts-label",
              "name": "label",
              "value": "={{ $('Loop Body Builds').item.json.label }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "export-record-timeout",
      "name": "Record Timeout",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2016,
        224
      ]
    },
    {
      "parameters": {
        "aggregate": "aggregateAllItemData",
        "options": {}
      },
      "id": "export-collect-results",
      "name": "Collect Results",
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        1056,
        -320
      ]
    },
    {
      "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-size').concat(['every-size-done']).join(', ') }}"
        }
      },
      "id": "export-publish-shopify",
      "name": "Publish to Shopify",
      "type": "n8n-nodes-base.shopify",
      "typeVersion": 1,
      "position": [
        1280,
        -320
      ]
    }
  ],
  "connections": {
    "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
          }
        ]
      ]
    },
    "Product Updated": {
      "main": [
        [
          {
            "node": "Tagged every-size?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Match Talents in Library": {
      "main": [
        [
          {
            "node": "Loop Body Builds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Talents": {
      "main": [
        [
          {
            "node": "Match Talents in Library",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tagged every-size?": {
      "main": [
        [
          {
            "node": "Body Builds To Generate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Body Builds To Generate": {
      "main": [
        [
          {
            "node": "Get Talents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Body Builds": {
      "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
          }
        ]
      ]
    },
    "Post Shot to Slack": {
      "main": [
        [
          {
            "node": "Record Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post Timeout to Slack": {
      "main": [
        [
          {
            "node": "Record Timeout",
            "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
          }
        ]
      ]
    },
    "Record Result": {
      "main": [
        [
          {
            "node": "Loop Body Builds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Record Timeout": {
      "main": [
        [
          {
            "node": "Loop Body Builds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  }
}