AutomationFlowsWeb Scraping › Pf Wf-02 Caption Generator V18.3

Pf Wf-02 Caption Generator V18.3

PF WF-02 Caption Generator v18.3. Uses microsoftSharePoint, httpRequest. Scheduled trigger; 38 nodes.

Cron / scheduled trigger★★★★★ complexity38 nodesMicrosoft SharePointHTTP Request
Web Scraping Trigger: Cron / scheduled Nodes: 38 Complexity: ★★★★★ Added:

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": "PF WF-02 Caption Generator v18.3",
  "nodes": [
    {
      "parameters": {
        "content": "## WF-02 v18.3 \u2014 Vollautomatik mit variablem Foto\n\n**Was tut dieser Workflow:**\nSt\u00fcndlich alle SharePoint-Items mit `Status=Entwurf` verarbeiten:\n1. Caption + Hashtags via Claude generieren\n2. HWG-Filter pr\u00fcfen (Heilversprechen blockieren)\n3. **NEU v18.3:** Item-spezifisches Praxis-Foto aus SP fetchen + nach GitHub pushen\n4. HTML-Template rendern (Gotenberg) \u2014 Instagram + Facebook, jetzt mit variablem `{{BACKGROUND_PHOTO}}`\n5. Bilder in SharePoint Doc-Library `Social-Media-Assets` hochladen\n6. Status setzen: `Bereit` / `Geblockt` / `Wartet-auf-Avatar`\n\n**Avatar-Posts** (Reel/\u00dcbung/Story) parken bis `AVATAR_ENABLED=true`.\n\n**KEIN Teams-Wait** \u2014 Auto-Release. 24h-Karenz macht WF-03.\n\n**Build-Spec:** `02_n8n-Workflows/WF-02_v18.3_Foto-Branch_Spec.md`\n**Foto-Convention:** `04_Canva-Vorlagen/Foto-Convention.md`",
        "height": 380,
        "width": 480,
        "color": 4
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -2800,
        80
      ],
      "id": "c4d0e21c-9942-46a8-89d1-43292a6c5619",
      "name": "Doku: Overview"
    },
    {
      "parameters": {
        "content": "## Konstanten (in Code-Nodes dupliziert)\n\n```javascript\nconst AVATAR_ENABLED = false;\nconst AVATAR_PROVIDER = \"d-id\";\nconst AVATAR_ID = \"judith_v1\";\n\nconst GOTENBERG_URL = \"http://gotenberg:3000/forms/chromium/screenshot/html\";\n\nconst SP_SITE_URL = \"https://physiofuchs889.sharepoint.com/sites/PhysioFuchsTW\";\nconst SP_LIST_GUID = \"d17a6a6f-e6ef-457d-a2a9-4c30ea56120f\";\nconst SP_DOCLIB_GUID = \"c1f0a171-5df2-4c8b-9ab4-db3ed8e76266\";\nconst SP_DOCLIB_NAME = \"Social-Media-Assets\";\n\n// NEU v18.3: SP-Standort der Praxis-Fotos (verifiziert 2026-06-08)\n// Gleiche Site wie Content-Kalender, deutsche Library \"Freigegebene Dokumente\"\nconst SP_FOTOS_SITE = \"https://physiofuchs889.sharepoint.com/sites/PhysioFuchsTW\";\nconst SP_FOTOS_FOLDER_REL = \"/sites/PhysioFuchsTW/Freigegebene Dokumente/Socialmedia/Content_Socialmedia/Fotos/{year}\";\n\nconst REPO_RAW = \"https://raw.githubusercontent.com/twaese/Physio-Fuchs-Social-Media-Automation/main\";\nconst IG_TPL = REPO_RAW + \"/04_Canva-Vorlagen/html-templates/instagram/PF_Feed_Standard.html\";\nconst FB_TPL = REPO_RAW + \"/04_Canva-Vorlagen/html-templates/facebook/PF_Feed_FB_Standard.html\";\nconst FALLBACK_PHOTO = REPO_RAW + \"/04_Canva-Vorlagen/html-templates/assets/judith-behandlung-01.png\";\n```",
        "height": 660,
        "width": 480,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -2800,
        480
      ],
      "id": "dbb3f93d-df15-48c5-a84a-2768458f966c",
      "name": "Doku: Konstanten"
    },
    {
      "parameters": {
        "content": "## NEU v18.3: Foto-Branch\n\n**Vor `Code: HTML bauen (IG+FB)` eingeschoben.**\n\nLogik:\n1. **Foto-Pfad bauen** \u2014 Year + ItemID-3stellig\n2. **SP: Fotos listen** \u2014 alle Files im Jahres-Ordner\n3. **Foto-Match** \u2014 sucht `PF_{year}_{id}_*.{jpg|png}`\n4. **If: Foto gefunden?**\n   - true \u2192 Download \u2192 GitHub Body \u2192 GitHub Push \u2192 HTML bauen\n   - false \u2192 direkt HTML bauen (mit Fallback-URL)\n\n**Fallback-Kaskade:**\n- Item-Foto (wenn vorhanden)\n- \u2193 Stock-Pool (TODO v18.4)\n- \u2193 `judith-behandlung-01.png` (last resort)\n\nDetails: `WF-02_v18.3_Foto-Branch_Spec.md`",
        "height": 360,
        "width": 360,
        "color": 3
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -224,
        -560
      ],
      "id": "fb18a3a0-1111-4444-bbbb-222233334444",
      "name": "Doku: v18.3 Foto-Branch"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -2272,
        304
      ],
      "id": "e1e0400a-ba2c-46d3-9b9c-471e743ea3bb",
      "name": "Cron: st\u00fcndlich"
    },
    {
      "parameters": {
        "resource": "item",
        "site": {
          "__rl": true,
          "value": "physiofuchs889.sharepoint.com,c74040a2-0d6b-4385-b514-fbd9830a56ac,7a7842d6-ea6c-440d-aeed-580bdd2a091c",
          "mode": "list",
          "cachedResultName": "Physio Fuchs Administration"
        },
        "list": {
          "__rl": true,
          "value": "d17a6a6f-e6ef-457d-a2a9-4c30ea56120f",
          "mode": "list",
          "cachedResultName": "PF-Content-Kalender-2026"
        },
        "options": {
          "fields": [
            "fields"
          ]
        },
        "simplify": false,
        "requestOptions": {}
      },
      "type": "n8n-nodes-base.microsoftSharePoint",
      "typeVersion": 1,
      "position": [
        -2048,
        304
      ],
      "id": "323b83ea-fe8a-4e0c-b150-b122ebce4fa3",
      "name": "SP: Entw\u00fcrfe holen",
      "credentials": {
        "microsoftSharePointOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Validate + Route \u2014 Produktionsmodus\nconst items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n  const f = item.json.fields || {};\n  const postTyp = f.field_2 || f.Field_2 || '';\n  const status  = f.field_6 || f.Field_6 || '';\n  const itemId  = item.json.Ausweis || item.json.id || f.AUSWEIS || f.id;\n  const title   = f.Title || f.Titel || '';\n\n  if (status !== 'Entwurf') continue;\n  if (!postTyp) continue;\n\n  // Avatar noch nicht aktiv \u2192 diese Typen komplett \u00fcberspringen\n  const avatarTypes = ['Reel', '\u00dcbung', 'Story', 'Uebung'];\n  if (avatarTypes.includes(postTyp)) continue;\n  const branch = 'feed';\n\n  results.push({\n    json: {\n      _sp_item_id: itemId,\n      _post_typ: postTyp,\n      _branch: branch,\n      sp_fields: f,\n    }\n  });\n}\n\nreturn results.slice(0, 1);"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1824,
        304
      ],
      "id": "f9581575-9071-4805-8dfd-27bdf8edb34b",
      "name": "Code: Validate + Route"
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json._branch }}",
                    "rightValue": "feed",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "15321ac6-ae9f-47a2-8f77-aedf4f3ea948"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "feed"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json._branch }}",
                    "rightValue": "avatar",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "5b80995d-d2dd-464a-a791-70008308ecf1"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "avatar"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        -1600,
        304
      ],
      "id": "0d42075f-c660-4287-945c-9598f772b19f",
      "name": "Switch: Feed/Avatar"
    },
    {
      "parameters": {
        "jsCode": "// Build Claude Payload f\u00fcr Caption-Generierung\nconst f = $json.sp_fields;\nconst pt = $json._post_typ;\n\n// Echte Field-Namen aus SharePoint:\n//  field_1 = Thema, field_3 = Content_Brief, field_7 = Hashtag_Thema\nconst thema       = f.field_1 || '';\nconst contentBrief = f.field_3 || '';\nconst hashtagThema = f.field_7 || '';\n\nconst PROMPT = `Du bist Texter f\u00fcr die Physio Fuchs Praxis in Ratingen-Lintorf.\nErzeuge eine Instagram-Caption f\u00fcr folgenden Post-Brief.\n\nPost-Typ: ${pt}\nThema: ${thema}\nContent-Brief: ${contentBrief}\nHashtag-Thema: ${hashtagThema}\n\nTonalit\u00e4t-Regeln (verbindlich):\n- \"Sie\"-Form, professionell, sympathisch, motivierend\n- KEINE Heilversprechen \u2014 verboten: \"heilt\", \"garantiert\", \"kuriert\", \"schmerzfrei in X Tagen\", \"beseitigt\"\n- KEINE Diagnosen\n- Maximal 3 Emojis, sparsam, fachlich passend\n- Bei \u00dcbungen: Hinweis erg\u00e4nzen \"Bei anhaltenden Beschwerden bitte \u00e4rztlich abkl\u00e4ren lassen\"\n- Inklusiv (\"Patientinnen und Patienten\", nicht Gender-Stern)\n\nAntworte AUSSCHLIESSLICH als JSON, ohne Markdown-Wrapping:\n{\n  \"titel\": \"<Bild-Headline, max 50 Zeichen>\",\n  \"text\": \"<Bild-Body, 2-3 kurze S\u00e4tze, max 200 Zeichen>\",\n  \"cta\": \"<Bild-CTA, max 50 Zeichen>\",\n  \"caption\": \"<volle Instagram-Caption, 2-3 Abs\u00e4tze, endet mit Frage>\",\n  \"hashtags\": [\"#tag1\", \"#tag2\", \"...\"]\n}\nHashtags: 8-15 St\u00fcck, mind. 2 lokale (Ratingen / Lintorf).`;\n\nreturn [{\n  json: {\n    _passthrough: $json,\n    model: 'claude-sonnet-4-6',\n    max_tokens: 2048,\n    messages: [{ role: 'user', content: PROMPT }]\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1344,
        160
      ],
      "id": "635cdabb-785e-4021-8ecc-e7211f34adb5",
      "name": "Code: Build Claude Payload"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $env.ANTHROPIC_API_KEY }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"{{ $json.model }}\",\n  \"max_tokens\": {{ $json.max_tokens }},\n  \"messages\": {{ JSON.stringify($json.messages) }}\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -1120,
        160
      ],
      "id": "db72f350-bb1b-4f27-bbc6-e24861c6738d",
      "name": "HTTP: Claude Caption"
    },
    {
      "parameters": {
        "jsCode": "// Parse Claude-Response, JSON extrahieren\nconst resp = $json;\nconst raw = resp.content?.[0]?.text || resp.completion || '';\n\nlet parsed;\ntry {\n  const match = raw.match(/\\{[\\s\\S]*\\}/);\n  parsed = JSON.parse(match ? match[0] : raw);\n} catch (e) {\n  throw new Error('Claude-Response nicht JSON-parsebar: ' + raw.slice(0, 200));\n}\n\nconst pt = $node['Code: Build Claude Payload'].json._passthrough;\n\nreturn [{\n  json: {\n    ...pt,\n    titel:    parsed.titel    || '',\n    text:     parsed.text     || '',\n    cta:      parsed.cta      || '',\n    caption:  parsed.caption  || '',\n    hashtags: Array.isArray(parsed.hashtags) ? parsed.hashtags : [],\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -896,
        160
      ],
      "id": "7485b3e4-1f99-4e83-80e9-a17ccb2a72cb",
      "name": "Code: Parse Claude"
    },
    {
      "parameters": {
        "jsCode": "// HWG-Filter: Caption gegen Heilversprechen-Blacklist pr\u00fcfen\nconst HWG_PATTERNS = [\n  /\\bheilt\\b/i,\n  /\\bheilung\\b/i,\n  /\\bkuriert\\b/i,\n  /\\bgarantiert\\b/i,\n  /\\bgew\u00e4hrleistet?\\b/i,\n  /\\bverspricht\\b/i,\n  /\\bversprechen wir\\b/i,\n  /\\b(schmerzfrei|beschwerdefrei) in (\\d+|wenigen|k\u00fcrzester) (tag|tage|tagen|woche|wochen)\\b/i,\n  /\\bwirkt (100 ?%|sicher|garantiert)\\b/i,\n  /\\bbeseitigt (schmerz|beschwerd|leiden)\\b/i,\n  /\\b(stellt|stellen wir) (ihre )?diagnose\\b/i,\n  /\\bbehandelt erfolgreich\\b/i,\n  /\\b(nebenwirkungsfrei|ohne nebenwirkungen)\\b/i,\n  /\\bbesser als (\u00e4rzt|operation|medikament)\\b/i,\n  /\\b(ersetzt|statt) (arzt|\u00e4rzt|operation|medikament)\\b/i,\n];\n\nconst f = $json;\nconst fullText = [f.titel, f.text, f.cta, f.caption].filter(Boolean).join(' ');\n\nconst matches = HWG_PATTERNS\n  .map(re => ({ pattern: re.source, hit: fullText.match(re) }))\n  .filter(x => x.hit);\n\nreturn [{\n  json: {\n    ...f,\n    _hwg_matches: matches.map(m => m.hit[0]),\n    _hwg_clean: matches.length === 0,\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -672,
        160
      ],
      "id": "3a470a81-b6f4-4f3d-b5b1-95771d2cb58c",
      "name": "Code: HWG-Filter"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "leftValue": "={{ $json._hwg_clean }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "id": "d126991f-52e9-4e8a-8898-988600949de4"
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -448,
        160
      ],
      "id": "e67f4a36-fa9d-48b5-9cfb-e55f1a06d293",
      "name": "If: HWG sauber?"
    },
    {
      "parameters": {
        "jsCode": "// === v18.3 Foto-Branch Node 1 ===\n// Berechnet Suchparameter f\u00fcr SP-Foto-Lookup.\nconst hwg = $('Code: HWG-Filter').first().json;\nconst validate = $('Code: Validate + Route').first().json;\n\nconst itemId = String(validate._sp_item_id).padStart(3, '0');\n\n// Jahr aus Ver\u00f6ffentlichungsdatum, sonst aktuelles Jahr\nconst datumRaw = hwg.sp_fields?.Ver_x00f6_ffentlichungsdatum;\nconst year = datumRaw ? new Date(datumRaw).getFullYear() : new Date().getFullYear();\n\n// SP-Pfad (verifiziert 2026-06-08): gleiche Site wie Content-Kalender\nconst SP_FOTOS_SITE = 'https://physiofuchs889.sharepoint.com/sites/PhysioFuchsTW';\nconst SP_FOTOS_FOLDER_REL = `/sites/PhysioFuchsTW/Freigegebene Dokumente/Socialmedia/Content_Socialmedia/Fotos/${year}`;\n\n// SP REST API URL f\u00fcr Folder-Listing\n// HINWEIS: SP-Pfad-Encoding ist tricky bei Leerzeichen. Wir nutzen einfaches Quoting,\n// nicht encodeURIComponent \u2014 sonst werden Slashes auch encodiert.\nconst sp_list_url = `${SP_FOTOS_SITE}/_api/web/GetFolderByServerRelativeUrl('${SP_FOTOS_FOLDER_REL}')/Files`;\n\nconst filename_prefix = `PF_${year}_${itemId}_`;\n\nreturn [{\n  json: {\n    itemId,\n    year,\n    sp_fotos_site: SP_FOTOS_SITE,\n    sp_fotos_folder_rel: SP_FOTOS_FOLDER_REL,\n    sp_list_url,\n    filename_prefix,\n    // Original-Daten durchreichen\n    ...hwg,\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -224,
        80
      ],
      "id": "fb18a301-0001-0001-0001-000000000001",
      "name": "Code: Foto-Pfad bauen"
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $json.sp_list_url }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "microsoftSharePointOAuth2Api",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json;odata=verbose"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        0,
        80
      ],
      "id": "fb18a302-0002-0002-0002-000000000002",
      "name": "HTTP: SP Fotos listen",
      "credentials": {
        "microsoftSharePointOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// === v18.3 Foto-Branch Node 3 ===\n// Sucht in SP-Folder-Listing die zur ItemID passende Foto-Datei.\nconst cfg = $('Code: Foto-Pfad bauen').first().json;\nconst prefix = cfg.filename_prefix;\n\n// SP-REST gibt Files in d.results zur\u00fcck (verbose-Format)\nconst resp = $input.first().json || {};\nlet files = [];\nif (resp.d?.results) files = resp.d.results;\nelse if (Array.isArray(resp.value)) files = resp.value;\nelse if (Array.isArray(resp)) files = resp;\n\n// Match per Filename-Prefix\nconst matches = files\n  .filter(f => (f.Name || f.name || '').startsWith(prefix))\n  .sort((a, b) => (a.Name || a.name || '').localeCompare(b.Name || b.name || ''));\n\nconst REPO_RAW = 'https://raw.githubusercontent.com/twaese/Physio-Fuchs-Social-Media-Automation/main';\nconst FALLBACK_URL = `${REPO_RAW}/04_Canva-Vorlagen/html-templates/assets/judith-behandlung-01.png`;\n\nif (matches.length > 0) {\n  const m = matches[0];\n  const name = m.Name || m.name;\n  const serverRel = m.ServerRelativeUrl || m.serverRelativeUrl;\n  // Vorhersagbare GitHub-URL nach dem Push\n  const gh_path = `04_Canva-Vorlagen/html-templates/assets/photos/${name}`;\n  const gh_url = `${REPO_RAW}/${gh_path}`;\n\n  return [{\n    json: {\n      ...cfg,\n      photo_found: true,\n      photo_name: name,\n      photo_sp_server_rel: serverRel,\n      photo_gh_path: gh_path,\n      final_photo_url: gh_url,\n      _match_log: `Foto gefunden: ${name}`,\n    }\n  }];\n}\n\n// Kein Item-Foto \u2192 Fallback\nreturn [{\n  json: {\n    ...cfg,\n    photo_found: false,\n    photo_name: null,\n    final_photo_url: FALLBACK_URL,\n    _match_log: `Kein Item-Foto (prefix \"${prefix}\" in ${files.length} Files) \u2192 Fallback`,\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        224,
        80
      ],
      "id": "fb18a303-0003-0003-0003-000000000003",
      "name": "Code: Foto-Match"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "leftValue": "={{ $json.photo_found }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "id": "fb18a304-true-cond-0001-000000000001"
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        448,
        80
      ],
      "id": "fb18a304-0004-0004-0004-000000000004",
      "name": "If: Foto gefunden?"
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $json.sp_fotos_site }}/_api/web/GetFileByServerRelativeUrl('{{ encodeURIComponent($json.photo_sp_server_rel) }}')/$value",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "microsoftSharePointOAuth2Api",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "*/*"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        672,
        0
      ],
      "id": "fb18a305-0005-0005-0005-000000000005",
      "name": "HTTP: SP Foto Download",
      "credentials": {
        "microsoftSharePointOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// === v18.3 Foto-Branch Node 5 ===\n// SP-Foto-Binary f\u00fcr GitHub-PUT vorbereiten.\nconst binary = $input.first().binary?.data;\nif (!binary || !binary.data) {\n  throw new Error('Kein Binary von SP-Foto-Download erhalten');\n}\n\nconst cfg = $('Code: Foto-Match').first().json;\n\nreturn [{\n  json: {\n    repo_path: cfg.photo_gh_path,\n    filename: cfg.photo_name,\n    public_url: cfg.final_photo_url,\n    github_body: {\n      message: `WF-02 v18.3: Praxis-Foto ${cfg.photo_name} f\u00fcr PF-${cfg.year}-${cfg.itemId}`,\n      content: binary.data,\n    },\n    _passthrough: cfg,\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        896,
        0
      ],
      "id": "fb18a306-0006-0006-0006-000000000006",
      "name": "Code: Foto \u2192 GitHub Body"
    },
    {
      "parameters": {
        "method": "PUT",
        "url": "=https://api.github.com/repos/twaese/Physio-Fuchs-Social-Media-Automation/contents/{{ $json.repo_path }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.GITHUB_TOKEN }}"
            },
            {
              "name": "Accept",
              "value": "application/vnd.github+json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.github_body) }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1120,
        0
      ],
      "id": "fb18a307-0007-0007-0007-000000000007",
      "name": "HTTP: GitHub-Push Foto"
    },
    {
      "parameters": {
        "jsCode": "// === v18.3 Foto-Branch Node 7 (Merge nach IF-Branch) ===\n// Vereint True- und False-Pfad: nimmt final_photo_url, schiebt Daten weiter.\nconst inputs = $input.all();\n// Eines der beiden Items kommt durch \u2014 egal welches Branch.\nconst data = inputs[0]?.json || {};\n\n// final_photo_url ist in Code: Foto-Match gesetzt, in beiden Branches verf\u00fcgbar\nconst matchData = $('Code: Foto-Match').first().json;\n\nreturn [{\n  json: {\n    ...matchData,\n    // Diagnose: woher kommt das Foto?\n    _foto_branch_taken: matchData.photo_found ? 'item-foto' : 'fallback',\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1344,
        80
      ],
      "id": "fb18a308-0008-0008-0008-000000000008",
      "name": "Code: Foto-URL final"
    },
    {
      "parameters": {
        "jsCode": "// HTML-Templates fetchen + Platzhalter f\u00fcllen f\u00fcr IG und FB\n// v18.3: BACKGROUND_PHOTO-Platzhalter neu (variabel pro Content)\nconst REPO_RAW = 'https://raw.githubusercontent.com/twaese/Physio-Fuchs-Social-Media-Automation/main';\nconst IG_TPL = REPO_RAW + '/04_Canva-Vorlagen/html-templates/instagram/PF_Feed_Standard.html';\nconst FB_TPL = REPO_RAW + '/04_Canva-Vorlagen/html-templates/facebook/PF_Feed_FB_Standard.html';\nconst ASSETS_BASE = REPO_RAW + '/04_Canva-Vorlagen/html-templates/assets/';\nconst FALLBACK_PHOTO = REPO_RAW + '/04_Canva-Vorlagen/html-templates/assets/judith-behandlung-01.png';\n\nconst f = $json;\nconst datum = new Date().toLocaleDateString('de-DE', {\n  day: '2-digit', month: '2-digit', year: 'numeric'\n});\n\nasync function fetchTemplate(url) {\n  const r = await this.helpers.httpRequest({ method: 'GET', url, returnFullResponse: false });\n  return typeof r === 'string' ? r : (r.body || r);\n}\n\nfunction substitute(html, vars) {\n  // Asset-Pfade auf absolute GitHub-Raw-URLs umschreiben (NUR f\u00fcr Logo/Ginkgo etc.)\n  html = html.replace(/src=\"\\.\\.\\/assets\\//g, `src=\"${ASSETS_BASE}`);\n  // Platzhalter ersetzen\n  for (const [k, v] of Object.entries(vars)) {\n    html = html.replaceAll('{{' + k + '}}', String(v));\n  }\n  return html;\n}\n\nconst vars = {\n  TITEL: f.titel || '',\n  TEXT:  f.text  || '',\n  CTA:   f.cta   || '',\n  DATUM: datum,\n  // NEU v18.3: Hintergrund-Foto-URL aus Foto-Branch\n  BACKGROUND_PHOTO: f.final_photo_url || FALLBACK_PHOTO,\n};\n\nconst igTplRaw = await fetchTemplate.call(this, IG_TPL);\nconst fbTplRaw = await fetchTemplate.call(this, FB_TPL);\n\nconst igHtml = substitute(igTplRaw, vars);\nconst fbHtml = substitute(fbTplRaw, vars);\n\nreturn [{\n  json: {\n    ...f,\n    _datum: datum,\n    _bg_photo: vars.BACKGROUND_PHOTO,\n    _ig_html: igHtml,\n    _fb_html: fbHtml,\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1568,
        80
      ],
      "id": "a72f08f1-2cf9-441c-93aa-136ba3b8f736",
      "name": "Code: HTML bauen (IG+FB)"
    },
    {
      "parameters": {
        "jsCode": "// HTML als Binary File f\u00fcr Gotenberg-Multipart verf\u00fcgbar machen\nconst html = $json._ig_html;\nconst buffer = Buffer.from(html, 'utf8');\nreturn [{\n  json: $json,\n  binary: {\n    ig_html_file: await this.helpers.prepareBinaryData(buffer, 'index.html', 'text/html')\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1792,
        0
      ],
      "id": "7c09c49d-0fef-481b-9368-d8bd6e74177a",
      "name": "Code: IG HTML\u2192Binary"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://gotenberg:3000/forms/chromium/screenshot/html",
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "parameterType": "formBinaryData",
              "name": "files",
              "inputDataFieldName": "ig_html_file"
            },
            {
              "name": "format",
              "value": "png"
            },
            {
              "name": "width",
              "value": "1080"
            },
            {
              "name": "height",
              "value": "1350"
            },
            {
              "name": "optimizeForSpeed",
              "value": "false"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2016,
        0
      ],
      "id": "da7333e0-8ad2-4ea1-9463-7c49444b1f16",
      "name": "HTTP: Gotenberg IG"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://physiofuchs889.sharepoint.com/sites/PhysioFuchsTW/_api/web/lists(guid'c1f0a171-5df2-4c8b-9ab4-db3ed8e76266')/RootFolder/Files/add(url='{{ $('Code: Validate + Route').item.json._sp_item_id }}_ig.png',overwrite=true)",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "microsoftSharePointOAuth2Api",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json;odata=verbose"
            },
            {
              "name": "Content-Type",
              "value": "application/octet-stream"
            }
          ]
        },
        "sendBody": true,
        "contentType": "binaryData",
        "inputDataFieldName": "data",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2240,
        0
      ],
      "id": "45a314ff-476d-4e75-b74e-bb4e790f7d30",
      "name": "SP: Upload IG-Bild",
      "credentials": {
        "microsoftSharePointOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// HTML als Binary f\u00fcr FB-Gotenberg\nconst html = $node['Code: HTML bauen (IG+FB)'].json._fb_html;\nconst buffer = Buffer.from(html, 'utf8');\nreturn [{\n  json: $json,\n  binary: {\n    fb_html_file: await this.helpers.prepareBinaryData(buffer, 'index.html', 'text/html')\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1792,
        160
      ],
      "id": "9ea645c0-df81-4d09-8604-98ce1cb8d703",
      "name": "Code: FB HTML\u2192Binary"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://gotenberg:3000/forms/chromium/screenshot/html",
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "parameterType": "formBinaryData",
              "name": "files",
              "inputDataFieldName": "fb_html_file"
            },
            {
              "name": "format",
              "value": "png"
            },
            {
              "name": "width",
              "value": "1080"
            },
            {
              "name": "height",
              "value": "1080"
            },
            {
              "name": "optimizeForSpeed",
              "value": "false"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2016,
        160
      ],
      "id": "8b4e896b-e257-4ddc-9ae5-6cdd37023245",
      "name": "HTTP: Gotenberg FB"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://physiofuchs889.sharepoint.com/sites/PhysioFuchsTW/_api/web/lists(guid'c1f0a171-5df2-4c8b-9ab4-db3ed8e76266')/RootFolder/Files/add(url='{{ $('Code: Validate + Route').item.json._sp_item_id }}_fb.png',overwrite=true)",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "microsoftSharePointOAuth2Api",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json;odata=verbose"
            },
            {
              "name": "Content-Type",
              "value": "application/octet-stream"
            }
          ]
        },
        "sendBody": true,
        "contentType": "binaryData",
        "inputDataFieldName": "data",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2240,
        160
      ],
      "id": "03a3ddf1-d313-4dc5-913d-f2f86564e1bc",
      "name": "SP: Upload FB-Bild",
      "credentials": {
        "microsoftSharePointOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Gotenberg-Binary f\u00fcr GitHub-PUT vorbereiten (IG)\nconst binary = $input.first().binary?.data;\nif (!binary || !binary.data) {\n  throw new Error('Kein Binary von Gotenberg IG erhalten');\n}\n\nconst itemId = $('Code: Validate + Route').item.json._sp_item_id;\nconst ts = new Date().toISOString().replace(/[:T.]/g, '-').slice(0, 19);\nconst filename = `${itemId}_ig_${ts}.png`;\nconst path = `04_Canva-Vorlagen/generated-posts/${filename}`;\n\nreturn [{\n  json: {\n    repo_path: path,\n    filename: filename,\n    public_url: `https://raw.githubusercontent.com/twaese/Physio-Fuchs-Social-Media-Automation/main/${path}`,\n    github_body: {\n      message: `WF-02 auto-post ${itemId} IG ${ts}`,\n      content: binary.data,\n    },\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2240,
        304
      ],
      "id": "25c6ff1f-24ca-4363-8da6-398fd7712b34",
      "name": "Code: IG \u2192 GitHub Body"
    },
    {
      "parameters": {
        "jsCode": "// Gotenberg-Binary f\u00fcr GitHub-PUT vorbereiten (FB)\nconst binary = $input.first().binary?.data;\nif (!binary || !binary.data) {\n  throw new Error('Kein Binary von Gotenberg FB erhalten');\n}\n\nconst itemId = $('Code: Validate + Route').item.json._sp_item_id;\nconst ts = new Date().toISOString().replace(/[:T.]/g, '-').slice(0, 19);\nconst filename = `${itemId}_fb_${ts}.png`;\nconst path = `04_Canva-Vorlagen/generated-posts/${filename}`;\n\nreturn [{\n  json: {\n    repo_path: path,\n    filename: filename,\n    public_url: `https://raw.githubusercontent.com/twaese/Physio-Fuchs-Social-Media-Automation/main/${path}`,\n    github_body: {\n      message: `WF-02 auto-post ${itemId} FB ${ts}`,\n      content: binary.data,\n    },\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2256,
        528
      ],
      "id": "15fc6b10-7af5-4afa-b9b3-1b311a6b7f1c",
      "name": "Code: FB \u2192 GitHub Body"
    },
    {
      "parameters": {
        "method": "PUT",
        "url": "=https://api.github.com/repos/twaese/Physio-Fuchs-Social-Media-Automation/contents/{{ $json.repo_path }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.GITHUB_TOKEN }}"
            },
            {
              "name": "Accept",
              "value": "application/vnd.github+json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.github_body) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2464,
        224
      ],
      "id": "9b84ca58-f0b4-4204-813e-36a3d13a0f39",
      "name": "GitHub: Push IG"
    },
    {
      "parameters": {
        "method": "PUT",
        "url": "=https://api.github.com/repos/twaese/Physio-Fuchs-Social-Media-Automation/contents/{{ $json.repo_path }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.GITHUB_TOKEN }}"
            },
            {
              "name": "Accept",
              "value": "application/vnd.github+json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.github_body) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2464,
        432
      ],
      "id": "1f9d0637-6b8f-4c64-ba7f-ecd5fd43425c",
      "name": "GitHub: Push FB"
    },
    {
      "parameters": {
        "jsCode": "const f = $('Code: HWG-Filter').first().json;\nconst itemId = $('Code: Validate + Route').first().json._sp_item_id;\n\n// GitHub-Raw-URLs (public, von Meta ladbar) statt SharePoint-internen URLs\n// .first() statt .item \u2014 Multi-Branch-Merge sonst undefined\nlet igUrl = '', fbUrl = '';\ntry { igUrl = $('Code: IG \u2192 GitHub Body').first().json.public_url || ''; } catch(e) { console.log('IG-URL-Lookup failed:', e.message); }\ntry { fbUrl = $('Code: FB \u2192 GitHub Body').first().json.public_url || ''; } catch(e) { console.log('FB-URL-Lookup failed:', e.message); }\n\n// v18.3: Foto-Branch-Diagnose mitloggen\nlet fotoLog = '';\ntry {\n  const fm = $('Code: Foto-Match').first().json;\n  fotoLog = fm.photo_found ? `Foto: ${fm.photo_name}` : 'Foto: Fallback';\n} catch (e) { fotoLog = 'Foto: ?'; }\n\nconst hashtagBlock = (f.hashtags || []).join(' ');\nconst captionPure = f.caption;   // OHNE Hashtags \u2014 WF-03 h\u00e4ngt sie f\u00fcr IG einmalig dran\n\nconst now = new Date().toISOString().replace('T', ' ').slice(0, 16);\nconst logEntry = `\\n${now} WF-02 v18.3: Caption+Bild fertig, HWG sauber, ${fotoLog}, GitHub-Push IG${igUrl?'\u2713':'\u2717'}/FB${fbUrl?'\u2713':'\u2717'}, Status Bereit`;\nconst existingLog = f.sp_fields.field_13 || '';\n\nreturn [{\n  json: {\n    _sp_item_id: itemId,\n    payload: {\n      field_10: captionPure,\n      field_7:  hashtagBlock,\n      field_6:  'Bereit',\n      field_8:  igUrl,\n      Bild_FB_Dateiname: fbUrl,\n      field_13: existingLog + logEntry,\n    }\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2464,
        80
      ],
      "id": "a2978a89-a477-4336-9a11-1b89f0ab87de",
      "name": "Code: Merge + Update-Payload"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://physiofuchs889.sharepoint.com/sites/PhysioFuchsTW/_api/web/lists(guid'd17a6a6f-e6ef-457d-a2a9-4c30ea56120f')/items({{ $json._sp_item_id }})",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "microsoftSharePointOAuth2Api",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Accept",
              "value": "application/json"
            },
            {
              "name": "X-HTTP-Method",
              "value": "MERGE"
            },
            {
              "name": "IF-MATCH",
              "value": "*"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.payload) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2688,
        80
      ],
      "id": "2dddb2e9-0cef-48eb-9831-502760e1976c",
      "name": "SP: Status Bereit",
      "credentials": {
        "microsoftSharePointOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://physiofuchs889.sharepoint.com/sites/PhysioFuchsTW/_api/web/lists(guid'd17a6a6f-e6ef-457d-a2a9-4c30ea56120f')/items({{ $('Code: Validate + Route').item.json._sp_item_id }})",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "microsoftSharePointOAuth2Api",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Accept",
              "value": "application/json"
            },
            {
              "name": "X-HTTP-Method",
              "value": "MERGE"
            },
            {
              "name": "IF-MATCH",
              "value": "*"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"Caption_Variante\": {{ JSON.stringify($json.caption + \"\\n\\n\" + ($json.hashtags || []).join(\" \")) }},\n  \"Status\": \"Geblockt\",\n  \"Kommentare\": {{ JSON.stringify(($json.sp_fields.Kommentare || \"\") + \"\\n\" + new Date().toISOString().slice(0,16).replace(\"T\",\" \") + \" WF-02 v18.3: HWG-FILTER-MATCH \u2192 \" + ($json._hwg_matches || []).join(\", \") + \" \u2192 Status Geblockt, bitte pr\u00fcfen\") }}\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -224,
        304
      ],
      "id": "b6e0d5d3-26e1-4578-8d5b-08e71f910d42",
      "name": "SP: Status Geblockt",
      "disabled": true
    },
    {
      "parameters": {
        "jsCode": "// Avatar-Check: Flag pr\u00fcfen\nconst AVATAR_ENABLED = false;  // \u2190 sp\u00e4ter auf true setzen wenn D-ID l\u00e4uft\n\nreturn [{\n  json: {\n    ...$json,\n    _avatar_enabled: AVATAR_ENABLED\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1344,
        512
      ],
      "id": "2a99f8d9-0bfd-4ce9-9b47-c014994edc19",
      "name": "Code: AVATAR_ENABLED?",
      "disabled": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "leftValue": "={{ $json._avatar_enabled }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -1120,
        512
      ],
      "id": "35b58031-92ec-46d3-8a56-9c602a020017",
      "name": "If: Avatar aktiv?",
      "disabled": true
    },
    {
      "parameters": {
        "content": "## Avatar-Branch (v19+)\n\nWenn `AVATAR_ENABLED=true`:\n1. Reel-Skript via Claude generieren\n2. HWG-Filter auf Skript\n3. POST D-ID API: Talking Photo + Voice\n4. Polling: Job fertig?\n5. MP4-URL in `field_9`\n6. Status: `Bereit`\n\nSiehe `06_Avatar-Reel-Konzepte/Avatar-Integration-Plan.md`",
        "height": 240,
        "width": 320,
        "color": 6
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -896,
        384
      ],
      "id": "cc5a4266-47f9-46a2-99a8-81cd299496a9",
      "name": "Doku: Avatar TODO (v19)"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://physiofuchs889.sharepoint.com/sites/PhysioFuchsTW/_api/web/lists(guid'd17a6a6f-e6ef-457d-a2a9-4c30ea56120f')/items({{ $('Code: Validate + Route').item.json._sp_item_id }})",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "microsoftSharePointOAuth2Api",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Accept",
              "value": "application/json"
            },
            {
              "name": "X-HTTP-Method",
              "value": "MERGE"
            },
            {
              "name": "IF-MATCH",
              "value": "*"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"Status\": \"Wartet-auf-Avatar\",\n  \"Kommentare\": {{ JSON.stringify(($json.sp_fields.Kommentare || \"\") + \"\\n\" + new Date().toISOString().slice(0,16).replace(\"T\",\" \") + \" WF-02 v18.3: Post-Typ \" + $json._post_typ + \" braucht Avatar-Pipeline (AVATAR_ENABLED=false) \u2192 geparkt, wartet auf Aktivierung\") }}\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -896,
        640
      ],
      "id": "0167c135-9be1-4866-8c62-a6d826315135",
      "name": "SP: Status Wartet-auf-Avatar",
      "disabled": true
    }
  ],
  "connections": {
    "Cron: st\u00fcndlich": {
      "main": [
        [
          {
            "node": "SP: Entw\u00fcrfe holen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SP: Entw\u00fcrfe holen": {
      "main": [
        [
          {
            "node": "Code: Validate + Route",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Validate + Route": {
      "main": [
        [
          {
            "node": "Switch: Feed/Avatar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch: Feed/Avatar": {
      "main": [
        [
          {
            "node": "Code: Build Claude Payload",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Code: AVATAR_ENABLED?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Build Claude Payload": {
      "main": [
        [
          {
            "node": "HTTP: Claude Caption",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP: Claude Caption": {
      "main": [
        [
          {
            "node": "Code: Parse Claude",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Parse Claude": {
      "main": [
        [
          {
            "node": "Code: HWG-Filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: HWG-Filter": {
      "main": [
        [
          {
            "node": "If: HWG sauber?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If: HWG sauber?": {
      "main": [
        [
          {
            "node": "Code: Foto-Pfad bauen",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "SP: Status Geblockt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Foto-Pfad bauen": {
      "main": [
        [
          {
            "node": "HTTP: SP Fotos listen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP: SP Fotos listen": {
      "main": [
        [
          {
            "node": "Code: Foto-Match",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Foto-Match": {
      "main": [
        [
          {
            "node": "If: Foto gefunden?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If: Foto gefunden?": {
      "main": [
        [
          {
            "node": "HTTP: SP Foto Download",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Code: Foto-URL final",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP: SP Foto Download": {
      "main": [
        [
          {
            "node": "Code: Foto \u2192 GitHub Body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Foto \u2192 GitHub Body": {
      "main": [
        [
          {
            "node": "HTTP: GitHub-Push Foto",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP: GitHub-Push Foto": {
      "main": [
        [
          {
            "node": "Code: Foto-URL final",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Foto-URL final": {
      "main": [
        [
          {
            "node": "Code: HTML bauen (IG+FB)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: HTML bauen (IG+FB)": {
      "main": [
        [
          {
            "node": "Code: IG HTML\u2192Binary",
            "type": "main",
            "index": 0
          },
          {
            "node": "Code: FB HTML\u2192Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: IG HTML\u2192Binary": {
      "main": [
        [
          {
            "node": "HTTP: Gotenberg IG",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP: Gotenberg IG": {
      "main": [
        [
          {
            "node": "SP: Upload IG-Bild",
            "type": "main",
            "index": 0
          },
          {
            "node": "Code: IG \u2192 GitHub Body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SP: Upload IG-Bild": {
      "main": [
        [
          {
            "node": "Code: Merge + Update-Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: FB HTML\u2192Binary": {
      "main": [
        [
          {
            "node": "HTTP: Gotenberg FB",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP: Gotenberg FB": {
      "main": [
        [
          {
            "node": "SP: Upload FB-Bild",
            "type": "main",
            "index": 0
          },
          {
            "node": "Code: FB \u2192 GitHub Body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SP: Upload FB-Bild": {
      "main": [
        [
          {
            "node": "Code: Merge + Update-Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Merge + Update-Payload": {
      "main": [
        [
          {
            "node": "SP: Status Bereit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: AVATAR_ENABLED?": {
      "main": [
        [
          {
            "node": "If: Avatar aktiv?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If: Avatar aktiv?": {
      "main": [
        [],
        [
          {
            "node": "SP: Status Wartet-auf-Avatar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: IG \u2192 GitHub Body": {
      "main": [
        [
          {
            "node": "GitHub: Push IG",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub: Push IG": {
      "main": [
        [
          {
            "node": "Code: Merge + Update-Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: FB \u2192 GitHub Body": {
      "main": [
        [
          {
            "node": "GitHub: Push FB",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub: Push FB": {
      "main": [
        [
          {
            "node": "Code: Merge + Update-Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "v18.3-foto-branch-2026-06-08",
  "id": "6DghT5VFKa9CGG2T-v183",
  "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

PF WF-02 Caption Generator v18.3. Uses microsoftSharePoint, httpRequest. Scheduled trigger; 38 nodes.

Source: https://github.com/twaese/Physio-Fuchs-Social-Media-Automation/blob/3df8034d8e4f0cb0bad3b795d2650d9a7005ed83/PF_WF-02_Caption_Generator_v18.3.json — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

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

Web Scraping

This template runs two scheduled workflows to govern Microsoft Entra ID (Azure AD) guest accounts by detecting stale users via Microsoft Graph, staging deletions in SharePoint with a 72-hour window, n

Microsoft SharePoint, Microsoft Teams, Microsoft Entra +1
Web Scraping

PF WF-02 Caption Generator v18. Uses microsoftSharePoint, httpRequest. Scheduled trigger; 25 nodes.

Microsoft SharePoint, HTTP Request
Web Scraping

PF – WF-03 Social Media Auto-Post. Uses microsoftSharePoint, httpRequest, microsoftTeams. Scheduled trigger; 16 nodes.

Microsoft SharePoint, HTTP Request, Microsoft Teams
Web Scraping

PF – WF-03 Social Media Post v5. Uses microsoftSharePoint, httpRequest, microsoftTeams. Scheduled trigger; 16 nodes.

Microsoft SharePoint, HTTP Request, Microsoft Teams
Web Scraping

Spotify-Sync-Surrealdb-V1. Uses httpRequest, n8n-nodes-surrealdb, spotify. Scheduled trigger; 62 nodes.

HTTP Request, N8N Nodes Surrealdb, Spotify