{
  "id": "PdbwxMitcnvpqIV7",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Monitor Zendesk Churn Risk Signals and Notify CS Team in Slack",
  "tags": [],
  "nodes": [
    {
      "id": "85c1fc94-a3ce-472c-9f08-56f7272d4f66",
      "name": "Log Churn Risk to Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        672,
        1040
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "ticket_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ticket_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "subject",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "subject",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "priority",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "priority",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "created_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "updated_at",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "updated_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "channel",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "channel",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "requester_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "requester_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "assignee_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "assignee_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "organization_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "organization_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "group_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "group_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "tags",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "tags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "encoded_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "encoded_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ticket_url",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ticket_url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "is_public",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "is_public",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "satisfaction_score",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "satisfaction_score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "custom_status_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "custom_status_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "brand_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "brand_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "formatted_at",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "formatted_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "is_urgent",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "is_urgent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "has_assignee",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "has_assignee",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "priority_level",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "priority_level",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ticket_age_hours",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ticket_age_hours",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "needs_attention",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "needs_attention",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "channel_type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "channel_type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/11ojVmCr69OzkW8nGhkeXm9c3g8nNzqz5G4DFSfJvofQ/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "11ojVmCr69OzkW8nGhkeXm9c3g8nNzqz5G4DFSfJvofQ",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/11ojVmCr69OzkW8nGhkeXm9c3g8nNzqz5G4DFSfJvofQ/edit?usp=drivesdk",
          "cachedResultName": "Churn Risk Ticket"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "2913f3a0-f1ba-470c-89fb-0e8109732d36",
      "name": "Send Slack Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        672,
        1248
      ],
      "parameters": {
        "text": "=\ud83d\udea8 *Churn Risk Alert*\n\n**Ticket ID:** {{ $json.ticket_id }}\n**Rating:** {{ $json.satisfaction_score }} \n**Subject:** {{ $json.subject }} \n**Description:** {{ $json.description }} \n\n**Action Required:** Please reach out to this customer immediately to address their concerns.",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09FM9N8UEA",
          "cachedResultName": "zendesk-YOUR_OPENAI_KEY_HERE-alerts"
        },
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "3690cb18-cfca-49ad-8583-1a35442ed74d",
      "name": "Fetch Zendesk Tickets",
      "type": "n8n-nodes-base.zendesk",
      "position": [
        -112,
        1152
      ],
      "parameters": {
        "options": {},
        "operation": "getAll"
      },
      "credentials": {
        "zendeskApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3f4c0ada-de8a-4c76-970d-9d6ec950f71f",
      "name": "Format Ticket Data",
      "type": "n8n-nodes-base.code",
      "position": [
        96,
        1152
      ],
      "parameters": {
        "jsCode": "// n8n Code Node - Zendesk Ticket Formatter\n// This code processes Zendesk ticket data and formats it into clean JSON structure\n\n// Function to format individual ticket data\nfunction formatTicketData(ticketData) {\n  return {\n    ticket_id: ticketData.id,\n    subject: ticketData.subject || \"No Subject\",\n    description: ticketData.description || \"No Description\",\n    status: ticketData.status,\n    priority: ticketData.priority,\n    type: ticketData.type || \"general\",\n    created_at: ticketData.created_at,\n    updated_at: ticketData.updated_at,\n    channel: ticketData.via?.channel || \"unknown\",\n    requester_id: ticketData.requester_id,\n    assignee_id: ticketData.assignee_id,\n    organization_id: ticketData.organization_id,\n    group_id: ticketData.group_id,\n    tags: ticketData.tags || [],\n    encoded_id: ticketData.encoded_id,\n    ticket_url: ticketData.url,\n    is_public: ticketData.is_public,\n    satisfaction_score: ticketData.satisfaction_rating?.score || \"not_rated\",\n    custom_status_id: ticketData.custom_status_id,\n    brand_id: ticketData.brand_id,\n    formatted_at: new Date().toISOString(),\n    is_urgent: (ticketData.tags || []).includes('urgent'),\n    has_assignee: ticketData.assignee_id ? true : false\n  };\n}\n\n// Function to get priority level (for sorting/filtering)\nfunction getPriorityLevel(priority) {\n  const levels = {\n    'low': 1,\n    'normal': 2,\n    'high': 3,\n    'urgent': 4\n  };\n  return levels[priority] || 2;\n}\n\n// Function to calculate ticket age in hours\nfunction getTicketAgeHours(createdAt) {\n  const created = new Date(createdAt);\n  const now = new Date();\n  return Math.round((now - created) / (1000 * 60 * 60));\n}\n\n// Loop over input items and format each Zendesk ticket\nfor (const item of $input.all()) {\n  // Get the original ticket data\n  const originalTicket = item.json;\n  \n  // Format the ticket data into clean structure\n  const formattedTicket = formatTicketData(originalTicket);\n  \n  // Add additional computed fields\n  item.json = {\n    ...formattedTicket,\n    priority_level: getPriorityLevel(originalTicket.priority),\n    ticket_age_hours: getTicketAgeHours(originalTicket.created_at),\n    needs_attention: (\n      originalTicket.status === 'open' && \n      ((originalTicket.tags || []).includes('urgent') || \n       getTicketAgeHours(originalTicket.created_at) > 24)\n    ),\n    channel_type: originalTicket.via?.channel === 'web' ? 'self_service' : \n                  originalTicket.via?.channel === 'api' ? 'system_generated' : \n                  originalTicket.via?.channel || 'unknown'\n  };\n}\n\n// Return all processed items\nreturn $input.all();"
      },
      "typeVersion": 2
    },
    {
      "id": "2d7eb3f7-d214-4e7b-b316-8f831e08583a",
      "name": "StickyNote3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -208,
        752
      ],
      "parameters": {
        "width": 267,
        "height": 364,
        "content": "## \ud83d\udce5 Ticket Retrieval\n\nFetches all tickets from your Zendesk instance.\n\n**\ud83d\udd27 Customization Options:**\n- Add filters for specific ticket types\n- Limit to recent tickets (last 24h)\n- Filter by organization or user segments\n- Add status filters (open, pending, etc.)\n\n**\u26a1 Performance:** Handles 100+ tickets efficiently"
      },
      "typeVersion": 1
    },
    {
      "id": "a0d7fdf9-8067-4a7a-ac49-a7fa3509c3ab",
      "name": "StickyNote4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        32,
        1296
      ],
      "parameters": {
        "width": 263,
        "height": 328,
        "content": "## \ud83d\udd27 Data Processing\n\nTransforms raw Zendesk data into clean, structured format.\n\n**\u2728 Enhancements added:**\n- Ticket age calculation\n- Priority level mapping\n- Urgency flags\n- Channel categorization\n- Null value handling\n\n**\ud83d\udcca Output:** Clean JSON ready for filtering and alerts"
      },
      "typeVersion": 1
    },
    {
      "id": "eec3e927-9e60-4bf8-9b6c-d3f9115e1e91",
      "name": "StickyNote5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        224,
        752
      ],
      "parameters": {
        "width": 290,
        "height": 388,
        "content": "## \u2696\ufe0f Churn Risk Filter\n\nIdentifies tickets with negative satisfaction scores.\n\n**\ud83c\udfaf Current Logic:**\n- satisfaction_score = \"bad\"\n\n**\ud83d\udd04 Extend to include:**\n- Multiple bad ratings from same customer\n- High-priority unresolved tickets\n- Tickets open > 48 hours\n- VIP customer complaints\n\n**\ud83d\udcc8 Customize:** Adjust criteria based on your churn patterns"
      },
      "typeVersion": 1
    },
    {
      "id": "b75fafe4-88de-4683-9524-a14ea0c0edf2",
      "name": "StickyNote6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        624,
        624
      ],
      "parameters": {
        "width": 267,
        "height": 408,
        "content": "## \ud83d\udccb Tracking & Analytics\n\nLogs all churn risk incidents to Google Sheets for analysis.\n\n**\ud83d\udcca Captured Data:**\n- Ticket details\n- Satisfaction scores\n- Timestamp\n- Customer info\n\n**\ud83d\udca1 Use for:**\n- Monthly churn reports\n- Trend analysis\n- Team performance tracking\n- Customer health scoring"
      },
      "typeVersion": 1
    },
    {
      "id": "f910ff82-e32a-421a-b10f-9aafd2e51a92",
      "name": "StickyNote7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        624,
        1408
      ],
      "parameters": {
        "width": 298,
        "height": 409,
        "content": "## \ud83d\udea8 Instant Team Alerts\n\nSends formatted Slack message to your CS team channel.\n\n**\ud83d\udce2 Message includes:**\n- Ticket ID & direct link\n- Customer satisfaction rating\n- Subject & description\n- Clear action required\n\n**\ud83c\udfa8 Customize:**\n- Add @mentions for specific team members\n- Include customer tier/value\n- Add escalation buttons\n- Format with Slack blocks for better UX"
      },
      "typeVersion": 1
    },
    {
      "id": "5a2dbeb2-b3ce-429f-8291-d4070e26165f",
      "name": "Check Negative Feedback",
      "type": "n8n-nodes-base.if",
      "position": [
        336,
        1152
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.satisfaction_score }}",
              "value2": "bad"
            }
          ]
        },
        "combineOperation": "any"
      },
      "typeVersion": 1
    },
    {
      "id": "ceb7a743-8028-47b1-af90-95eceab7eded",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -352,
        1152
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 0 20 * * 1-5"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "e33a6d2e-cb33-4b52-a9a7-b3ee885387eb",
      "name": "StickyNote",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        1296
      ],
      "parameters": {
        "width": 263,
        "height": 312,
        "content": "## \ud83d\udd27 Automated Schedule Trigger\n\nDaily Execution: Runs every day at 8:00 PM\n\u2699\ufe0f Configuration:\n\nTrigger Rule: 0 20 * * * (Cron expression) \n\nTimezone: Set to your local timezone \n\nMode: Every Day"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "96c30166-321c-4be9-bfef-401706c70cdb",
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Fetch Zendesk Tickets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Ticket Data": {
      "main": [
        [
          {
            "node": "Check Negative Feedback",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Zendesk Tickets": {
      "main": [
        [
          {
            "node": "Format Ticket Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Negative Feedback": {
      "main": [
        [
          {
            "node": "Log Churn Risk to Sheet",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Slack Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}