AutomationFlowsData & Sheets › Weekly Testimonial Social Post

Weekly Testimonial Social Post

Weekly Testimonial Social Post. Uses googleSheets, httpRequest. Scheduled trigger; 10 nodes.

Cron / scheduled trigger★★★★☆ complexity10 nodesGoogle SheetsHTTP Request
Data & Sheets Trigger: Cron / scheduled Nodes: 10 Complexity: ★★★★☆ Added:

This workflow follows the Google Sheets → HTTP Request 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": "Weekly Testimonial Social Post",
  "nodes": [
    {
      "id": "sch-002",
      "name": "Monday 9am CT",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        240,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 9 * * 1"
            }
          ]
        }
      }
    },
    {
      "id": "sheets-001",
      "name": "Get All Testimonials",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        480,
        300
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "1W9NRB2H8u0cjctCueXh7VYgL27m5vLLFJfONepNWixk",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Sheet1",
          "mode": "name"
        },
        "options": {}
      }
    },
    {
      "id": "code-003",
      "name": "Random Select Unused Testimonial",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        720,
        300
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const rows = $input.all();\nconst unused = rows.filter(item => {\n  const val = String(item.json.used || '').trim().toUpperCase();\n  return val !== 'TRUE';\n});\nif (!unused.length) throw new Error('No unused testimonials in sheet. Mark some rows used=FALSE or add more rows.');\nconst selected = unused[Math.floor(Math.random() * unused.length)];\nreturn [{ json: selected.json }];"
      }
    },
    {
      "id": "http-002",
      "name": "Gemini: Generate Caption",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        960,
        300
      ],
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=YOUR_GEMINI_API_KEY",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "string",
        "body": "={{ JSON.stringify({ contents: [{ parts: [{ text: `You are a social media copywriter for Adam Styer, a mortgage broker in Austin, TX.\\n\\nA client left this testimonial:\\n\"${$json.testimonial_text}\" \u2014 ${$json.borrower_name}\\n\\nWrite an Instagram caption that:\\n1. Opens with the full testimonial quote in quotation marks (do not shorten or alter it)\\n2. Adds 1-2 warm, human sentences from Adam's point of view (first person) \u2014 vulnerable and real, not corporate\\n3. Ends with: DM me HOME to get started\\n4. Last line: #austinmortgage #mortgagebroker #homepurchase #austinrealestate #firsttimehomebuyer\\n\\nRules: Under 2,200 characters. No hashtags except the provided ones. Write the final caption only \u2014 no commentary, no options, no labels.` }] }] }) }}"
      }
    },
    {
      "id": "code-004",
      "name": "Extract Caption + Build Image Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        300
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const geminiResp = $json;\nconst caption = geminiResp.candidates?.[0]?.content?.parts?.[0]?.text?.trim() || '';\nif (!caption) throw new Error('Gemini returned no caption text.');\nconst firstName = ($json.borrower_name || '').split(' ')[0];\nconst shortQuote = ($json.testimonial_text || '').substring(0, 120);\nconst imagePrompt = `Professional social media quote card. Dark background (#1a1a1a). Gold accent bar at top (#c9a84c). White serif text in center: \"${shortQuote}\" \u2014 ${firstName}. Bottom text in small gold font: Adam Styer | Mortgage Solutions LP. Clean, minimal, premium mortgage brand aesthetic. No people, no faces. Portrait/square format.`;\nreturn [{ json: { ...$json, instagram_caption: caption, image_prompt: imagePrompt } }];"
      }
    },
    {
      "id": "http-003",
      "name": "Gemini: Generate Quote Card Image",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1440,
        300
      ],
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/models/imagen-3.0-generate-002:predict?key=YOUR_GEMINI_API_KEY",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "string",
        "body": "={{ JSON.stringify({ instances: [{ prompt: $json.image_prompt }], parameters: { sampleCount: 1, aspectRatio: '1:1', safetyFilterLevel: 'block_few' } }) }}"
      }
    },
    {
      "id": "code-005",
      "name": "Upload Image to Supabase Storage",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1680,
        300
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const predictions = $json.predictions;\nif (!predictions || !predictions.length) throw new Error('Gemini imagen returned no predictions.');\nconst base64Image = predictions[0].bytesBase64Encoded;\nif (!base64Image) throw new Error('No base64 image data in Gemini response.');\nconst imageBuffer = Buffer.from(base64Image, 'base64');\nconst supabaseUrl = 'https://uuqedsvjlkeszrbwzizl.supabase.co';\nconst supabaseKey = 'YOUR_SUPABASE_SERVICE_ROLE_KEY';\nconst bucket = 'social-assets';\nconst fileName = `testimonial-${Date.now()}.jpg`;\nconst uploadResp = await fetch(`${supabaseUrl}/storage/v1/object/${bucket}/${fileName}`, {\n  method: 'POST',\n  headers: {\n    'Authorization': `Bearer ${supabaseKey}`,\n    'Content-Type': 'image/jpeg'\n  },\n  body: imageBuffer\n});\nif (!uploadResp.ok) {\n  const err = await uploadResp.text();\n  throw new Error(`Supabase Storage upload failed: ${err}`);\n}\nconst publicUrl = `${supabaseUrl}/storage/v1/object/public/${bucket}/${fileName}`;\nconst prevData = $('Random Select Unused Testimonial').first().json;\nreturn [{ json: { ...prevData, image_public_url: publicUrl, instagram_caption: $('Extract Caption + Build Image Prompt').first().json.instagram_caption } }];"
      }
    },
    {
      "id": "http-publer",
      "name": "Publer: Post to All Platforms",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1920,
        300
      ],
      "parameters": {
        "method": "POST",
        "url": "https://app.publer.com/api/v1/posts/schedule",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer-API a1adab72251a3fc861f7dfa0c97439955d28da23c423ec88"
            },
            {
              "name": "Publer-Workspace-Id",
              "value": "69b052bf835c8c689fab8fd8"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "string",
        "body": "={{ JSON.stringify({ text: $json.instagram_caption, accounts: ['69b05329de86f5e15b7c0722','69b0536404b824ffb2c05426','69b0530110a77a0ed895847d'], media: [{ path: $json.image_public_url }], is_draft: false }) }}"
      }
    },
    {
      "id": "sheets-002",
      "name": "Google Sheets: Mark Used = TRUE",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        2160,
        300
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "1W9NRB2H8u0cjctCueXh7VYgL27m5vLLFJfONepNWixk",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Sheet1",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "id": "={{ $('Random Select Unused Testimonial').first().json.id }}",
            "used": "TRUE"
          },
          "matchingColumns": [
            "id"
          ]
        },
        "options": {}
      }
    },
    {
      "id": "http-008",
      "name": "Log to automation_logs",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2400,
        300
      ],
      "parameters": {
        "method": "POST",
        "url": "https://uuqedsvjlkeszrbwzizl.supabase.co/rest/v1/automation_logs",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "YOUR_SUPABASE_SERVICE_ROLE_KEY"
            },
            {
              "name": "Authorization",
              "value": "Bearer YOUR_SUPABASE_SERVICE_ROLE_KEY"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Prefer",
              "value": "return=minimal"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "string",
        "body": "={{ JSON.stringify({ type: 'testimonial_post', platform: 'instagram,linkedin,facebook', testimonial_id: $('Random Select Unused Testimonial').first().json.id, posted_at: new Date().toISOString() }) }}"
      }
    }
  ],
  "connections": {
    "Monday 9am CT": {
      "main": [
        [
          {
            "node": "Get All Testimonials",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get All Testimonials": {
      "main": [
        [
          {
            "node": "Random Select Unused Testimonial",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Random Select Unused Testimonial": {
      "main": [
        [
          {
            "node": "Gemini: Generate Caption",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini: Generate Caption": {
      "main": [
        [
          {
            "node": "Extract Caption + Build Image Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Caption + Build Image Prompt": {
      "main": [
        [
          {
            "node": "Gemini: Generate Quote Card Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini: Generate Quote Card Image": {
      "main": [
        [
          {
            "node": "Upload Image to Supabase Storage",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Image to Supabase Storage": {
      "main": [
        [
          {
            "node": "Publer: Post to All Platforms",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Publer: Post to All Platforms": {
      "main": [
        [
          {
            "node": "Google Sheets: Mark Used = TRUE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets: Mark Used = TRUE": {
      "main": [
        [
          {
            "node": "Log to automation_logs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "timezone": "America/Chicago"
  },
  "id": "weekly-testimonial-post-v1",
  "meta": {
    "templateCredsSetupCompleted": false
  }
}

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

Weekly Testimonial Social Post. Uses googleSheets, httpRequest. Scheduled trigger; 10 nodes.

Source: https://github.com/AStyer8345/loanos/blob/69749dc6e960e040240d728322010dffc2631082/automations/workflow-2-weekly-testimonial-post.json — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

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

Data & Sheets

This workflow automates video distribution to 9 social platforms simultaneously using Blotato's API. It includes both a scheduled publisher (checks Google Sheets for videos marked "Ready") and a subwo

Google Sheets, HTTP Request, Form Trigger +2
Data & Sheets

YogiAI. Uses googleSheets, googleSheetsTool, httpRequest, stopAndError. Scheduled trigger; 61 nodes.

Google Sheets, Google Sheets Tool, HTTP Request +1
Data & Sheets

This workflow monitors Google Calendar for events indicating that a customer will visit the company today or the next day, retrieves the required details, and sends reminder notifications to the relev

Google Calendar, Google Sheets, HTTP Request +1
Data & Sheets

ofn hook v0.24.0 beta. Uses start, httpRequest, functionItem, itemLists. Scheduled trigger; 42 nodes.

Start, HTTP Request, Function Item +3
Data & Sheets

Security teams, DevOps engineers, vulnerability analysts, and automation builders who want to eliminate repetitive Nessus scan parsing, AI-based risk triage, and manual reporting. Designed for orgs fo

Email Send, HTTP Request, Google Sheets +1