{ "name": "Unassigned Zendesk Tickets to Slack", "nodes": [ { "name": "On clicking 'execute'", "type": "n8n-nodes-base.manualTrigger", "position": [ 360, 350 ], "parameters": {}, "typeVersion": 1, "id": "unassigned-zendesk-tickets-to-slack-0" }, { "name": "Cron", "type": "n8n-nodes-base.cron", "color": "#068906", "position": [ 360, 560 ], "parameters": { "triggerTimes": { "item": [ { "mode": "everyDay", "hour": 16, "minute": 30 } ] } }, "typeVersion": 1, "id": "unassigned-zendesk-tickets-to-slack-1" }, { "name": "Prepare ticket summary", "type": "n8n-nodes-base.code", "position": [ 690, 460 ], "parameters": { "mode": "runOnceForAllItems", "jsCode": "function collect({previousState,body,statusCode,bodyEncoding}) {\n const state = previousState === null ? {page:1,tickets:[],count:null} : previousState;\n if (!state || !Number.isInteger(state.page) || !Array.isArray(state.tickets)) throw Error('Invalid search state');\n if (previousState !== null) {\n if (statusCode < 200 || statusCode >= 300 || bodyEncoding !== null || !body || !Array.isArray(body.results) || !Number.isInteger(body.count) || body.count < 0) throw Error('Zendesk search failed or returned an invalid page');\n if (body.count > 1000) throw Error('Zendesk search exceeds the 1000-result limit');\n if (state.count !== null && state.count !== body.count) throw Error('Zendesk search changed while paging');\n const ids = new Set(state.tickets.map(t=>t.id));\n for (const ticket of body.results) {\n if (!ticket || !Number.isSafeInteger(ticket.id) || ticket.id < 1 || typeof ticket.url !== 'string' || typeof ticket.status !== 'string' || typeof ticket.subject !== 'string' || ids.has(ticket.id)) throw Error('Invalid or duplicate Zendesk ticket');\n ids.add(ticket.id); state.tickets.push({id:ticket.id,url:ticket.url,status:ticket.status,subject:ticket.subject});\n }\n state.count=body.count;\n if (state.tickets.length > state.count || (body.next_page == null && state.tickets.length !== state.count) || (body.next_page != null && body.results.length === 0)) throw Error('Incomplete Zendesk search');\n if (body.next_page == null) return {state,more:false,query:{query:'type:ticket assignee:none status= 10) throw Error('Zendesk search exceeds the 1000-result limit');\n state.page++;\n }\n return {state,more:true,query:{query:'type:ticket assignee:none status value.replaceAll('&','&').replaceAll('<','<').replaceAll('>','>').replace(/[\\r\\n]+/g,' ').replace(/[*_~`]/g, char => ({'*':'*','_':'_','~':'~','`':'`'})[char]);\n let message='*Unassigned Tickets*\\n\\n';\n for (const t of tickets) {\n if (!t || !Number.isSafeInteger(t.id) || t.id < 1 || typeof t.url !== 'string' || typeof t.status !== 'string' || typeof t.subject !== 'string') throw Error('Invalid Zendesk ticket');\n const match = /^https:\\/\\/([a-z0-9-]+\\.zendesk\\.com)\\/api\\/v2\\/tickets\\/(\\d+)\\.json$/i.exec(t.url);\n if (!match || Number(match[2]) !== t.id) throw Error('Invalid ticket URL');\n message += `** [${escape(t.status.toUpperCase())}] - ${escape(t.subject)}\\n`;\n }\n if (message.length > 40000) throw Error('Slack message exceeds 40000 characters');\n return {message,hasTickets:tickets.length>0};\n}\n\nlet state=collect({previousState:null,body:null,statusCode:null,bodyEncoding:null}).state, result; for (const item of $input.all()) { result=collect({previousState:state,body:item.json.body,statusCode:item.json.statusCode,bodyEncoding:null}); state=result.state; } if (!result || result.more) throw Error('Incomplete Zendesk search'); const summary=summarize({tickets:result.tickets}); return summary.hasTickets ? [{json:summary}] : [];" }, "typeVersion": 2, "id": "unassigned-zendesk-tickets-to-slack-2" }, { "name": "Slack", "type": "n8n-nodes-base.slack", "position": [ 870, 460 ], "parameters": { "text": "={{$json[\"message\"]}}", "channel": "YOUR_SLACK_CHANNEL_ID", "attachments": [], "otherOptions": {} }, "typeVersion": 1, "id": "unassigned-zendesk-tickets-to-slack-3" }, { "name": "Search Zendesk tickets", "type": "n8n-nodes-base.httpRequest", "position": [ 510, 460 ], "parameters": { "method": "GET", "url": "https://YOUR_ZENDESK_SUBDOMAIN.zendesk.com/api/v2/search.json", "authentication": "predefinedCredentialType", "nodeCredentialType": "zendeskApi", "sendQuery": true, "queryParameters": { "parameters": [ { "name": "query", "value": "type:ticket assignee:none status