Product photo → Arabic and English captions

Drop a product photo in a Drive folder; AI writes Instagram captions in Arabic and English with hashtags.

Medium Saves ~6 hours/month Content & SocialBy Flowngn team

Works with n8n · Make · Zapier

How it works

Trigger: Google Drive — new file in your Products folder
  1. Detect a new photo in your Drive folder.
  2. Download it and let AI look at the product.
  3. AI writes an Arabic caption, an English caption and hashtags in your brand voice.
  4. Save them to your Content sheet with the photo link, ready to review and post.

Diagram

GNew product photoGoogle Drive TriggerGDownload photoGoogle DriveWrite captionsBasic LLM ChainClaude modelAnthropic Chat Mo…{ }Read captionsCodeGSave to Content she…Google Sheets
Trigger App Code AI

Code

JavaScript used in the Code step — already included in the template below.

Read captions · Code node
// Mode: Run Once for Each Item
// The AI was asked for JSON like:
// {"product": "Black abaya", "ar": "...", "en": "...", "hashtags": "#abaya #..."}
const match = $json.text.match(/\{[\s\S]*\}/);
const c = match ? JSON.parse(match[0]) : {};
const file = $("New product photo").item.json;

return {
  json: {
    date: new Date().toISOString().slice(0, 10),
    file: file.name,
    photo: file.webViewLink,
    product: c.product || "",
    caption_ar: c.ar || "",
    caption_en: c.en || "",
    hashtags: c.hashtags || "",
    status: "Needs review",
  },
};

Build it

Pick your tool — same automation, step by step.

Copy and paste straight into an n8n canvas (Ctrl+V), or download and use Import from File. Then connect your own credentials and replace every YOUR_… placeholder. AI steps use a Claude model node — swap it for OpenAI, Gemini or Ollama if you prefer.

Workflow JSON
Show code (206 lines)
{
  "name": "Product photo → Arabic and English captions (Flowngn)",
  "nodes": [
    {
      "parameters": {
        "content": "## Product photo → Arabic and English captions\nDrop a product photo in a Drive folder; AI writes Instagram captions in Arabic and English with hashtags.\n\n**Setup**\n1. Open each node and connect your own credentials.\n2. Replace every YOUR_… placeholder.\n3. Test once, then activate.\n\nFree template from Flowngn",
        "height": 260,
        "width": 340
      },
      "id": "43ef6e94-927f-4bc8-a7a3-d4912df10d1f",
      "name": "Setup notes",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -420,
        -60
      ]
    },
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "triggerOn": "specificFolder",
        "folderToWatch": {
          "__rl": true,
          "value": "YOUR_PRODUCTS_FOLDER_ID",
          "mode": "id"
        },
        "event": "fileCreated",
        "options": {}
      },
      "id": "43ef6e94-927f-4bc8-a7a3-d4912df10d1f",
      "name": "New product photo",
      "type": "n8n-nodes-base.googleDriveTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "operation": "download",
        "fileId": {
          "__rl": true,
          "value": "={{ $json.id }}",
          "mode": "id"
        },
        "options": {}
      },
      "id": "43ef6e94-927f-4bc8-a7a3-d4912df10d1f",
      "name": "Download photo",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        300,
        0
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=You write Instagram captions for YOUR_BRAND. Brand voice: YOUR_BRAND_VOICE. Audience: YOUR_AUDIENCE.\nLook at this product photo. File name (may include product name and price): {{ $('New product photo').item.json.name }}\n\nReply ONLY with JSON: {\"product\": \"<short product name>\", \"ar\": \"<Arabic caption, 2-3 short lines, natural Gulf/Egyptian-friendly Arabic>\", \"en\": \"<English caption, 2-3 short lines>\", \"hashtags\": \"<8-10 relevant hashtags, Arabic and English>\"}. Never invent prices, materials or offers that aren't in the file name.",
        "passthroughBinaryImages": true
      },
      "id": "43ef6e94-927f-4bc8-a7a3-d4912df10d1f",
      "name": "Write captions",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.5,
      "position": [
        600,
        0
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "claude-sonnet-5",
          "mode": "list"
        },
        "options": {}
      },
      "id": "43ef6e94-927f-4bc8-a7a3-d4912df10d1f",
      "name": "Claude model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.3,
      "position": [
        600,
        220
      ]
    },
    {
      "parameters": {
        "jsCode": "// Mode: Run Once for Each Item\n// The AI was asked for JSON like:\n// {\"product\": \"Black abaya\", \"ar\": \"...\", \"en\": \"...\", \"hashtags\": \"#abaya #...\"}\nconst match = $json.text.match(/\\{[\\s\\S]*\\}/);\nconst c = match ? JSON.parse(match[0]) : {};\nconst file = $(\"New product photo\").item.json;\n\nreturn {\n  json: {\n    date: new Date().toISOString().slice(0, 10),\n    file: file.name,\n    photo: file.webViewLink,\n    product: c.product || \"\",\n    caption_ar: c.ar || \"\",\n    caption_en: c.en || \"\",\n    hashtags: c.hashtags || \"\",\n    status: \"Needs review\",\n  },\n};",
        "mode": "runOnceForEachItem"
      },
      "id": "43ef6e94-927f-4bc8-a7a3-d4912df10d1f",
      "name": "Read captions",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        0
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Content",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {}
        },
        "options": {}
      },
      "id": "43ef6e94-927f-4bc8-a7a3-d4912df10d1f",
      "name": "Save to Content sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1200,
        0
      ]
    }
  ],
  "connections": {
    "New product photo": {
      "main": [
        [
          {
            "node": "Download photo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download photo": {
      "main": [
        [
          {
            "node": "Write captions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write captions": {
      "main": [
        [
          {
            "node": "Read captions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read captions": {
      "main": [
        [
          {
            "node": "Save to Content sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude model": {
      "ai_languageModel": [
        [
          {
            "node": "Write captions",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}

Open in the playground to edit →

Tips

  • Name files with the product name and price ("black-abaya-450.jpg"), since AI uses the file name too.
  • Use an AI model that can read images.

Related flows