{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "a6a8404f-b2cb-4889-b846-ef9019583595",
      "name": "\ud83d\udccb Flow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -800,
        -368
      ],
      "parameters": {
        "width": 720,
        "height": 556,
        "content": "## \ud83d\uddbc\ufe0f Gemini Imagen 3 \u2013 Image Generation \u2192 Upload to URL\n\n**How it works:**\n1. Webhook receives a POST request with `jobType`: **localize** or **mockup**\n2. **Route by Job Type** switch sends it down the correct pipeline\n3. A code node crafts the ideal **Gemini Imagen 3 prompt** from the payload\n4. **Gemini Imagen 3** generates the image and returns it as base64\n5. A code node **decodes base64 \u2192 n8n binary** (mimeType `image/png`)\n6. The binary is handed to the **n8n Upload to URL node** which PUTs it to your CDN presigned URL\n7. A final code node builds the **JSON response** with the public CDN URL\n\n**Credential needed:**\n`Google AI Header Auth` \u2014 HTTP Header Auth\nHeader name: `x-goog-api-key`  \u00b7  Value: `YOUR_GEMINI_API_KEY`\nGet your key at: aistudio.google.com\n\n**Two things to replace after import:**\n- `YOUR_CDN_PRESIGNED_PUT_URL` in both Upload to URL nodes\n- `YOUR_CDN_DOMAIN` in both response code nodes\n\n**Gemini Imagen 3 endpoint:**\n`POST https://generativelanguage.googleapis.com/v1beta/models/imagen-3.0-generate-001:predict`\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "dee009cf-b726-43ab-a744-779843852d31",
      "name": "Sticky \u2013 Entry & Routing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        320
      ],
      "parameters": {
        "color": 7,
        "width": 560,
        "height": 668,
        "content": "### 1\ufe0f\u20e3 Webhook Entry & Job Router\n**Webhook \u2013 Receive Image Job** accepts a POST at `/gemini-image-job` with `jobType` and generation parameters in the request body.\n**Route by Job Type Switch** reads `$json.body.jobType` and routes:\n- Output 0 `localize` \u2192 Localised Marketing Campaign pipeline\n- Output 1 `mockup` \u2192 Product Mockup pipeline\n- Fallback \u2192 **Respond \u2013 Error** node returns a 400 JSON error"
      },
      "typeVersion": 1
    },
    {
      "id": "8e022129-2430-4209-bed9-9e13de95f776",
      "name": "Sticky \u2013 Localize Pipeline",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        576,
        240
      ],
      "parameters": {
        "color": 7,
        "width": 1504,
        "height": 392,
        "content": "### 2\ufe0f\u20e3 Localised Marketing Campaign\n**Build Localize Prompt** code node constructs the Imagen 3 prompt: recreate the master image with all embedded text accurately translated into `targetLanguage`, preserving original layout, fonts, brand colours and visual hierarchy. Injects `brandName` and `campaignText` from the payload. Also stamps a unique `jobId`.\n**Gemini Imagen 3 \u2013 Localize** POSTs to the Imagen 3 predict endpoint with `sampleCount: 1` and `aspectRatio: 1:1`. Returns `predictions[0].bytesBase64Encoded`.\n**Decode Localize Image** code node decodes the base64 string to a Buffer, writes it into the n8n binary store as property `data` with `mimeType: image/png` and filename `localized_{lang}_{jobId}.png`.\n**Upload to URL \u2013 Localize** is the **n8n built-in Upload to URL node** \u2014 it PUTs the binary to your CDN presigned URL with method PUT.\n**Build Localize Response** code node returns `{ success, jobId, language, publicUrl, generatedAt }`.\n**Respond to Webhook \u2013 Localize** sends the JSON back to the caller."
      },
      "typeVersion": 1
    },
    {
      "id": "639f2425-b883-4a43-a1bd-fe8c03ee8861",
      "name": "Sticky \u2013 Mockup Pipeline",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        576,
        672
      ],
      "parameters": {
        "color": 7,
        "width": 1584,
        "height": 332,
        "content": "### 3\ufe0f\u20e3 High-Fidelity Product Mockup\n**Build Mockup Prompt** code node constructs the Imagen 3 prompt: photorealistic `productType` with the brand logo/pattern applied using `colorScheme`, studio lighting, natural fabric texture, realistic shadows. Stamps a unique `jobId`.\n**Gemini Imagen 3 \u2013 Mockup** POSTs to the same Imagen 3 endpoint. Returns the photorealistic product image as base64.\n**Decode Mockup Image** code node decodes base64 \u2192 binary, filename `mockup_{product}_{jobId}.png`.\n**Upload to URL \u2013 Mockup** is the **n8n built-in Upload to URL node** \u2014 PUTs the binary to your CDN presigned URL.\n**Build Mockup Response** code node returns `{ success, jobId, productType, colorScheme, publicUrl, generatedAt }`.\n**Respond to Webhook \u2013 Mockup** sends the JSON back to the caller."
      },
      "typeVersion": 1
    },
    {
      "id": "b9681a8a-149d-4743-9e30-b1b9ac25c29c",
      "name": "Webhook \u2013 Receive Image Job",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -32,
        608
      ],
      "parameters": {
        "path": "gemini-image-job",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "7bdb63c2-e880-4ffd-8e00-010deec889a5",
      "name": "Route by Job Type",
      "type": "n8n-nodes-base.switch",
      "position": [
        320,
        592
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Localize",
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.jobType.toLowerCase() }}",
                    "rightValue": "localize"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Mockup",
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.jobType.toLowerCase() }}",
                    "rightValue": "mockup"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "5ecbbc02-0a8b-46e8-a2a2-ade5fa4f02f2",
      "name": "Respond \u2013 Error",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        320,
        800
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}"
      },
      "typeVersion": 1
    },
    {
      "id": "25e9153d-88a4-44c0-ac84-13874e46583b",
      "name": "Build Localize Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        624,
        432
      ],
      "parameters": {
        "jsCode": "const b      = $json.body;\nconst lang   = b.targetLanguage || 'Spanish';\nconst brand  = b.brandName      || 'Brand';\nconst text   = b.campaignText   || 'Special Offer';\nconst jobId  = `LOC-${Date.now()}`;\n\nconst prompt = `Recreate this marketing image with all embedded text accurately translated into ${lang}. ` +\n  `Preserve the exact original layout, visual hierarchy, font style and brand colours. ` +\n  `Brand: ${brand}. Translated headline in ${lang}: \"${text}\". ` +\n  `Photorealistic quality, sharp typography, professional marketing aesthetic, 1024x1024.`;\n\nreturn [{ json: { prompt, lang, brand, campaignText: text, jobId,\n                  masterImageUrl: b.masterImageUrl || '' } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "1ed05274-5d9d-488c-ba1c-3d495533d202",
      "name": "Gemini Imagen 3 \u2013 Localize",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        880,
        432
      ],
      "parameters": {
        "url": "https://generativelanguage.googleapis.com/v1beta/models/imagen-3.0-generate-001:predict",
        "method": "POST",
        "options": {},
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "1a22bb68-eea1-47eb-8ddc-05af39fd9ebf",
      "name": "Decode Localize Image",
      "type": "n8n-nodes-base.code",
      "position": [
        1152,
        432
      ],
      "parameters": {
        "jsCode": "const pred  = $json.predictions?.[0];\nif (!pred) throw new Error('Imagen 3 returned no prediction');\nconst b64   = pred.bytesBase64Encoded;\nconst mime  = pred.mimeType || 'image/png';\nconst ext   = mime.split('/')[1] || 'png';\nconst prev  = $('Build Localize Prompt').item.json;\nconst fname = `localized_${prev.lang.toLowerCase().replace(/\\s+/g,'_')}_${prev.jobId}.${ext}`;\nconst buf   = Buffer.from(b64, 'base64');\nreturn [{\n  json:   { ...prev, filename: fname, mimeType: mime },\n  binary: { data: { data: b64, mimeType: mime, fileName: fname,\n                    fileSize: buf.length, fileExtension: ext } }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e0281466-27cd-4873-b85e-165c864e41e0",
      "name": "Build Localize Response",
      "type": "n8n-nodes-base.code",
      "position": [
        1648,
        432
      ],
      "parameters": {
        "jsCode": "const p = $('Build Localize Prompt').item.json;\nreturn [{ json: {\n  success: true, jobId: p.jobId, jobType: 'localize',\n  language: p.lang, brand: p.brand,\n  filename: p.filename,\n  publicUrl: `https://YOUR_CDN_DOMAIN/${p.filename}`,\n  generatedAt: new Date().toISOString()\n}}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "cf55d88e-88bc-4c3e-aa11-22017b1cc3f6",
      "name": "Respond to Webhook \u2013 Localize",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1888,
        432
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}"
      },
      "typeVersion": 1
    },
    {
      "id": "71c6deda-bf19-4aff-87bc-be07f734d905",
      "name": "Build Mockup Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        624,
        832
      ],
      "parameters": {
        "jsCode": "const b       = $json.body;\nconst product = b.productType      || 't-shirt';\nconst colors  = b.colorScheme      || 'navy and gold';\nconst logo    = b.logoDescription  || 'brand logo';\nconst jobId   = `MKP-${Date.now()}`;\n\nconst prompt = `Photorealistic product mockup of a ${product}. ` +\n  `Apply a ${logo} using ${colors} colour scheme. ` +\n  `Studio background, professional lighting, natural fabric texture, realistic shadows and highlights. ` +\n  `Premium commercial product photo ready for e-commerce. 1024x1024, no text overlays.`;\n\nreturn [{ json: { prompt, product, colors, logo, jobId } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "dddf4923-4fc0-453a-91d0-ad5ae7cd4d3e",
      "name": "Gemini Imagen 3 \u2013 Mockup",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        880,
        832
      ],
      "parameters": {
        "url": "https://generativelanguage.googleapis.com/v1beta/models/imagen-3.0-generate-001:predict",
        "method": "POST",
        "options": {},
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "93482732-f535-4118-89e6-36f0eaa87603",
      "name": "Decode Mockup Image",
      "type": "n8n-nodes-base.code",
      "position": [
        1152,
        832
      ],
      "parameters": {
        "jsCode": "const pred  = $json.predictions?.[0];\nif (!pred) throw new Error('Imagen 3 returned no prediction');\nconst b64   = pred.bytesBase64Encoded;\nconst mime  = pred.mimeType || 'image/png';\nconst ext   = mime.split('/')[1] || 'png';\nconst prev  = $('Build Mockup Prompt').item.json;\nconst fname = `mockup_${prev.product.replace(/\\s+/g,'_')}_${prev.jobId}.${ext}`;\nconst buf   = Buffer.from(b64, 'base64');\nreturn [{\n  json:   { ...prev, filename: fname, mimeType: mime },\n  binary: { data: { data: b64, mimeType: mime, fileName: fname,\n                    fileSize: buf.length, fileExtension: ext } }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "d750be8f-048a-44f9-b0e4-26d279ddd93e",
      "name": "Build Mockup Response",
      "type": "n8n-nodes-base.code",
      "position": [
        1648,
        832
      ],
      "parameters": {
        "jsCode": "const p = $('Build Mockup Prompt').item.json;\nreturn [{ json: {\n  success: true, jobId: p.jobId, jobType: 'mockup',\n  productType: p.product, colorScheme: p.colors,\n  filename: p.filename,\n  publicUrl: `https://YOUR_CDN_DOMAIN/${p.filename}`,\n  generatedAt: new Date().toISOString()\n}}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4024e030-fe6a-4c63-a89f-5e4b28753eca",
      "name": "Respond to Webhook \u2013 Mockup",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1888,
        832
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}"
      },
      "typeVersion": 1
    },
    {
      "id": "aed7cefc-af66-4a33-97ee-5aab1bd6d580",
      "name": "Upload to URL",
      "type": "n8n-nodes-uploadtourl.uploadToUrl",
      "position": [
        1392,
        432
      ],
      "parameters": {},
      "credentials": {
        "uploadToUrlApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "15704ba4-1042-46a6-9488-9b1dfd7cb46b",
      "name": "Upload to URL1",
      "type": "n8n-nodes-uploadtourl.uploadToUrl",
      "position": [
        1424,
        832
      ],
      "parameters": {},
      "credentials": {
        "uploadToUrlApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Upload to URL": {
      "main": [
        [
          {
            "node": "Build Localize Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to URL1": {
      "main": [
        [
          {
            "node": "Build Mockup Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Job Type": {
      "main": [
        [
          {
            "node": "Build Localize Prompt",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build Mockup Prompt",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond \u2013 Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Mockup Prompt": {
      "main": [
        [
          {
            "node": "Gemini Imagen 3 \u2013 Mockup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Decode Mockup Image": {
      "main": [
        [
          {
            "node": "Upload to URL1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Localize Prompt": {
      "main": [
        [
          {
            "node": "Gemini Imagen 3 \u2013 Localize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Mockup Response": {
      "main": [
        [
          {
            "node": "Respond to Webhook \u2013 Mockup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Decode Localize Image": {
      "main": [
        [
          {
            "node": "Upload to URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Localize Response": {
      "main": [
        [
          {
            "node": "Respond to Webhook \u2013 Localize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Imagen 3 \u2013 Mockup": {
      "main": [
        [
          {
            "node": "Decode Mockup Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Imagen 3 \u2013 Localize": {
      "main": [
        [
          {
            "node": "Decode Localize Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook \u2013 Receive Image Job": {
      "main": [
        [
          {
            "node": "Route by Job Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}