AutomationFlowsData & Sheets › Wf 009 Publishing

Wf 009 Publishing

WF-009-publishing. Uses postgres, httpRequest. Scheduled trigger; 23 nodes.

Cron / scheduled trigger★★★★☆ complexity23 nodesPostgresHTTP Request
Data & Sheets Trigger: Cron / scheduled Nodes: 23 Complexity: ★★★★☆ Added:

This workflow follows the HTTP Request → Postgres 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
{
  "id": "wf009-publishing-0000000000000",
  "name": "WF-009-publishing",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "*/30 * * * *"
            }
          ]
        }
      },
      "id": "c90900000-0009-0009-0009-000000000001",
      "name": "Schedule 30min",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        500
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT * FROM v_publishing_queue LIMIT 1",
        "options": {}
      },
      "id": "c90900000-0009-0009-0009-000000000002",
      "name": "Poll Publishing Queue",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        220,
        500
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "leftValue": "={{ $json.id }}",
              "rightValue": "",
              "operator": {
                "type": "any",
                "operation": "exists"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "c90900000-0009-0009-0009-000000000003",
      "name": "Has Record?",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.2,
      "position": [
        440,
        500
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "leftValue": "={{ !$json.scheduled_at || new Date($json.scheduled_at) <= new Date() }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "c90900000-0009-0009-0009-000000000004",
      "name": "Is Time To Publish?",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.2,
      "position": [
        660,
        500
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "=SELECT ca.hook_variations, ca.story_arc, ca.seo_data, ca.cta_variations, ca.facts, s.title, s.platform, s.channel_id FROM content_assets ca, scripts s WHERE ca.id = {{ $json.asset_id }} AND s.id = {{ $json.script_id }}",
        "options": {}
      },
      "id": "c90900000-0009-0009-0009-000000000005",
      "name": "Fetch Full Context",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        880,
        500
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "=SELECT media_type, file_path FROM media_assets WHERE asset_id = {{ $('Poll Publishing Queue').first().json.asset_id }} AND (script_id = {{ $('Poll Publishing Queue').first().json.script_id }} OR script_id IS NULL) AND media_type IN ('video','thumbnail') ORDER BY id DESC",
        "options": {}
      },
      "id": "c90900000-0009-0009-0009-000000000006",
      "name": "Fetch Media Files",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        1100,
        500
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "files"
      },
      "id": "c90900000-0009-0009-0009-000000000007",
      "name": "Aggregate Media Files",
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        1320,
        500
      ]
    },
    {
      "parameters": {
        "jsCode": "const pr = $('Poll Publishing Queue').first().json;\nconst ctx = $('Fetch Full Context').first().json;\nconst files = $('Aggregate Media Files').first().json.files || [];\nconst video = files.find(f => f.media_type === 'video');\nconst thumbnail = files.find(f => f.media_type === 'thumbnail');\n\nconst hook = (ctx.hook_variations || [])[0]?.text || '';\nconst takeaway = ctx.story_arc?.key_takeaway || '';\nconst cta = (ctx.cta_variations || []).find(c => c.platform === ctx.platform) || (ctx.cta_variations || [])[0] || {};\nconst tags = (ctx.seo_data?.tags || []).slice(0, 30);\nconst hashtags = tags.slice(0, 15).map(t => '#' + String(t).replace(/[^a-zA-Z0-9]/g, '')).join(' ');\n\nconst description = [\n  hook,\n  '',\n  takeaway,\n  '',\n  cta.text || '',\n  '',\n  hashtags\n].filter(l => l !== undefined).join('\\n');\n\nreturn [{ json: {\n  record_id: pr.id,\n  asset_id: pr.asset_id,\n  script_id: pr.script_id,\n  channel_id: ctx.channel_id,\n  title: ctx.title,\n  description,\n  tags,\n  video_file_path: video ? video.file_path : null,\n  thumbnail_file_path: thumbnail ? thumbnail.file_path : null\n} }];"
      },
      "id": "c90900000-0009-0009-0009-000000000008",
      "name": "Build YouTube Metadata",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1540,
        500
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=http://media-api:8080/api/v1/file?path={{ $('Build YouTube Metadata').first().json.video_file_path }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "id": "c90900000-0009-0009-0009-000000000010",
      "name": "Download Video Binary",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1980,
        500
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "youTubeOAuth2Api",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "X-Upload-Content-Type",
              "value": "video/mp4"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ snippet: { title: $('Build YouTube Metadata').first().json.title, description: $('Build YouTube Metadata').first().json.description, tags: $('Build YouTube Metadata').first().json.tags, categoryId: '28', defaultLanguage: 'en' }, status: { privacyStatus: 'unlisted', selfDeclaredMadeForKids: false }, alterations: { hasAlterations: true } }) }}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true
            }
          }
        }
      },
      "id": "c90900000-0009-0009-0009-000000000011",
      "name": "Initiate Resumable Upload",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2200,
        500
      ],
      "credentials": {
        "youTubeOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "return [{ json: { location: $json.headers.location }, binary: $('Download Video Binary').first().binary }];"
      },
      "id": "c90900000-0009-0009-0009-000000000025",
      "name": "Merge Upload URL With Binary",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2310,
        500
      ]
    },
    {
      "parameters": {
        "method": "PUT",
        "url": "={{ $json.location }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "youTubeOAuth2Api",
        "sendBody": true,
        "contentType": "binaryData",
        "inputDataFieldName": "data",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "video/mp4"
            }
          ]
        },
        "options": {}
      },
      "id": "c90900000-0009-0009-0009-000000000012",
      "name": "Upload Video Binary",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2420,
        500
      ],
      "credentials": {
        "youTubeOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "leftValue": "={{ $json.id }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "c90900000-0009-0009-0009-000000000013",
      "name": "Upload Success?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        2640,
        500
      ]
    },
    {
      "parameters": {
        "jsCode": "return [{ json: {\n  workflow_name: 'WF-009-publishing',\n  execution_id: String($execution.id),\n  node_name: 'Upload Video Binary',\n  error_type: 'publish_failed',\n  error_message: `YouTube video upload failed: ${JSON.stringify($json.error || $json)}`.slice(0, 1000),\n  payload: { record_id: $('Build YouTube Metadata').first().json.record_id, asset_id: $('Build YouTube Metadata').first().json.asset_id }\n} }];"
      },
      "id": "c90900000-0009-0009-0009-000000000014",
      "name": "Build DLQ Payload",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2860,
        640
      ]
    },
    {
      "parameters": {
        "workflowId": {
          "__rl": true,
          "value": "sub002-dead-letter-queue-0000000",
          "mode": "id"
        },
        "options": {}
      },
      "id": "c90900000-0009-0009-0009-000000000015",
      "name": "Log to SUB-002",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1.1,
      "position": [
        3080,
        640
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=http://media-api:8080/api/v1/file?path={{ $('Build YouTube Metadata').first().json.thumbnail_file_path }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "id": "c90900000-0009-0009-0009-000000000016",
      "name": "Download Thumbnail Binary",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2860,
        400
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://www.googleapis.com/upload/youtube/v3/thumbnails/set?videoId={{ $('Upload Video Binary').first().json.id }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "youTubeOAuth2Api",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "image/png"
            }
          ]
        },
        "sendBody": true,
        "contentType": "binaryData",
        "inputDataFieldName": "data",
        "options": {}
      },
      "id": "c90900000-0009-0009-0009-000000000017",
      "name": "Upload Thumbnail",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3080,
        400
      ],
      "onError": "continueRegularOutput",
      "credentials": {
        "youTubeOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "amount": 1800,
        "unit": "seconds"
      },
      "id": "c90900000-0009-0009-0009-000000000018",
      "name": "Wait 30 Minutes",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        3300,
        400
      ]
    },
    {
      "parameters": {
        "method": "PUT",
        "url": "https://www.googleapis.com/youtube/v3/videos?part=status",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "youTubeOAuth2Api",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ id: $('Upload Video Binary').first().json.id, status: { privacyStatus: 'public' } }) }}",
        "options": {}
      },
      "id": "c90900000-0009-0009-0009-000000000020",
      "name": "Set Video Public",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3740,
        400
      ],
      "credentials": {
        "youTubeOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "=UPDATE publishing_records SET status = 'published', platform_id = '{{ $('Upload Video Binary').first().json.id }}', platform_url = 'https://youtube.com/watch?v={{ $('Upload Video Binary').first().json.id }}', published_at = NOW() WHERE id = {{ $('Build YouTube Metadata').first().json.record_id }} RETURNING id, platform_url",
        "options": {}
      },
      "id": "c90900000-0009-0009-0009-000000000021",
      "name": "Update Publishing Record",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        3960,
        400
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "return [{ json: {\n  message: `\\u2705 Published: ${$json.platform_url}`,\n  parse_mode: 'Markdown',\n  inline_keyboard: []\n} }];"
      },
      "id": "c90900000-0009-0009-0009-000000000022",
      "name": "Build Success Message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        4180,
        400
      ]
    },
    {
      "parameters": {
        "workflowId": {
          "__rl": true,
          "value": "sub003-telegram-notification-000000",
          "mode": "id"
        },
        "options": {}
      },
      "id": "c90900000-0009-0009-0009-000000000023",
      "name": "Notify Success",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1.1,
      "position": [
        4400,
        400
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "=INSERT INTO workflow_logs (workflow_name, execution_id, status, input_summary, output_summary) VALUES ('WF-009-publishing', '{{ $execution.id }}', 'completed', '{{ JSON.stringify({ record_id: $('Build YouTube Metadata').first().json.record_id }) }}'::jsonb, '{{ JSON.stringify({ video_id: $('Upload Video Binary').first().json.id }) }}'::jsonb)",
        "options": {}
      },
      "id": "c90900000-0009-0009-0009-000000000024",
      "name": "Log Result Success",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        4620,
        400
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Schedule 30min": {
      "main": [
        [
          {
            "node": "Poll Publishing Queue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Poll Publishing Queue": {
      "main": [
        [
          {
            "node": "Has Record?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Record?": {
      "main": [
        [
          {
            "node": "Is Time To Publish?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Time To Publish?": {
      "main": [
        [
          {
            "node": "Fetch Full Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Full Context": {
      "main": [
        [
          {
            "node": "Fetch Media Files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Media Files": {
      "main": [
        [
          {
            "node": "Aggregate Media Files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Media Files": {
      "main": [
        [
          {
            "node": "Build YouTube Metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build YouTube Metadata": {
      "main": [
        [
          {
            "node": "Download Video Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Video Binary": {
      "main": [
        [
          {
            "node": "Initiate Resumable Upload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Initiate Resumable Upload": {
      "main": [
        [
          {
            "node": "Merge Upload URL With Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Upload URL With Binary": {
      "main": [
        [
          {
            "node": "Upload Video Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Video Binary": {
      "main": [
        [
          {
            "node": "Upload Success?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Success?": {
      "main": [
        [
          {
            "node": "Download Thumbnail Binary",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build DLQ Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build DLQ Payload": {
      "main": [
        [
          {
            "node": "Log to SUB-002",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Thumbnail Binary": {
      "main": [
        [
          {
            "node": "Upload Thumbnail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Thumbnail": {
      "main": [
        [
          {
            "node": "Wait 30 Minutes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 30 Minutes": {
      "main": [
        [
          {
            "node": "Set Video Public",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Video Public": {
      "main": [
        [
          {
            "node": "Update Publishing Record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Publishing Record": {
      "main": [
        [
          {
            "node": "Build Success Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Success Message": {
      "main": [
        [
          {
            "node": "Notify Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Success": {
      "main": [
        [
          {
            "node": "Log Result Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}

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

WF-009-publishing. Uses postgres, httpRequest. Scheduled trigger; 23 nodes.

Source: https://github.com/navin-labs/content-factory-os/blob/main/n8n/workflows/main/WF-009-publishing.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

Disparador 1.8. Uses itemLists, postgres, emailSend, httpRequest. Scheduled trigger; 85 nodes.

Item Lists, Postgres, Email Send +1
Data & Sheets

공유회_알림톡_크론. Uses postgres, httpRequest, n8n-nodes-solapi. Scheduled trigger; 39 nodes.

Postgres, HTTP Request, N8N Nodes Solapi
Data & Sheets

QuepasaAutomatic. Uses postgres, postgresTrigger, httpRequest. Scheduled trigger; 39 nodes.

Postgres, Postgres Trigger, HTTP Request
Data & Sheets

QuepasaAutomatic. Uses postgres, postgresTrigger, httpRequest. Scheduled trigger; 39 nodes.

Postgres, Postgres Trigger, HTTP Request
Data & Sheets

QuepasaAutomatic. Uses postgres, postgresTrigger, httpRequest. Scheduled trigger; 39 nodes.

Postgres, Postgres Trigger, HTTP Request