{
  "name": "YouTube to PostSider",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "id": "00000000-0000-4000-8000-000000000074",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "resource": "playlistItem",
        "operation": "getAll",
        "playlistId": "[YOUR UPLOADS PLAYLIST ID, starts with UU...]",
        "part": [
          "snippet"
        ],
        "returnAll": false,
        "limit": 1,
        "options": {}
      },
      "id": "00000000-0000-4000-8000-000000000075",
      "name": "1. New video",
      "type": "n8n-nodes-base.youtube",
      "typeVersion": 2,
      "position": [
        220,
        0
      ],
      "credentials": {
        "youTubeOAuth2Api": {
          "id": "5",
          "name": "YouTube account"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "00000000-0000-4000-8000-000000000076",
              "name": "idea",
              "value": "={{ $json.snippet.title }}",
              "type": "string"
            },
            {
              "id": "00000000-0000-4000-8000-000000000077",
              "name": "tone",
              "value": "confident and friendly",
              "type": "string"
            },
            {
              "id": "00000000-0000-4000-8000-000000000078",
              "name": "videoUrl",
              "value": "=https://www.youtube.com/watch?v={{ $json.snippet.resourceId.videoId }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "00000000-0000-4000-8000-000000000079",
      "name": "2. Post idea",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        440,
        0
      ]
    },
    {
      "parameters": {
        "content": "WHAT THIS DOES\nChecks your YouTube uploads on a schedule. Every new video becomes AI-written announcements for X, LinkedIn and Telegram, placed in the PostSider calendar at the next free slot and pushed into the approval queue.\n\nSETUP (once, about 5 minutes)\n1. PostSider API key: app.postsider.com > Settings > API > Create key (ps_...).\n2. n8n credentials (Header Auth): \"PostSider API Key\" (Authorization = ps_...) and \"OpenAI API Key\" (Authorization = Bearer sk-...).\n3. Connect a \"YouTube account\" (OAuth2) credential on \"1. New video\".\n4. Open \"1. New video\" and set your uploads playlist id (starts with UU...; find it on your channel page > Videos > Playlists, or use the Channel > Content details API).\n5. Replace each [YOUR POSTSIDER ... CHANNEL ID] with real channel ids (app > Settings > Channels). Delete legs you do not use.\n\nHOW TO RUN\n1. Activate the workflow. On the schedule it polls for the latest video.\n2. Approve the announcements in the app to publish them at their slots.\n\nSELF-HOSTED POSTSIDER\nReplace api.postsider.com with your domain; add /api if your install serves the API under /api.",
        "height": 700,
        "width": 620,
        "color": 3
      },
      "id": "00000000-0000-4000-8000-000000000095",
      "name": "How to use",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -480
      ]
    },
    {
      "parameters": {
        "content": "SETUP STEP BY STEP\nFollow in order. Do this once per workflow.\n\nSTEP 1 - Create the n8n credentials:\n  a. \"PostSider API Key\" (Header Auth): Name = Authorization, Value = your ps_ key (app.postsider.com > Settings > API > Create key).\n  b. \"OpenAI API Key\" (Header Auth): Name = Authorization, Value = Bearer sk-... (platform.openai.com).\n  c. \"YouTube account\" (Google OAuth2): connect the Google account that owns the channel.\n\nSTEP 2 - \"1. New video\" (YouTube node):\n  Select the \"YouTube account\" credential.\n  Playlist: your uploads playlist id (starts with UU...). Find it on your channel page under Videos > Playlists.\n\nSTEP 3 - Select the \"PostSider API Key\" credential on every node whose name contains \"Find best slot\", \"Create draft\" or \"Request approval\".\n\nSTEP 4 - Select the \"OpenAI API Key\" credential on every \"Generate post (AI)\" node.\n\nSTEP 5 - Replace the channel ids:\n  Replace [YOUR POSTSIDER X CHANNEL ID], [YOUR POSTSIDER LINKEDIN CHANNEL ID] and [YOUR POSTSIDER TELEGRAM CHANNEL ID] with your real channel ids (app > Settings > Channels). Delete the legs of platforms you do not use.\n\nSTEP 6 - Run:\n  Activate the workflow. Upload a test video; the next poll creates the announcements. Approve them in the app to publish.",
        "height": 720,
        "width": 640,
        "color": 3
      },
      "id": "00000000-0000-4000-8000-000000000096",
      "name": "Setup step by step",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -1260
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "https://api.postsider.com/public/v1/find-slot/[YOUR POSTSIDER X CHANNEL ID]"
      },
      "id": "00000000-0000-4000-8000-000000000080",
      "name": "3.1 Find best slot",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        660,
        -220
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "1",
          "name": "PostSider API Key"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/chat/completions",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"gpt-4o-mini\",\n  \"temperature\": 0.8,\n  \"messages\": [\n    { \"role\": \"system\", \"content\": \"You write social media posts for X (Twitter). Rules: Max 280 characters. Announce a new video with a sharp hook, 1-3 hashtags, and the video link. Respond with the post text only.\" },\n    { \"role\": \"user\", \"content\": \"Topic: {{ $('2. Post idea').first().json.idea }}. Tone: {{ $('2. Post idea').first().json.tone }}. Write the post.\" }\n  ]\n}"
      },
      "id": "00000000-0000-4000-8000-000000000081",
      "name": "3.1 Generate post (AI)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        880,
        -220
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "2",
          "name": "OpenAI API Key"
        }
      }
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const slot = $('3.1 Find best slot').first().json; // { date } UTC, append 'Z'\nconst ai = $input.first().json;\nconst content = (ai.choices?.[0]?.message?.content || '').trim();\nreturn [{\n  json: {\n    body: {\n      type: 'draft',\n      date: slot.date + 'Z',\n      shortLink: false,\n      tags: [],\n      posts: [{\n        integration: { id: '[YOUR POSTSIDER X CHANNEL ID]' },\n        value: [{ content, image: [] }],\n        settings: {\"__type\":\"x\"}\n      }]\n    }\n  }\n}];"
      },
      "id": "00000000-0000-4000-8000-000000000082",
      "name": "3.1 Build draft",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        -220
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.postsider.com/public/v1/posts",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.body) }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Idempotency-Key",
              "value": "=ps-{{ $now.toISO() }}"
            }
          ]
        }
      },
      "id": "00000000-0000-4000-8000-000000000083",
      "name": "3.1 Create draft",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1320,
        -220
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "1",
          "name": "PostSider API Key"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.postsider.com/public/v1/posts/{{ $json.postId }}/request-approval"
      },
      "id": "00000000-0000-4000-8000-000000000084",
      "name": "3.1 Request approval",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1540,
        -220
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "1",
          "name": "PostSider API Key"
        }
      }
    },
    {
      "parameters": {
        "method": "GET",
        "url": "https://api.postsider.com/public/v1/find-slot/[YOUR POSTSIDER LINKEDIN CHANNEL ID]"
      },
      "id": "00000000-0000-4000-8000-000000000085",
      "name": "3.2 Find best slot",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        660,
        0
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "1",
          "name": "PostSider API Key"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/chat/completions",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"gpt-4o-mini\",\n  \"temperature\": 0.8,\n  \"messages\": [\n    { \"role\": \"system\", \"content\": \"You write social media posts for LinkedIn. Rules: Professional announcement of a new video: what it covers, who it is for, a takeaway. 2-4 hashtags and the video link. Respond with the post text only.\" },\n    { \"role\": \"user\", \"content\": \"Topic: {{ $('2. Post idea').first().json.idea }}. Tone: {{ $('2. Post idea').first().json.tone }}. Write the post.\" }\n  ]\n}"
      },
      "id": "00000000-0000-4000-8000-000000000086",
      "name": "3.2 Generate post (AI)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        880,
        0
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "2",
          "name": "OpenAI API Key"
        }
      }
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const slot = $('3.2 Find best slot').first().json; // { date } UTC, append 'Z'\nconst ai = $input.first().json;\nconst content = (ai.choices?.[0]?.message?.content || '').trim();\nreturn [{\n  json: {\n    body: {\n      type: 'draft',\n      date: slot.date + 'Z',\n      shortLink: false,\n      tags: [],\n      posts: [{\n        integration: { id: '[YOUR POSTSIDER LINKEDIN CHANNEL ID]' },\n        value: [{ content, image: [] }],\n        settings: {\"__type\":\"linkedin\"}\n      }]\n    }\n  }\n}];"
      },
      "id": "00000000-0000-4000-8000-000000000087",
      "name": "3.2 Build draft",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.postsider.com/public/v1/posts",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.body) }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Idempotency-Key",
              "value": "=ps-{{ $now.toISO() }}"
            }
          ]
        }
      },
      "id": "00000000-0000-4000-8000-000000000088",
      "name": "3.2 Create draft",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1320,
        0
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "1",
          "name": "PostSider API Key"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.postsider.com/public/v1/posts/{{ $json.postId }}/request-approval"
      },
      "id": "00000000-0000-4000-8000-000000000089",
      "name": "3.2 Request approval",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1540,
        0
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "1",
          "name": "PostSider API Key"
        }
      }
    },
    {
      "parameters": {
        "method": "GET",
        "url": "https://api.postsider.com/public/v1/find-slot/[YOUR POSTSIDER TELEGRAM CHANNEL ID]"
      },
      "id": "00000000-0000-4000-8000-000000000090",
      "name": "3.3 Find best slot",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        660,
        220
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "1",
          "name": "PostSider API Key"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/chat/completions",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"gpt-4o-mini\",\n  \"temperature\": 0.8,\n  \"messages\": [\n    { \"role\": \"system\", \"content\": \"You write social media posts for Telegram. Rules: Compact news-style announcement of a new video, 2-4 short lines, clear value, the video link, 1-2 hashtags. Respond with the post text only.\" },\n    { \"role\": \"user\", \"content\": \"Topic: {{ $('2. Post idea').first().json.idea }}. Tone: {{ $('2. Post idea').first().json.tone }}. Write the post.\" }\n  ]\n}"
      },
      "id": "00000000-0000-4000-8000-000000000091",
      "name": "3.3 Generate post (AI)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        880,
        220
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "2",
          "name": "OpenAI API Key"
        }
      }
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const slot = $('3.3 Find best slot').first().json; // { date } UTC, append 'Z'\nconst ai = $input.first().json;\nconst content = (ai.choices?.[0]?.message?.content || '').trim();\nreturn [{\n  json: {\n    body: {\n      type: 'draft',\n      date: slot.date + 'Z',\n      shortLink: false,\n      tags: [],\n      posts: [{\n        integration: { id: '[YOUR POSTSIDER TELEGRAM CHANNEL ID]' },\n        value: [{ content, image: [] }],\n        settings: {\"__type\":\"telegram\"}\n      }]\n    }\n  }\n}];"
      },
      "id": "00000000-0000-4000-8000-000000000092",
      "name": "3.3 Build draft",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        220
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.postsider.com/public/v1/posts",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.body) }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Idempotency-Key",
              "value": "=ps-{{ $now.toISO() }}"
            }
          ]
        }
      },
      "id": "00000000-0000-4000-8000-000000000093",
      "name": "3.3 Create draft",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1320,
        220
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "1",
          "name": "PostSider API Key"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.postsider.com/public/v1/posts/{{ $json.postId }}/request-approval"
      },
      "id": "00000000-0000-4000-8000-000000000094",
      "name": "3.3 Request approval",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1540,
        220
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "1",
          "name": "PostSider API Key"
        }
      }
    }
  ],
  "connections": {
    "3.1 Find best slot": {
      "main": [
        [
          {
            "node": "3.1 Generate post (AI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3.1 Generate post (AI)": {
      "main": [
        [
          {
            "node": "3.1 Build draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3.1 Build draft": {
      "main": [
        [
          {
            "node": "3.1 Create draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3.1 Create draft": {
      "main": [
        [
          {
            "node": "3.1 Request approval",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3.2 Find best slot": {
      "main": [
        [
          {
            "node": "3.2 Generate post (AI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3.2 Generate post (AI)": {
      "main": [
        [
          {
            "node": "3.2 Build draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3.2 Build draft": {
      "main": [
        [
          {
            "node": "3.2 Create draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3.2 Create draft": {
      "main": [
        [
          {
            "node": "3.2 Request approval",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3.3 Find best slot": {
      "main": [
        [
          {
            "node": "3.3 Generate post (AI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3.3 Generate post (AI)": {
      "main": [
        [
          {
            "node": "3.3 Build draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3.3 Build draft": {
      "main": [
        [
          {
            "node": "3.3 Create draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3.3 Create draft": {
      "main": [
        [
          {
            "node": "3.3 Request approval",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "1. New video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1. New video": {
      "main": [
        [
          {
            "node": "2. Post idea",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2. Post idea": {
      "main": [
        [
          {
            "node": "3.1 Find best slot",
            "type": "main",
            "index": 0
          },
          {
            "node": "3.2 Find best slot",
            "type": "main",
            "index": 0
          },
          {
            "node": "3.3 Find best slot",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "active": false,
  "tags": [
    "PostSider",
    "YouTube",
    "AI",
    "Video"
  ]
}