AutomationFlowsAI & RAG › Gemini 3.1 Generate V2

Gemini 3.1 Generate V2

GEMINI 3.1 GENERATE v2. Uses httpRequest, telegram. Event-driven trigger; 10 nodes.

Event trigger★★★★☆ complexity10 nodesHTTP RequestTelegram
AI & RAG Trigger: Event Nodes: 10 Complexity: ★★★★☆ Added:

This workflow follows the HTTP Request → Telegram recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "GEMINI 3.1 GENERATE v2",
  "nodes": [
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        28992,
        20176
      ],
      "id": "9181a364-0ab8-4fff-9b66-3cd6e9adba90",
      "name": "When clicking \u2018Execute workflow\u2019"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "porto-tools-image",
        "authentication": "headerAuth",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        28992,
        20416
      ],
      "id": "74055aca-c88a-47bf-9bb1-e5a5d79b7863",
      "name": "Webhook (PORTO)",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "projectId",
              "name": "projectId",
              "value": "={{ $env.GCP_PROJECT_ID || 'project-8fa51551-4f6c-4ff6-8b7' }}",
              "type": "string"
            },
            {
              "id": "accessToken",
              "name": "accessToken",
              "value": "={{ $env.GCP_ACCESS_TOKEN }}",
              "type": "string"
            },
            {
              "id": "prompt",
              "name": "prompt",
              "value": "Generate a premium product image on a clean studio background. High detail, realistic lighting, sharp focus.",
              "type": "string"
            },
            {
              "id": "aspectRatio",
              "name": "aspectRatio",
              "value": "1:1",
              "type": "string"
            },
            {
              "id": "source",
              "name": "source",
              "value": "manual",
              "type": "string"
            },
            {
              "id": "requestId",
              "name": "requestId",
              "value": "={{ $now.toISO() }}",
              "type": "string"
            },
            {
              "id": "references",
              "name": "references",
              "value": "={{ [] }}",
              "type": "array"
            },
            {
              "id": "referenceMapping",
              "name": "referenceMapping",
              "value": "={{ ({}) }}",
              "type": "object"
            }
          ]
        },
        "options": {}
      },
      "id": "5138585b-c3ae-4387-aa82-db02b30067b7",
      "name": "Manual Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        29312,
        20176
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "projectId",
              "name": "projectId",
              "value": "={{ $env.GCP_PROJECT_ID || 'project-8fa51551-4f6c-4ff6-8b7' }}",
              "type": "string"
            },
            {
              "id": "accessToken",
              "name": "accessToken",
              "value": "={{ $env.GCP_ACCESS_TOKEN }}",
              "type": "string"
            },
            {
              "id": "prompt",
              "name": "prompt",
              "value": "={{ $json.body.prompt }}",
              "type": "string"
            },
            {
              "id": "aspectRatio",
              "name": "aspectRatio",
              "value": "={{ $json.body.aspectRatio || '1:1' }}",
              "type": "string"
            },
            {
              "id": "source",
              "name": "source",
              "value": "porto-web",
              "type": "string"
            },
            {
              "id": "requestId",
              "name": "requestId",
              "value": "={{ $json.body.requestId }}",
              "type": "string"
            },
            {
              "id": "userEmail",
              "name": "userEmail",
              "value": "={{ $json.body.userEmail }}",
              "type": "string"
            },
            {
              "id": "references",
              "name": "references",
              "value": "={{ $json.body.references || [] }}",
              "type": "array"
            },
            {
              "id": "referenceMapping",
              "name": "referenceMapping",
              "value": "={{ $json.body.referenceMapping || {} }}",
              "type": "object"
            }
          ]
        },
        "options": {}
      },
      "id": "99f16994-e25c-4d6e-9041-f7adeefddabc",
      "name": "Webhook Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        29312,
        20416
      ]
    },
    {
      "parameters": {
        "jsCode": "const refs = Array.isArray($json.references) ? $json.references : [];\nconst downloaded = [];\nfor (const r of refs) {\n  if (!r || typeof r.url !== 'string') continue;\n  const buf = await this.helpers.httpRequest({\n    method: 'GET',\n    url: r.url,\n    encoding: 'arraybuffer',\n    returnFullResponse: false,\n  });\n  const base64 = Buffer.from(buf).toString('base64');\n  downloaded.push({ mimeType: r.mimeType || 'image/png', data: base64 });\n}\nreturn [{ json: { ...$json, refsBase64: downloaded } }];"
      },
      "id": "c3a91e2f-6b7d-4e8a-9c1f-2d4e5f6a7b8c",
      "name": "Download References",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        29504,
        20304
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://aiplatform.googleapis.com/v1/projects/{{$json.projectId}}/locations/global/publishers/google/models/gemini-3.1-flash-image-preview:generateContent",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{$json.accessToken}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ (() => { const refs = Array.isArray($json.refsBase64) ? $json.refsBase64 : []; let p = $json.prompt || ''; const map = $json.referenceMapping || {}; for (const [token, idx] of Object.entries(map)) { const num = Number(idx) + 1; p = p.split(token).join('image #' + num); } const parts = refs.map(r => ({ inlineData: { mimeType: r.mimeType, data: r.data } })); parts.push({ text: p }); return { contents: [{ role: 'user', parts }], generationConfig: { responseModalities: ['TEXT', 'IMAGE'], imageConfig: { aspectRatio: $json.aspectRatio } } }; })() }}",
        "options": {}
      },
      "id": "a184784f-f933-4b71-979e-5a95e7d88d76",
      "name": "Vertex Gemini 3.1 Image",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        29632,
        20304
      ],
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const upstream = $('Vertex Gemini 3.1 Image').first().json;\n\nfunction readConfigNode(nodeName) {\n  try {\n    const item = $(nodeName).first().json;\n    return item?.source ? item : null;\n  } catch (error) {\n    return null;\n  }\n}\n\nfunction asObject(value) {\n  return value && typeof value === 'object' && !Array.isArray(value) ? value : null;\n}\n\nfunction asArray(value) {\n  return Array.isArray(value) ? value : [];\n}\n\nfunction firstString(...values) {\n  for (const value of values) {\n    if (typeof value === 'string' && value.trim()) return value.trim();\n  }\n  return '';\n}\n\nfunction collectParts(response) {\n  const candidates = asArray(response?.candidates);\n  const parts = [];\n\n  for (const candidate of candidates) {\n    parts.push(...asArray(candidate?.content?.parts));\n    parts.push(...asArray(candidate?.content?.role?.parts));\n  }\n\n  parts.push(...asArray(response?.content?.parts));\n  parts.push(...asArray(response?.parts));\n\n  return parts.filter(Boolean);\n}\n\nfunction readInlineData(part) {\n  const inlineData = asObject(part?.inlineData) || asObject(part?.inline_data);\n  if (!inlineData) return null;\n\n  const rawData = firstString(inlineData.data, inlineData.imageBase64, inlineData.base64);\n  if (!rawData) return null;\n\n  const dataUrlMatch = rawData.match(/^data:([^;]+);base64,(.+)$/);\n  const mimeType = firstString(\n    inlineData.mimeType,\n    inlineData.mime_type,\n    dataUrlMatch?.[1],\n    'image/png',\n  );\n  const imageBase64 = (dataUrlMatch?.[2] || rawData).replace(/\\s+/g, '');\n\n  return { mimeType, imageBase64 };\n}\n\nfunction isProbablyBase64(value) {\n  return /^[A-Za-z0-9+/]+={0,2}$/.test(value) && value.length % 4 === 0;\n}\n\nfunction extensionFromMime(mimeType) {\n  if (mimeType.includes('jpeg') || mimeType.includes('jpg')) return 'jpg';\n  if (mimeType.includes('webp')) return 'webp';\n  return 'png';\n}\n\nconst sourceItem = readConfigNode('Webhook Config') || readConfigNode('Manual Config') || {};\nconst parts = collectParts(upstream);\nconst imagePart = parts.find((part) => readInlineData(part));\nconst textPart = parts.find((part) => typeof part?.text === 'string' && part.text.trim());\n\nif (!imagePart) {\n  const candidateCount = asArray(upstream?.candidates).length;\n  const partKeys = parts.map((part) => Object.keys(asObject(part) || {}).join('|')).filter(Boolean);\n  throw new Error(\n    'Tidak ada inline image dari Gemini response. candidates=' + candidateCount +\n    '; parts=' + parts.length +\n    '; partKeys=' + (partKeys.join(', ') || 'none') +\n    '. Pastikan responseModalities berisi IMAGE dan model mendukung image output.'\n  );\n}\n\nconst inline = readInlineData(imagePart);\nif (!inline?.imageBase64) {\n  throw new Error('Inline image ditemukan, tetapi field data/base64 kosong.');\n}\n\nif (!isProbablyBase64(inline.imageBase64)) {\n  throw new Error('Inline image data bukan base64 valid. Cek output Gemini/HTTP node.');\n}\n\nconst imageBytes = Buffer.from(inline.imageBase64, 'base64');\nif (!imageBytes.length) {\n  throw new Error('Inline image base64 valid tetapi menghasilkan file kosong.');\n}\n\nconst mimeType = inline.mimeType;\nconst extension = extensionFromMime(mimeType);\nconst requestId = firstString(sourceItem.requestId, $execution.id, Date.now().toString());\n\nreturn [{\n  json: {\n    ok: true,\n    model: 'gemini-3.1-flash-image-preview',\n    mimeType,\n    imageBase64: inline.imageBase64,\n    imageSizeBytes: imageBytes.length,\n    text: textPart?.text ?? '',\n    source: sourceItem.source || 'manual',\n    requestId,\n    aspectRatio: sourceItem.aspectRatio || '1:1',\n    userEmail: sourceItem.userEmail || null,\n  },\n  binary: {\n    image: {\n      data: inline.imageBase64,\n      mimeType,\n      fileName: 'gemini-3-1-output-' + requestId + '.' + extension,\n      fileExtension: extension,\n      fileSize: imageBytes.length,\n    },\n  },\n}];"
      },
      "id": "b7a545d7-1ae8-49af-a4f8-67bf068f0e08",
      "name": "Convert Image to Binary",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        29888,
        20304
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "source-porto-web",
                    "leftValue": "={{ $json.source }}",
                    "rightValue": "porto-web",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "name": "filter.operator.equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "porto-web"
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra",
          "renameFallbackOutput": "manual"
        }
      },
      "id": "8811a762-1864-4cb7-aeba-181387af6499",
      "name": "Route by source",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        30144,
        20304
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { ok: true, source: $json.source, requestId: $json.requestId, model: $json.model, aspectRatio: $json.aspectRatio, mimeType: $json.mimeType, imageBase64: $json.imageBase64, imageSizeBytes: $json.imageSizeBytes, text: $json.text || '' } }}",
        "options": {
          "responseCode": 200
        }
      },
      "id": "ce409c00-0980-4c50-bafd-65119501b3cc",
      "name": "Respond to Webhook (PORTO)",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        30432,
        20176
      ]
    },
    {
      "parameters": {
        "operation": "sendPhoto",
        "chatId": "-1003986112540",
        "binaryData": true,
        "binaryPropertyName": "image",
        "additionalFields": {
          "caption": "={{ $json.text || 'Generated with Gemini 3.1 Flash Image Preview' }}"
        }
      },
      "id": "91654968-dfe5-4f36-bdfb-dc233d37f5f0",
      "name": "Send Image to Telegram",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        30432,
        20416
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Manual Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook (PORTO)": {
      "main": [
        [
          {
            "node": "Webhook Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Config": {
      "main": [
        [
          {
            "node": "Download References",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Config": {
      "main": [
        [
          {
            "node": "Download References",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download References": {
      "main": [
        [
          {
            "node": "Vertex Gemini 3.1 Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Vertex Gemini 3.1 Image": {
      "main": [
        [
          {
            "node": "Convert Image to Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert Image to Binary": {
      "main": [
        [
          {
            "node": "Route by source",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by source": {
      "main": [
        [
          {
            "node": "Respond to Webhook (PORTO)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Image to Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "a70e7b37-37e8-4c67-baaa-8cfdcd53073f",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "I7lkeFnnbHg0nPGP",
  "tags": []
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

GEMINI 3.1 GENERATE v2. Uses httpRequest, telegram. Event-driven trigger; 10 nodes.

Source: https://github.com/ID-Adi/PORTO/blob/cdf2a32e25bb55475a0d21c66d53e5a14723be20/n8n/Generate_Image_tools.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Creators, designers, and developers exploring AI-powered image generation. Automation enthusiasts who want to integrate image creation into n8n workflows. Telegram bot builders looking to add visual A

Telegram Trigger, Airtable, HTTP Request +2
AI & RAG

Monitors multiple technology websites 24/7 Uses AI to read and understand each article Filters out low-quality content automatically. Saves the best articles to Notion with summaries. Sends you Telegr

RSS Feed Read, Notion, HTTP Request +1
AI & RAG

[](https://www.linkedin.com/in/mosaab-yassir-lafrimi/)[](https://t.me/joevenner)

Telegram Trigger, HTTP Request, Redis +3
AI & RAG

Transcribe audio messages from Telegram using Google Gemini for free.

Telegram, Telegram Trigger, HTTP Request +3
AI & RAG

A sophisticated Telegram bot that provides AI-powered responses with conversation memory. This template demonstrates how to integrate any AI API service with Telegram, making it easy to swap between d

Telegram Trigger, Telegram, HTTP Request