{
  "id": "BO9g2SCjZIC8telE",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Auto-Publish Meta Posts from Google Sheets with Slack & Email Alerts",
  "tags": [],
  "nodes": [
    {
      "id": "f0c752c3-2396-4c36-8300-e124323d4410",
      "name": "Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2000,
        -928
      ],
      "parameters": {
        "width": 498,
        "height": 608,
        "content": "## \ud83d\udccb Auto-Publish Meta Posts\n\n### How it works\nThis workflow automatically posts new content from a Google Sheet to your Facebook Page and notifies your team when it succeeds or fails. It checks the sheet every minute for rows marked \u201cpending.\u201d When one is found, it extracts the caption and image URL, posts to Facebook via the Graph API, and then sends Slack and email notifications. The workflow also updates the Google Sheet status to \u201cCompleted\u201d once a post is published, keeping everything organized and preventing duplicates.\n\n### Setup steps\n1. Connect your Google Sheets, Facebook Graph API, Slack, and Email (SMTP or Outlook) credentials.\n2. In Google Sheets, create a table with columns: `post_id`, `caption`, `image_url`, and `status`.\n3. Enter one or more pending posts with `status = pending`.\n4. Configure your Facebook Page ID and access token (use credentials, not hard-coded tokens).\n5. Adjust Slack channel, email addresses, or poll frequency if needed.\n6. Run once manually to verify posting works end-to-end.\n7. Once verified, enable the workflow \u2014 it will publish automatically whenever new rows appear.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "fcad0e1d-2523-492b-8c4e-8f77813fbe22",
      "name": "Sheets Setup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1984,
        -288
      ],
      "parameters": {
        "color": 2,
        "width": 624,
        "height": 410,
        "content": "## Input & Filtering \n\n\nChecks the Google Sheet every minute and filters for rows where `status = pending`. \nThe Code node ensures only the latest pending row is used. \nThen the data is extracted for posting (caption + image URL).\n"
      },
      "typeVersion": 1
    },
    {
      "id": "998b413e-7413-4f7f-af7c-aedcdc5f700c",
      "name": "Success Check",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1328,
        -336
      ],
      "parameters": {
        "color": 2,
        "width": 432,
        "height": 454,
        "content": "## Meta Publishing \n\n\nPublishes the image and caption to your Facebook Page using the Graph API. \nAfter posting, the IF node checks if a valid Post ID exists to confirm success before continuing.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "27171806-46fd-4889-a044-85766c8269a1",
      "name": "Success Notifications",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -864,
        -592
      ],
      "parameters": {
        "color": 2,
        "width": 480,
        "height": 598,
        "content": "## Success Notifications & Sheet Update \n\n\nIf the post succeeds, sends a Slack and Outlook/Email confirmation with caption, image, and post ID. \nFinally, updates the Google Sheet to mark the post as \u201cCompleted.\u201d\n"
      },
      "typeVersion": 1
    },
    {
      "id": "9d764a80-d3ff-4d41-a074-405faac0100c",
      "name": "Error Notifications",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -864,
        16
      ],
      "parameters": {
        "color": 2,
        "width": 448,
        "height": 422,
        "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n## Error Handling \n\n\nIf posting fails, sends a Slack and email alert containing the error message, caption, and image URL. \nHelps you quickly spot and fix issues with credentials or image links.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "bd4e17ad-641d-471b-ba10-60e4eb66396b",
      "name": "rigger \u2013 Fetch Pending Posts (Google Sheets)",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        -1936,
        -48
      ],
      "parameters": {
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "="
        }
      },
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "46269dca-9c92-4ffb-8aae-638200b11e67",
      "name": "Filter Pending Posts (Code Node)",
      "type": "n8n-nodes-base.code",
      "position": [
        -1712,
        -48
      ],
      "parameters": {
        "jsCode": "// n8n Code Node\n// Purpose: Get the last row with status = \"pending\" from Google Sheets input data\n\n// Get input data from previous node\nconst data = $input.all().map(item => item.json);\n\n// Filter to only \"pending\" posts\nconst pendingRows = data.filter(item => item.status === \"pending\");\n\n// If no pending rows, return an error message\nif (pendingRows.length === 0) {\n  return [{ message: \"No pending rows found\" }];\n}\n\n// Get the last pending row (highest row_number)\nconst lastRow = pendingRows.reduce((latest, current) => {\n  return current.row_number > latest.row_number ? current : latest;\n});\n\n// Return the last pending row\nreturn [{ json: lastRow }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "866cf0cb-bf26-48d7-9d07-efa7503b94a4",
      "name": "Prepare Post Data",
      "type": "n8n-nodes-base.set",
      "position": [
        -1488,
        -48
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "caption-field",
              "name": "caption",
              "type": "string",
              "value": "={{ $json.caption }}"
            },
            {
              "id": "image-url-field",
              "name": "image_url",
              "type": "string",
              "value": "={{ $json.image_url }}"
            },
            {
              "id": "2c98f6f8-b7cf-4235-8080-e3915f8319e8",
              "name": "status",
              "type": "string",
              "value": "={{ $json.status }}"
            }
          ]
        }
      },
      "typeVersion": 3.3
    },
    {
      "id": "46abb3e3-ee59-403e-b863-c9b927aaf628",
      "name": "Publish Post to Meta (Facebook Graph API)",
      "type": "n8n-nodes-base.facebookGraphApi",
      "position": [
        -1264,
        -48
      ],
      "parameters": {
        "edge": "photos",
        "options": {
          "queryParameters": {
            "parameter": [
              {
                "name": "url",
                "value": "={{ $json.image_url }}"
              },
              {
                "name": "caption",
                "value": "={{ $json.caption }}"
              },
              {
                "name": "access_token",
                "value": "="
              }
            ]
          }
        },
        "graphApiVersion": "v23.0",
        "httpRequestMethod": "POST"
      },
      "credentials": {
        "facebookGraphApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "291b95ed-08b5-4fa1-a540-077a411d0ba6",
      "name": "Check Publish Success",
      "type": "n8n-nodes-base.if",
      "position": [
        -1040,
        -48
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "success-condition",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.post_id }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "c882a92b-b4a3-49c5-b65b-916f2be96537",
      "name": "Notify Success in Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        -816,
        -336
      ],
      "parameters": {
        "text": "=\u2705 Post successfully published to Meta!\n\nCaption: {{ $('Prepare Post Data').item.json.caption }}\nImage: {{ $('Prepare Post Data').item.json.image_url }}\nPost ID: {{ $json.id }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "="
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "aabd920d-ed41-4511-b722-411ee82d653c",
      "name": "Send Success Email (Outlook/SMTP)",
      "type": "n8n-nodes-base.microsoftOutlook",
      "position": [
        -816,
        -144
      ],
      "parameters": {
        "subject": "\u2705 Meta Post Published Successfully",
        "bodyContent": "=\u2705 <b>Post successfully published to Facebook Page!</b><br><br>\n<b>Caption:</b> {{ $('Prepare Post Data').item.json.caption }}<br>\n<b>Image URL:</b> <a href=\"{{ $('Prepare Post Data').item.json.image_url }}\">{{ $('Prepare Post Data').item.json.image_url }}</a><br>\n<b>Post ID:</b> {{ $json.id }}<br><br>\n\ud83d\udd52 Posted via n8n automation workflow.\n",
        "additionalFields": {
          "bodyContentType": "html"
        }
      },
      "credentials": {
        "microsoftOutlookOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "6be47779-6759-4f2b-96e1-3973f21292a9",
      "name": "Update Post Status in Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -592,
        -336
      ],
      "parameters": {
        "columns": {
          "value": {
            "status": "Completed",
            "post_id": "={{ $('Filter Pending Posts (Code Node)').item.json.post_id }}"
          },
          "schema": [
            {
              "id": "post_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "post_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "caption",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "caption",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "image_url",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "image_url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "post_id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "="
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "="
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "079b3340-1229-49b3-ae92-0d0e0a760383",
      "name": "Notify Failure in Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        -816,
        48
      ],
      "parameters": {
        "text": "=\u274c Failed to publish post to Meta!\n\nCaption: {{ $('Prepare Post Data').item.json.caption }}\nImage: {{ $('Prepare Post Data').item.json.image_url }}\nError: {{ $json.error.message || $json.message || 'Unknown error' }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "="
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "03740964-7291-4b80-85d5-37baad0d38e6",
      "name": "Send Failure Email",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        -608,
        64
      ],
      "parameters": {
        "options": {},
        "subject": "\u274c Meta Post Failed"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "fb6c4ed8-0291-494f-b4b7-d31ac555e81d",
  "connections": {
    "Prepare Post Data": {
      "main": [
        [
          {
            "node": "Publish Post to Meta (Facebook Graph API)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Publish Success": {
      "main": [
        [
          {
            "node": "Notify Success in Slack",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Success Email (Outlook/SMTP)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Notify Failure in Slack",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Failure Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Success in Slack": {
      "main": [
        [
          {
            "node": "Update Post Status in Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Pending Posts (Code Node)": {
      "main": [
        [
          {
            "node": "Prepare Post Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Publish Post to Meta (Facebook Graph API)": {
      "main": [
        [
          {
            "node": "Check Publish Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "rigger \u2013 Fetch Pending Posts (Google Sheets)": {
      "main": [
        [
          {
            "node": "Filter Pending Posts (Code Node)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}