{
  "id": "z3jqwXEftE3owiCT",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "BlueSky Suite: Track BlueSky post analytics in Google Sheets",
  "tags": [],
  "nodes": [
    {
      "id": "fb306bc5-06c1-43f9-847e-e137508e8425",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1232,
        -16
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c36fba80-c89f-42ea-ae06-20ff592a8bfc",
      "name": "Get row(s) in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -432,
        -16
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "Posted",
              "lookupColumn": "Status"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "86c103ad-5359-4d8b-a977-12dc2252eac2",
      "name": "Get Post Stats",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        560,
        0
      ],
      "parameters": {
        "url": "https://bsky.social/xrpc/app.bsky.feed.getPosts",
        "options": {},
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "uris",
              "value": "=at://{{ $('BlueSky Auth').first().json.did }}/app.bsky.feed.post/{{ $('Loop Over Items').item.json[\"Post Link\"].split(\"/post/\")[1] }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('BlueSky Auth').first().json.accessJwt }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "276afa3f-3de1-4a9c-ad98-02e5a58b8d60",
      "name": "Update row in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueRegularOutput",
      "position": [
        944,
        0
      ],
      "parameters": {
        "columns": {
          "value": {
            "Like Count": "={{ $json?.posts[0]?.likeCount || 0 }}",
            "row_number": "={{ $('Loop Over Items').item.json.row_number }}",
            "Reply Count": "={{ $json?.posts[0]?.replyCount || 0 }}",
            "Repost Count": "={{ $json?.posts[0]?.repostCount || 0 }}"
          },
          "schema": [
            {
              "id": "Content",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Content",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Thread ID",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Thread ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Sequence",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Sequence",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Image URL",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Image URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Scheduled Time",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Scheduled Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Post Link",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Post Link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Post URI",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Post URI",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Like Count",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Like Count",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Repost Count",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Repost Count",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Reply Count",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Reply Count",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "bd75f8c6-b009-43b0-8f46-9714c9309fc2",
      "name": "Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        -1024,
        -16
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "4e2b2be1-d052-4dc8-96e8-60054b1cba1d",
              "name": "bluesky_handle",
              "type": "string",
              "value": ""
            },
            {
              "id": "b336da17-f730-49e6-85c9-4f8e9b3f8522",
              "name": "app_password",
              "type": "string",
              "value": ""
            },
            {
              "id": "3f701790-b159-490a-a656-13e170f1a8a1",
              "name": "timezone",
              "type": "string",
              "value": "Asia/Kolkata"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "4f57d122-012f-4185-8f23-812acc87e00d",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2000,
        -240
      ],
      "parameters": {
        "width": 640,
        "height": 496,
        "content": "# \ud83d\udcc8 Analytics Bot - How To Use\n**Goal:** Keep your engagement stats (Likes, Reposts, Replies) updated automatically in the same Google Sheet that you use for content scheduling . Make sure your Google Sheet has all the columns required. [**Sample Google Sheet**](https://docs.google.com/spreadsheets/d/1Mg04gK1K5DBtJHrWw3ePRFc_JjkxwAp0deGjapVl2q0/edit?usp=sharing)\n\n**Step 1:** Open the \"Configuration\" node and enter your BlueSky Handle and App Password and your timezone\n\n**Step 2:** The \"Active Window\" Strategy To save API calls, this workflow only checks posts from the last 14 days.\n\n**Day 1-14:** Checks daily to catch viral spikes.\n\n**Day 15+:** Stops checking (Post is considered \"Archived\").\n\n**Step 3:** Activate Turn on the workflow. It runs daily (e.g., 9 AM) and updates the rows in your sheet without creating duplicates."
      },
      "typeVersion": 1
    },
    {
      "id": "ad79671f-e383-4456-9cd8-d55bc58b29dd",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -160,
        -224
      ],
      "parameters": {
        "width": 256,
        "height": 192,
        "content": "### 4- The Gatekeeper\nChecks that 'Posted At' and 'Post Link' are not empty to prevent errors on blank or failed rows.\n\nThen it only allows posts where 'Posted At' date is within the last 14 days.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "78a1f0b3-2661-413f-8aea-d3cf8103f02d",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1088,
        -224
      ],
      "parameters": {
        "width": 288,
        "height": 176,
        "content": "### 1- START HERE \nEnter your BlueSky Handle (e.g., steve.bsky.social), App Password and timezone (eg: America/Los_Angeles or Europe/Berlin etc) here.\n\n[Find your timezone name here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)"
      },
      "typeVersion": 1
    },
    {
      "id": "a2d78b89-7a98-473a-abc4-c052346f1094",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -864,
        144
      ],
      "parameters": {
        "width": 256,
        "height": 144,
        "content": "### 2- Get access token\nAuthenticates with BlueSky to retrieve an Access Token. This token is used for all subsequent API calls."
      },
      "typeVersion": 1
    },
    {
      "id": "73cc8432-0844-428a-9e60-11cd573ae9f2",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -512,
        -176
      ],
      "parameters": {
        "height": 144,
        "content": "### 3- Google sheets rows \nFetches all rows where Status is 'Posted'. To test, ensure your sheet has at least one row marked as 'Posted'."
      },
      "typeVersion": 1
    },
    {
      "id": "9474744d-e05d-4ce5-93eb-9decda6b5e53",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        448,
        -224
      ],
      "parameters": {
        "width": 272,
        "height": 192,
        "content": "### 6- Resilient Fetcher\nFetches post metrics from BlueSky.\nConfigured to \"Continue on Error\". If a post was deleted from BlueSky, this node returns an error JSON instead of crashing the workflow."
      },
      "typeVersion": 1
    },
    {
      "id": "69564433-dd8d-49b6-bc1d-f9645be6095c",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        800,
        -224
      ],
      "parameters": {
        "width": 272,
        "height": 192,
        "content": "### 7- Safe Updater \nUpdates the Google Sheet.\nUses special \"Safe Logic\" (|| 0). If the post was deleted (returns 404), it writes 0 into the stats columns instead of breaking the automation."
      },
      "typeVersion": 1
    },
    {
      "id": "f49c2556-07eb-44be-b21a-f3a7fd248912",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        192,
        144
      ],
      "parameters": {
        "width": 320,
        "height": 192,
        "content": "### 5- The Batch Processor \nThis node takes the filtered list of \"Active Posts\" (from the last 14 days) and processes them one by one.\n\nIt ensures that if one post fails (e.g., deleted), it doesn't stop the entire workflow, allowing the rest of your stats to update successfully."
      },
      "typeVersion": 1
    },
    {
      "id": "5706bed9-7b6d-446c-99cf-e336ca958260",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -544,
        -336
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 704,
        "content": "# Input"
      },
      "typeVersion": 1
    },
    {
      "id": "f945d73e-f59a-44b5-98a1-9bd1094f9957",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        160,
        -336
      ],
      "parameters": {
        "color": 7,
        "width": 944,
        "height": 704,
        "content": "# Processing"
      },
      "typeVersion": 1
    },
    {
      "id": "972cd0de-e7e7-41e9-ae39-35f2583d623d",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        240,
        -16
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "141f80a2-7974-405a-85f3-d1601af95ad3",
      "name": "Filter1",
      "type": "n8n-nodes-base.filter",
      "position": [
        -112,
        -16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "b6ab450f-c868-4582-b4f9-8b7e98974238",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json['Post Link'] }}",
              "rightValue": ""
            },
            {
              "id": "83f5b4cd-09c9-4b57-adab-fc6960d4c5f7",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json['Posted At'] }}",
              "rightValue": ""
            },
            {
              "id": "5d651b8d-26e0-4f26-abf2-52dd2cb916bf",
              "operator": {
                "type": "dateTime",
                "operation": "after"
              },
              "leftValue": "={{ DateTime.fromFormat($json['Posted At'], 'yyyy-MM-dd HH:mm', { zone: $('Configuration').first().json.timezone }) }}",
              "rightValue": "={{ $now.setZone($('Configuration').first().json.timezone || 'UTC').minus({days: 14}) }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "9d4160cc-fc59-4ccf-aa1d-bd96c9d5ccbd",
      "name": "BlueSky Auth",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -784,
        -16
      ],
      "parameters": {
        "url": "https://bsky.social/xrpc/com.atproto.server.createSession",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"identifier\":\"{{$('Configuration').first().json.bluesky_handle}}\",\n  \"password\": \"{{ $('Configuration').first().json.app_password }}\"\n}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.3
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "406642f8-0dc8-4b1f-9367-a5643fca02c8",
  "connections": {
    "Filter1": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "BlueSky Auth": {
      "main": [
        [
          {
            "node": "Get row(s) in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configuration": {
      "main": [
        [
          {
            "node": "BlueSky Auth",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Post Stats": {
      "main": [
        [
          {
            "node": "Update row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Get Post Stats",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet": {
      "main": [
        [
          {
            "node": "Filter1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update row in sheet": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}