AutomationFlowsSlack & Telegram › Audit Android Feature Flags From Github with Firebase and Send Slack Reports

Audit Android Feature Flags From Github with Firebase and Send Slack Reports

ByWeblineIndia @weblineindia on n8n.io

This workflow automatically scans an Android GitHub repository, detects feature flags used in the codebase, compares them with Firebase Remote Config flags, identifies unused flags and sends a weekly cleanup report to Slack.

Cron / scheduled trigger★★★★☆ complexity20 nodesHTTP RequestSlack
Slack & Telegram Trigger: Cron / scheduled Nodes: 20 Complexity: ★★★★☆ Added:
Audit Android Feature Flags From Github with Firebase and Send Slack Reports — n8n workflow card showing HTTP Request, Slack integration

This workflow corresponds to n8n.io template #15233 — we link there as the canonical source.

This workflow follows the HTTP Request → Slack recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "38DYe2Tgzx3bDAuh",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Android Feature Flag Cleanup Automation",
  "tags": [
    {
      "id": "F5TgwgrB2LTO3oA7",
      "name": "completed",
      "createdAt": "2026-01-02T08:54:40.571Z",
      "updatedAt": "2026-01-02T08:54:40.571Z"
    }
  ],
  "nodes": [
    {
      "id": "a1a80a8e-cfd4-405c-b89b-bf1821d35c9c",
      "name": "Code: Compare Flags",
      "type": "n8n-nodes-base.code",
      "position": [
        1616,
        -1520
      ],
      "parameters": {
        "jsCode": "// TEMP: flags found in Android code\nconst codeFlags = [\"Admin\"];\n\n// Firebase flags from Set node\nconst firebaseFlags = items[0].json.flags || [];\n\nconst codeSet = new Set(codeFlags);\n\nconst usedFirebaseFlags = firebaseFlags.filter(f => codeSet.has(f));\nconst unusedFirebaseFlags = firebaseFlags.filter(f => !codeSet.has(f));\n\nreturn [\n  {\n    json: {\n      codeFlags,\n      usedFirebaseFlags,\n      unusedFirebaseFlags\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "6b951151-0e84-4cfa-bf79-f5356538eeb7",
      "name": "Extercet key flags",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        -1520
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  const code = item.json.code;\n\n  // Simple regex examples\n  const stringKeys = [...code.matchAll(/\"([^\"]+)\"/g)].map(m => m[1]);\n\n  return {\n    json: {\n      fileName: item.json.fileName,\n      filePath: item.json.filePath,\n      stringLiterals: [...new Set(stringKeys)]\n    }\n  };\n});\n"
      },
      "typeVersion": 2
    },
    {
      "id": "9a087f52-47dd-4c75-9c16-85b623c45495",
      "name": "Filter keys",
      "type": "n8n-nodes-base.code",
      "position": [
        944,
        -1520
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  const cleanKeys = item.json.stringLiterals.filter(key =>\n    key.length <= 30 &&           // avoid long UI text\n    !key.includes('\\n') &&        // remove multiline\n    !key.includes('$')            // remove template strings\n  );\n\n  return {\n    json: {\n      fileName: item.json.fileName,\n      filePath: item.json.filePath,\n      keys: [...new Set(cleanKeys)]\n    }\n  };\n});\n"
      },
      "typeVersion": 2
    },
    {
      "id": "3a3700e8-3f06-4ca2-9de9-6d70654ef938",
      "name": "classify key",
      "type": "n8n-nodes-base.code",
      "position": [
        1168,
        -1520
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  const featureFlags = [];\n  const configKeys = [];\n  const uiKeys = [];\n\n  for (const key of item.json.keys) {\n    if (/on\\s|called/i.test(key)) {\n      uiKeys.push(key);\n    } else if (/admin|feature|enable|disable|flag/i.test(key)) {\n      featureFlags.push(key);\n    } else {\n      configKeys.push(key);\n    }\n  }\n\n  return {\n    json: {\n      fileName: item.json.fileName,\n      filePath: item.json.filePath,\n      featureFlags,\n      configKeys,\n      uiKeys\n    }\n  };\n});\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e13b992d-1a51-4534-82d4-063c2c9ca6f2",
      "name": "Weekly Schedule Trigger",
      "type": "n8n-nodes-base.cron",
      "position": [
        -1296,
        -1520
      ],
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "hour": 9,
              "mode": "everyWeek",
              "minute": 30
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e999844b-f908-4591-be73-4a07ab991680",
      "name": "Set: Repository Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        -1072,
        -1520
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f2eb9f20-ceda-4486-aeee-b7a7d653a1fe",
              "name": "GITHUB_OWNER",
              "type": "string",
              "value": "Github_ownername_here"
            },
            {
              "id": "9a1ae444-27eb-4444-981c-ca4d58c8d8f1",
              "name": "GITHUB_REPO",
              "type": "string",
              "value": "Github_repo_here"
            },
            {
              "id": "10855b23-e2ea-4a5b-9b8d-b027b6289667",
              "name": "BASE_BRANCH",
              "type": "string",
              "value": "master"
            },
            {
              "id": "1a585ec8-3012-45fe-a0cc-1315dd96fe36",
              "name": "FILE_EXTS_CSV",
              "type": "string",
              "value": ".kt,.java"
            },
            {
              "id": "4ba18209-19b7-45ad-995c-2bfc4831461a",
              "name": "MAX_FILES",
              "type": "number",
              "value": 500
            },
            {
              "id": "b07ef56f-df61-4c6a-8aff-9bccf3b5ef8c",
              "name": "THRESHOLD_DAYS",
              "type": "number",
              "value": 30
            },
            {
              "id": "16edf04d-63c5-4166-8c6b-2ba8b953bf60",
              "name": "FIREBASE_PROJECT_ID",
              "type": "string",
              "value": "Your_project_id_here"
            },
            {
              "id": "8906d1d9-129d-413c-8aab-8cee2d4630e0",
              "name": "ENABLE_PR",
              "type": "boolean",
              "value": true
            },
            {
              "id": "426e8238-ee9a-47da-9744-11573f339ae6",
              "name": "ENABLE_JIRA",
              "type": "boolean",
              "value": true
            },
            {
              "id": "d1938a7d-defb-44c7-82b2-8a38b9468556",
              "name": "SHEET_ID",
              "type": "string",
              "value": ""
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a34a9a42-f760-4b9b-946c-ba1a4aabefa9",
      "name": "Get Branch Details",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -848,
        -1520
      ],
      "parameters": {
        "url": "=https://api.github.com/repos/{{$json.GITHUB_OWNER}}/{{$json.GITHUB_REPO}}/branches/master",
        "options": {
          "response": {
            "response": {}
          }
        },
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "githubApi"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "6a3c92e2-f586-4553-8160-ae06d82b4bd0",
      "name": "Get Repository File List",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -624,
        -1520
      ],
      "parameters": {
        "url": "=https://api.github.com/repos/{{$('Set: Repository Configuration').item.json.GITHUB_OWNER}}/{{ $('Set: Repository Configuration').item.json.GITHUB_REPO }}/git/trees/{{$json.commit.sha}}?recursive=1",
        "options": {},
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "githubApi"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "646d31dd-e01a-4e72-911d-2e684c989151",
      "name": "Process Each File Separately",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -400,
        -1520
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "tree"
      },
      "typeVersion": 1
    },
    {
      "id": "a76757b9-70aa-40d3-a4af-d6a2785bf97c",
      "name": "check file IF: .kt or .java",
      "type": "n8n-nodes-base.if",
      "position": [
        -208,
        -1520
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ea4135b2-5c35-41b6-8b4f-77cd27b62993",
              "operator": {
                "type": "string",
                "operation": "regex"
              },
              "leftValue": "={{ $json.path }}",
              "rightValue": ".kt"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "67a9e0bc-cf1a-4f95-acb7-5b95c13a4789",
      "name": "Download File Content",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        48,
        -1520
      ],
      "parameters": {
        "url": "=https://api.github.com/repos/{{ $('Set: Repository Configuration').item.json.GITHUB_OWNER }}/{{ $('Set: Repository Configuration').item.json.GITHUB_REPO }}/contents/{{$json.path}}?ref=master",
        "options": {}
      },
      "typeVersion": 4.3
    },
    {
      "id": "71e964a1-2dde-4504-9616-482da51395eb",
      "name": "Read Files One by One",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        272,
        -1520
      ],
      "parameters": {
        "options": {},
        "batchSize": "=1"
      },
      "typeVersion": 3
    },
    {
      "id": "8eff63c7-fe89-433c-b0f0-b049de6a7d19",
      "name": "Decode File Content",
      "type": "n8n-nodes-base.code",
      "position": [
        496,
        -1520
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  const base64Content = item.json.content;\n\n  if (!base64Content) {\n    // Skip items without content\n    return null;\n  }\n\n  const buffer = Buffer.from(base64Content, 'base64');\n  const text = buffer.toString('utf8');\n\n  return {\n    json: {\n      code: text,\n      filePath: item.json.path,\n      fileName: item.json.name\n    }\n  };\n}).filter(Boolean);\n"
      },
      "typeVersion": 2
    },
    {
      "id": "78d07e93-fe93-489f-9919-9ab9f96d1119",
      "name": "Mock Firebase Flags Data",
      "type": "n8n-nodes-base.set",
      "position": [
        1392,
        -1520
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "{\n  \"source\": \"firebase\",\n  \"flags\": [\n    \"feature_login_v2\",\n    \"enable_new_ui\",\n    \"admin_panel_enabled\",\n    \"paywall_experiment\",\n    \"unused_old_flag\"\n  ]\n}\n"
      },
      "typeVersion": 3.4
    },
    {
      "id": "5fcb7efb-29dd-4ef6-926a-3f433673fab4",
      "name": "Create Final Summary Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1840,
        -1520
      ],
      "parameters": {
        "jsCode": "return [\n  {\n    json: {\n      summary: {\n        totalCodeFlags: items[0].json.codeFlags.length,\n        totalFirebaseFlags:\n          items[0].json.usedFirebaseFlags.length +\n          items[0].json.unusedFirebaseFlags.length,\n        unusedFirebaseFlagsCount:\n          items[0].json.unusedFirebaseFlags.length\n      },\n      details: items[0].json\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e8211969-805b-441c-877b-e2c2c314d73b",
      "name": "Send Slack Report",
      "type": "n8n-nodes-base.slack",
      "position": [
        2080,
        -1520
      ],
      "parameters": {
        "text": "=*Android Feature Flag Weekly Report*  \n*Summary* \n  \u2022 Total code flags found: {{$json.summary.totalCodeFlags}} \n  \u2022 Total Firebase flags: {{$json.summary.totalFirebaseFlags}}    \n  \u2022 Unused Firebase flags:{{$json.summary.unusedFirebaseFlagsCount}} \n\n*Code Flags* \n\u2022 {{$json.details.codeFlags.join(', ')}}  \n\n*Unused Firebase Flags (Cleanup Candidates)* \n{{$json.details.unusedFirebaseFlags.map(f => '\u2022 ' + f).join('\\n')}}  \n\nAction: Please review these flags and confirm if they can be safely removed from Firebase Remote Config.",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09S57E2JQ2",
          "cachedResultName": "n8n"
        },
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "1398a3b9-b84d-4c7b-a073-441de81e4ae5",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1376,
        -1712
      ],
      "parameters": {
        "color": 7,
        "width": 1312,
        "height": 432,
        "content": "## GitHub File Collection & Android File FilterI'm a note \nThis part starts the workflow automatically every week at the scheduled time.\nIt loads all required settings like GitHub repository name, branch and scan options.\nThen it connects to GitHub and gets the complete list of project files.\nAfter that, files are checked one by one and only Android files are selected for scanning."
      },
      "typeVersion": 1
    },
    {
      "id": "bb22e22d-d531-4d4c-be8a-f9c5fa3cc217",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -1712
      ],
      "parameters": {
        "color": 7,
        "width": 1312,
        "height": 432,
        "content": "## Code Reading & Flag Detection\n\nThis process downloads the content of each selected Android file from GitHub.\nIt reads files one by one and converts the encoded content into readable code.\nThen it extracts text values used inside the code and removes unwanted or duplicate data.\nFinally, it identifies possible feature flags, config keys and normal UI text."
      },
      "typeVersion": 1
    },
    {
      "id": "8dc27c8f-dbbd-438c-9928-ffef6783b907",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1344,
        -1712
      ],
      "parameters": {
        "color": 7,
        "width": 928,
        "height": 432,
        "content": "## Firebase Comparison & Final Report\n\nThis part uses sample Firebase flags for testing until the real Firebase API is connected.\nIt compares Firebase flags with flags found in the Android code.\nThen it prepares a summary report with totals and unused flags.\nFinally, the report is sent to the Slack channel for review."
      },
      "typeVersion": 1
    },
    {
      "id": "e0fd2e66-039d-4216-b614-6cfb24d9f101",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2064,
        -2240
      ],
      "parameters": {
        "width": 512,
        "height": 576,
        "content": "## How it works\n\nThis workflow runs automatically every week to check feature flags used in an Android project. It first connects to GitHub and scans Android source files (.kt and .java) from the selected repository. Then it reads the code, finds possible feature flags, config keys and other related values.\n\nNext, it compares those code flags with Firebase Remote Config flags. This helps identify flags that still exist in Firebase but are no longer used in the app code.\n\nAfter comparison, the workflow creates a short summary report showing total flags found, active flags and unused Firebase flags that may be removed. Finally, the report is automatically sent to a Slack channel so the team can review cleanup candidates.\n\n## Setup steps\n\n**1.** Add your GitHub credential in n8n.\n**2.** Update Repository configuration with your GitHub owner, repository name and branch.\n**3.** Add your Slack credential and choose the channel for reports.\n**4.** Replace test Firebase flags with real Firebase Remote Config API credentials when ready.\n**5.** Set weekly schedule time in the Cron node if needed.\n**6.** Run once manually to test the workflow output.\n**7.** Enable the workflow for automatic weekly reporting."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "14b5d09e-a66c-4580-bb65-6b0b912b1455",
  "connections": {
    "Filter keys": {
      "main": [
        [
          {
            "node": "classify key",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "classify key": {
      "main": [
        [
          {
            "node": "Mock Firebase Flags Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extercet key flags": {
      "main": [
        [
          {
            "node": "Filter keys",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Branch Details": {
      "main": [
        [
          {
            "node": "Get Repository File List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Compare Flags": {
      "main": [
        [
          {
            "node": "Create Final Summary Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Decode File Content": {
      "main": [
        [
          {
            "node": "Extercet key flags",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download File Content": {
      "main": [
        [
          {
            "node": "Read Files One by One",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Files One by One": {
      "main": [
        [],
        [
          {
            "node": "Decode File Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Weekly Schedule Trigger": {
      "main": [
        [
          {
            "node": "Set: Repository Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Repository File List": {
      "main": [
        [
          {
            "node": "Process Each File Separately",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mock Firebase Flags Data": {
      "main": [
        [
          {
            "node": "Code: Compare Flags",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Final Summary Report": {
      "main": [
        [
          {
            "node": "Send Slack Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "check file IF: .kt or .java": {
      "main": [
        [
          {
            "node": "Download File Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Each File Separately": {
      "main": [
        [
          {
            "node": "check file IF: .kt or .java",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set: Repository Configuration": {
      "main": [
        [
          {
            "node": "Get Branch Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

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

This workflow automatically scans an Android GitHub repository, detects feature flags used in the codebase, compares them with Firebase Remote Config flags, identifies unused flags and sends a weekly cleanup report to Slack.

Source: https://n8n.io/workflows/15233/ — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

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

Slack & Telegram

This workflow is an automated employee time tracking and reporting system that monitors weekly work hours via TMetric, then delivers personalized summaries directly to each team member on Slack. It co

HTTP Request, Item Lists, Data Table +1
Slack & Telegram

Import Productboard Notes Companies And Features Into Snowflake. Uses stickyNote, httpRequest, splitOut, snowflake. Scheduled trigger; 35 nodes.

HTTP Request, Snowflake, Slack
Slack & Telegram

Import Productboard Notes, Companies and Features into Snowflake. Uses stickyNote, httpRequest, splitOut, snowflake. Scheduled trigger; 35 nodes.

HTTP Request, Snowflake, Slack
Slack & Telegram

This workflow imports Productboard data into Snowflake, automating data extraction, mapping, and updates for features, companies, and notes. It supports scheduled weekly updates, data cleansing, and S

HTTP Request, Snowflake, Slack
Slack & Telegram

This workflow streamlines the entire inventory replenishment process by leveraging AI for demand forecasting and intelligent logic for supplier selection. It aggregates data from multiple sources—POS

HTTP Request, MySQL, Slack