{
  "id": "iE2SE7NWuJsvGuTK",
  "name": "Create and publish merchandise to Shopify",
  "tags": [],
  "nodes": [
    {
      "id": "24364342-4a92-4347-a5f4-6e1f23edb472",
      "name": "Template Description",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        592,
        624
      ],
      "parameters": {
        "color": 2,
        "width": 540,
        "height": 640,
        "content": "# Create and publish merchandise from designs to Shopify\n\n## \ud83d\udca1 Who\u2019s it for\nE-commerce store owners, print-on-demand businesses, and artists looking to automate the creation and listing of merchandise based on design files.\n\n## \ud83d\ude80 How it works / What it does\n1. **Trigger**: Watches a Google Drive folder for new design files.\n2. **Analyze**: Uses OpenAI Vision to analyze the design (subject, mood) and suggest keywords.\n3. **Process**: Removes the background using Remove.bg and uploads the clean asset to Cloudinary.\n4. **Mockup**: Generates realistic product mockups (T-shirts, Tote bags) via Cloudinary transformations.\n5. **Content**: Uses OpenAI to write SEO-friendly product titles and descriptions.\n6. **Draft**: Creates a draft product in Shopify.\n7. **Approval**: Sends a Slack message with the mockup and details for human approval.\n8. **Publish**: Upon approval, publishes the product to Shopify and promotes it on Instagram and Pinterest.\n\n## \u2699\ufe0f How to set up\n1. Configure the **Workflow Configuration** node with your API keys and IDs (Cloudinary, Remove.bg, Slack, Instagram, Pinterest).\n2. Upload your base product images (e.g., blank white t-shirt) to Cloudinary and get their Public IDs. Update the configuration node.\n3. Connect your Google Drive, OpenAI, Shopify, and Slack credentials in the respective nodes.\n\n## \ud83d\udccb Requirements\n- **n8n** (Self-hosted or Cloud)\n- **Google Drive** account\n- **OpenAI API** key (GPT-4o access recommended)\n- **Remove.bg** API key\n- **Cloudinary** account\n- **Shopify** store\n- **Slack** workspace\n- **Instagram Business** & **Pinterest** accounts"
      },
      "typeVersion": 1
    },
    {
      "id": "52d3f39a-2049-4bc4-95c9-683106add607",
      "name": "Step 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1504,
        736
      ],
      "parameters": {
        "color": 7,
        "width": 216,
        "height": 112,
        "content": "### 1. Ingest & Config\nGet file from Drive and set global variables."
      },
      "typeVersion": 1
    },
    {
      "id": "50062d6e-33b8-46fe-a0ef-5fef8aa30047",
      "name": "Step 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1744,
        736
      ],
      "parameters": {
        "color": 7,
        "width": 188,
        "height": 144,
        "content": "### 2. Analyze & Process\nAnalyze image with AI, remove background, and upload to Cloudinary."
      },
      "typeVersion": 1
    },
    {
      "id": "8e68bade-ff04-4a7f-ba79-976c52b2f431",
      "name": "Step 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2672,
        736
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 128,
        "content": "### 3. Mockup & Copywriting\nGenerate mockups via Cloudinary and SEO text via OpenAI."
      },
      "typeVersion": 1
    },
    {
      "id": "faa31420-972a-4376-933d-b6573a25f47e",
      "name": "Step 4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3216,
        736
      ],
      "parameters": {
        "color": 7,
        "width": 180,
        "height": 128,
        "content": "### 4. Human Approval\nSend draft to Slack. Wait for button click."
      },
      "typeVersion": 1
    },
    {
      "id": "52d72e6d-f6d3-4040-9f81-3d7d128655d5",
      "name": "Step 5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3872,
        656
      ],
      "parameters": {
        "color": 7,
        "width": 200,
        "height": 112,
        "content": "### 5. Publish & Promote\nSet product to active. Post to Social Media."
      },
      "typeVersion": 1
    },
    {
      "id": "66c8d48d-e2a3-42c8-b7aa-a3e9904091b6",
      "name": "Google Drive Trigger - Design Upload1",
      "type": "n8n-nodes-base.googleDriveTrigger",
      "position": [
        1328,
        896
      ],
      "parameters": {
        "event": "fileCreated",
        "options": {
          "fileType": "all"
        },
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "triggerOn": "specificFolder",
        "folderToWatch": {
          "__rl": true,
          "mode": "id",
          "value": "<__PLACEHOLDER_VALUE__Google Drive Upload_Design Folder ID__>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "252c79a7-e255-400a-a7c0-697f98c788f9",
      "name": "Workflow Configuration1",
      "type": "n8n-nodes-base.set",
      "position": [
        1552,
        896
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "cloudinaryCloudName",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Cloudinary Cloud Name__>"
            },
            {
              "id": "id-2",
              "name": "cloudinaryApiKey",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Cloudinary API Key__>"
            },
            {
              "id": "id-3",
              "name": "cloudinaryApiSecret",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Cloudinary API Secret__>"
            },
            {
              "id": "id-4",
              "name": "removeBgApiKey",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Remove.bg API Key__>"
            },
            {
              "id": "id-5",
              "name": "tshirtWhitePublicId",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__White T-Shirt Base Image Public ID__>"
            },
            {
              "id": "id-6",
              "name": "tshirtBlackPublicId",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Black T-Shirt Base Image Public ID__>"
            },
            {
              "id": "id-7",
              "name": "toteBagPublicId",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Tote Bag Base Image Public ID__>"
            },
            {
              "id": "id-8",
              "name": "slackChannelId",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Slack Channel ID for Approvals__>"
            },
            {
              "id": "id-9",
              "name": "instagramBusinessAccountId",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Instagram Business Account ID__>"
            },
            {
              "id": "id-10",
              "name": "instagramAccessToken",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Instagram Access Token__>"
            },
            {
              "id": "id-11",
              "name": "pinterestAccessToken",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Pinterest Access Token__>"
            },
            {
              "id": "id-12",
              "name": "pinterestBoardId",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Pinterest Board ID__>"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "f28fa19d-e010-44dc-bf64-936d24671885",
      "name": "OpenAI Vision - Design Analysis1",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        1776,
        896
      ],
      "parameters": {
        "text": "Analyze this design image. Output the information in strictly valid JSON format:\n1. subject: The main subject of the design (e.g., cat, geometric pattern)\n2. mood: The mood/vibe (e.g., retro, punk, minimal)\n3. copyrightRisk: Copyright risk assessment (low/medium/high)\n4. recommendedProducts: Recommended product categories (Array: [\"tshirt\", \"bag\", \"hoodie\"])\n5. keywords: SEO keywords (Array)\n6. colorPalette: Main colors (Array)\n\nReturn ONLY the JSON.",
        "modelId": {
          "__rl": true,
          "mode": "id",
          "value": "gpt-4o"
        },
        "options": {},
        "resource": "image",
        "inputType": "base64",
        "operation": "analyze"
      },
      "typeVersion": 2
    },
    {
      "id": "0f68a43d-6f9b-440c-93d8-7ceb7b3c90e5",
      "name": "Remove.bg - Background Removal1",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2000,
        896
      ],
      "parameters": {
        "url": "https://api.remove.bg/v1.0/removebg",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        },
        "sendBody": true,
        "contentType": "multipart-form-data",
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "image_file_b64",
              "value": "={{ $binary.data.data.toString('base64') }}"
            },
            {
              "name": "size",
              "value": "auto"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Api-Key",
              "value": "={{ $('Workflow Configuration1').first().json.removeBgApiKey }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "424f2b5b-4119-43e4-bb8e-9ad2888ce48a",
      "name": "Cloudinary - Upload Design1",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2224,
        896
      ],
      "parameters": {
        "url": "=https://api.cloudinary.com/v1_1/{{ $('Workflow Configuration1').first().json.cloudinaryCloudName }}/image/upload",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "name": "file",
              "value": "=data:image/png;base64,{{ $binary.data.data.toString(\"base64\") }}"
            },
            {
              "name": "api_key",
              "value": "={{ $('Workflow Configuration1').first().json.cloudinaryApiKey }}"
            },
            {
              "name": "timestamp",
              "value": "={{ Math.floor(Date.now() / 1000) }}"
            },
            {
              "name": "signature",
              "value": "={{ $jmespath($json, \"signature\") }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "382ba576-dced-46ee-9e45-af59cb4c5058",
      "name": "Code - Generate Mockup URLs1",
      "type": "n8n-nodes-base.code",
      "position": [
        2448,
        896
      ],
      "parameters": {
        "jsCode": "// Get configuration and data from previous nodes\nconst config = $('Workflow Configuration1').first().json;\nconst designUpload = $('Cloudinary - Upload Design1').first().json;\nconst analysis = $('OpenAI Vision - Design Analysis1').first().json;\n\n// Extract Cloudinary public_id from upload response\nconst designPublicId = designUpload.public_id;\nconst cloudName = config.cloudinaryCloudName;\n\n// Base product image public IDs from configuration\nconst tshirtWhiteId = config.tshirtWhitePublicId;\nconst tshirtBlackId = config.tshirtBlackPublicId;\nconst toteBagId = config.toteBagPublicId;\n\n// Function to generate Cloudinary transformation URL\nfunction generateMockupUrl(baseProductId, overlayDesignId, transformations = '') {\n  return `https://res.cloudinary.com/${cloudName}/image/upload/${transformations}/l_${overlayDesignId.replace(/\\//g, ':')}/fl_layer_apply,g_center,w_300,h_300/${baseProductId}`;\n}\n\n// Generate mockup URLs for different products\nconst mockups = {\n  tshirtWhite: generateMockupUrl(tshirtWhiteId, designPublicId, 'c_fill,w_800,h_800'),\n  tshirtBlack: generateMockupUrl(tshirtBlackId, designPublicId, 'c_fill,w_800,h_800'),\n  toteBag: generateMockupUrl(toteBagId, designPublicId, 'c_fill,w_800,h_800')\n};\n\n// Determine primary mockup based on AI analysis\nlet primaryMockup = mockups.tshirtWhite;\nif (analysis.recommendedProducts && analysis.recommendedProducts.includes('bag')) {\n  primaryMockup = mockups.toteBag;\n} else if (analysis.colorPalette && analysis.colorPalette.some(color => color.toLowerCase().includes('dark') || color.toLowerCase().includes('black'))) {\n  primaryMockup = mockups.tshirtBlack;\n}\n\n// Return all mockup data\nreturn [{\n  json: {\n    mockups: mockups,\n    primaryMockup: primaryMockup,\n    designPublicId: designPublicId,\n    analysis: analysis,\n    cloudinaryBaseUrl: `https://res.cloudinary.com/${cloudName}/image/upload/`\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "12604daa-5edd-444e-8034-876fc976d4e9",
      "name": "OpenAI - SEO Copywriting1",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        2672,
        896
      ],
      "parameters": {
        "operation": "message"
      },
      "typeVersion": 2
    },
    {
      "id": "f8696e97-631c-4a73-a3b0-ce86f46e4a2b",
      "name": "Shopify - Create Draft Product1",
      "type": "n8n-nodes-base.shopify",
      "position": [
        3024,
        896
      ],
      "parameters": {
        "title": "={{ $('OpenAI - SEO Copywriting1').first().json.title }}",
        "resource": "product",
        "additionalFields": {
          "tags": "={{ $('OpenAI - SEO Copywriting1').first().json.tags.join(', ') }}",
          "images": [
            {
              "src": "={{ $('Code - Generate Mockup URLs1').first().json.primaryMockup }}"
            }
          ],
          "vendor": "Visual Remix Engine",
          "body_html": "={{ $('OpenAI - SEO Copywriting1').first().json.description }}",
          "product_type": "Apparel",
          "published_at": ""
        }
      },
      "typeVersion": 1
    },
    {
      "id": "fc3c20b7-bfd0-406a-a25e-804e8174b5a0",
      "name": "Slack - Approval Request1",
      "type": "n8n-nodes-base.slack",
      "position": [
        3248,
        896
      ],
      "parameters": {
        "text": "=\ud83c\udfa8 *New Design Approval Request*\n\n*AI Analysis:*\n\u2022 Subject: {{ $('Code - Generate Mockup URLs1').first().json.analysis.subject }}\n\u2022 Mood: {{ $('Code - Generate Mockup URLs1').first().json.analysis.mood }}\n\u2022 Copyright Risk: {{ $('Code - Generate Mockup URLs1').first().json.analysis.copyrightRisk }}\n\n*Product Name:* {{ $('OpenAI - SEO Copywriting1').first().json.title }}\n*Price:* ${{ $('OpenAI - SEO Copywriting1').first().json.price }}\n\n*Mockup:* {{ $('Code - Generate Mockup URLs1').first().json.primaryMockup }}\n\nDo you want to publish this?",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Workflow Configuration1').first().json.slackChannelId }}"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.3
    },
    {
      "id": "beb4da16-28fa-4c45-9c29-6a13596656e1",
      "name": "Wait - For Approval1",
      "type": "n8n-nodes-base.wait",
      "position": [
        3472,
        896
      ],
      "parameters": {
        "resume": "webhook",
        "options": {
          "responseData": "Approval received"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "33b4454a-88b0-4eee-9643-cdf74605bee8",
      "name": "Switch - Approval Decision1",
      "type": "n8n-nodes-base.switch",
      "position": [
        3696,
        896
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Approved",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.actions[0].value }}",
                    "rightValue": "approve"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Rejected",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.actions[0].value }}",
                    "rightValue": "reject"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.3
    },
    {
      "id": "a2d60096-c649-4f0b-95b4-ad5cec8e797f",
      "name": "Shopify - Publish Product1",
      "type": "n8n-nodes-base.shopify",
      "position": [
        3920,
        992
      ],
      "parameters": {
        "resource": "product",
        "operation": "update",
        "productId": "={{ $('Shopify - Create Draft Product1').first().json.id }}",
        "updateFields": {
          "published_at": "={{ $now.toISO() }}"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f78bccec-b828-4b72-b851-089fd0df82fa",
      "name": "Instagram - Post Product1",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4144,
        992
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration1').first().json.instagramBusinessAccountId }}/media",
        "method": "POST",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "image_url",
              "value": "={{ $('Code - Generate Mockup URLs1').first().json.primaryMockup }}"
            },
            {
              "name": "caption",
              "value": "=\u2728 New Arrival! \u2728\n\n{{ $('OpenAI - SEO Copywriting1').first().json.description.substring(0, 100) }}...\n\n{{ $('OpenAI - SEO Copywriting1').first().json.tags.map(t => \"#\" + t).join(\" \") }} #design #fashion #art\n\nLink in bio!"
            },
            {
              "name": "access_token",
              "value": "={{ $('Workflow Configuration1').first().json.instagramAccessToken }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "b348e27c-33c9-498c-b9b6-e98391bf450d",
      "name": "Pinterest - Create Pin1",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4368,
        992
      ],
      "parameters": {
        "url": "https://api.pinterest.com/v5/pins",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"board_id\": \"{{ $('Workflow Configuration1').first().json.pinterestBoardId }}\",\n  \"media_source\": {\n    \"source_type\": \"image_url\",\n    \"url\": \"{{ $('Code - Generate Mockup URLs1').first().json.primaryMockup }}\"\n  },\n  \"title\": \"{{ $('OpenAI - SEO Copywriting1').first().json.title }}\",\n  \"description\": \"{{ $('OpenAI - SEO Copywriting1').first().json.description }}\",\n  \"link\": \"{{ $('Shopify - Publish Product1').first().json.admin_graphql_api_id }}\"\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Workflow Configuration1').first().json.pinterestAccessToken }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "affa41e2-6015-48af-9ada-492291165efe",
      "name": "Slack - Rejection Notice1",
      "type": "n8n-nodes-base.slack",
      "position": [
        3920,
        800
      ],
      "parameters": {
        "text": "=\u274c *Design Rejected*\n\nProduct: {{ $('OpenAI - SEO Copywriting1').first().json.title }}\n\nThis design was not published.",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Workflow Configuration1').first().json.slackChannelId }}"
        },
        "otherOptions": {}
      },
      "typeVersion": 2.3
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "a6fa5b34-afa9-4039-88da-57514b10dfbe",
  "connections": {
    "Wait - For Approval1": {
      "main": [
        [
          {
            "node": "Switch - Approval Decision1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Configuration1": {
      "main": [
        [
          {
            "node": "OpenAI Vision - Design Analysis1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Instagram - Post Product1": {
      "main": [
        [
          {
            "node": "Pinterest - Create Pin1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI - SEO Copywriting1": {
      "main": [
        [
          {
            "node": "Shopify - Create Draft Product1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack - Approval Request1": {
      "main": [
        [
          {
            "node": "Wait - For Approval1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Shopify - Publish Product1": {
      "main": [
        [
          {
            "node": "Instagram - Post Product1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cloudinary - Upload Design1": {
      "main": [
        [
          {
            "node": "Code - Generate Mockup URLs1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch - Approval Decision1": {
      "main": [
        [
          {
            "node": "Shopify - Publish Product1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack - Rejection Notice1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code - Generate Mockup URLs1": {
      "main": [
        [
          {
            "node": "OpenAI - SEO Copywriting1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove.bg - Background Removal1": {
      "main": [
        [
          {
            "node": "Cloudinary - Upload Design1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Shopify - Create Draft Product1": {
      "main": [
        [
          {
            "node": "Slack - Approval Request1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Vision - Design Analysis1": {
      "main": [
        [
          {
            "node": "Remove.bg - Background Removal1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Drive Trigger - Design Upload1": {
      "main": [
        [
          {
            "node": "Workflow Configuration1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}