Upload files to "/"

This commit is contained in:
2026-04-14 13:31:43 +00:00
commit 6bc367e852
3 changed files with 791 additions and 0 deletions
@@ -0,0 +1,677 @@
{
"name": "001 - Zendesk - Evaluation - Change Requests",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 8,14 * * *"
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-288,
480
],
"id": "5f73b6ba-196b-4e14-9f4e-5650d0987cf8",
"name": "Schedule Trigger"
},
{
"parameters": {
"promptType": "define",
"text": "=Analyze these tickets and identify change requests:\n\n{{ $json.tickets }}",
"options": {
"systemMessage": "You are an ITIL 4-aligned Change Evaluation assistant.\n\nYour task is to identify and extract ONLY valid IT change requests from incoming tickets.\n\nA ticket qualifies as a Change Request ONLY if:\n- It represents a planned change to a service, system, infrastructure, or configuration item (CI)\n- It has potential impact beyond a single user (e.g. multiple users, teams, services, or environments)\n- It introduces, modifies, or removes something in production or a controlled environment\n- It would reasonably require assessment, authorization, and scheduling under ITIL 4 Change Enablement\n\nDO NOT classify as Change Requests:\n- Incidents (break/fix, outages, user issues)\n- Service requests (access requests, password resets, software installs for one user)\n- Tasks affecting only a single user with no broader impact\n- Low-impact, routine actions that do not modify shared services or infrastructure\n- Pre-approved standard requests that are purely user-specific\n\nEdge case handling:\n- If a request affects a shared system but originates from one user, treat it as a change\n- If unclear impact, assume NOT a change unless there is evidence of broader service impact\n\nThe input tickets may be written in any language (e.g. Finnish or English). You must correctly interpret and evaluate them regardless of language.\n\nTreat infrastructure, network, and platform changes as valid Change Requests EVEN if user impact is not explicitly stated.\n\nStrong indicators of a Change Request include (in any language):\n- Network changes (VPN, firewall, routing, connectivity)\n- Cloud changes (Azure, AWS, virtual desktop, infrastructure)\n- New integrations between systems\n- Changes to shared environments or platforms\n\nExamples:\n- \"uusi VPN-yhteys\", \"VPN connection\", \"network connection\"\n- \"Azure virtual desktop\", \"cloud environment setup\"\n- \"integration between systems\"\n\nThese ALWAYS imply broader impact and must be classified as Change Requests.\n\nFor each valid Change Request, use EXACTLY these field labels with no variation:\nTicket ID:\nTicket URL:\nSubject:\nShort reason:\n\nDo not add ticket numbers to the CR report header line.\n\nONLY report valid Change Requests. Do not mention or list any other tickets.\n\nALWAYS start every response with \"CR report -\", including when no change requests are found.\nIf no change requests are found, respond exactly: \"CR report - No new change requests found\".\n\nRespond in plain text only. No markdown, no bullet symbols, no code formatting.",
"maxIterations": 10
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3.1,
"position": [
608,
480
],
"id": "146c4706-8432-4226-aaf9-906ed9cdc5f5",
"name": "AI Agent"
},
{
"parameters": {
"jsCode": "const tickets = $input.all().map(i => ({\n id: i.json.id,\n url: i.json.url,\n subject: i.json.subject,\n description: i.json.description,\n type: i.json.type,\n tags: i.json.tags\n}));\n\nreturn [{ json: { tickets: JSON.stringify(tickets) } }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
384,
480
],
"id": "44535c6c-7fc0-4011-8a54-31fbdcdfbca5",
"name": "Aggregate the results"
},
{
"parameters": {
"chatId": "8632524096",
"text": "={{ $json.output }}",
"additionalFields": {
"parse_mode": "HTML"
}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
960,
384
],
"id": "63cbdd64-de7d-465a-9b8e-12f68c960c41",
"name": "Report potential change requests",
"webhookId": "296b8fb3-720c-455e-83a0-e7daf8a70ae1",
"credentials": {
"telegramApi": {
"id": "luur85dfnEY8JM2b",
"name": "Telegram account"
}
}
},
{
"parameters": {
"model": "gpt-4o",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
"typeVersion": 1,
"position": [
-288,
1184
],
"id": "284a1241-e52e-4908-902d-fbe1858ff936",
"name": "Azure OpenAI",
"credentials": {
"azureOpenAiApi": {
"id": "WMWRB7VuQQA5tULF",
"name": "Azure Open AI account"
}
}
},
{
"parameters": {
"operation": "getAll",
"options": {
"group": 25719675640220,
"query": "created>1day status<solved"
}
},
"type": "n8n-nodes-base.zendesk",
"typeVersion": 1,
"position": [
-64,
192
],
"id": "fae13afb-85d5-4a58-8196-500764aa1267",
"name": "Get IT-Change Control",
"alwaysOutputData": true,
"credentials": {
"zendeskApi": {
"id": "WcQEnkRlDXFidIPt",
"name": "Zendesk account"
}
}
},
{
"parameters": {
"operation": "getAll",
"options": {
"group": 360000235932,
"query": "created>1day status<solved"
}
},
"type": "n8n-nodes-base.zendesk",
"typeVersion": 1,
"position": [
-64,
0
],
"id": "187382f8-2da7-4e18-8049-c622dd16e867",
"name": "Get IT-Tukipalvelut",
"alwaysOutputData": true,
"credentials": {
"zendeskApi": {
"id": "WcQEnkRlDXFidIPt",
"name": "Zendesk account"
}
}
},
{
"parameters": {
"operation": "getAll",
"options": {
"group": 360000423611,
"query": "created>1day status<solved"
}
},
"type": "n8n-nodes-base.zendesk",
"typeVersion": 1,
"position": [
-64,
960
],
"id": "1188c8c0-b30b-489c-8403-ab5bcd1f0d71",
"name": "Get IT-Hankinnat",
"alwaysOutputData": true,
"credentials": {
"zendeskApi": {
"id": "WcQEnkRlDXFidIPt",
"name": "Zendesk account"
}
}
},
{
"parameters": {
"operation": "getAll",
"options": {
"group": 8592654549660,
"query": "created>1day status<solved"
}
},
"type": "n8n-nodes-base.zendesk",
"typeVersion": 1,
"position": [
-64,
384
],
"id": "5f01553f-e75e-4336-aa28-ef8013245c03",
"name": "Get Järjestelmähankintaryhmä",
"alwaysOutputData": true,
"credentials": {
"zendeskApi": {
"id": "WcQEnkRlDXFidIPt",
"name": "Zendesk account"
}
}
},
{
"parameters": {
"operation": "getAll",
"options": {
"group": 26020691579676,
"query": "created>1day status<solved"
}
},
"type": "n8n-nodes-base.zendesk",
"typeVersion": 1,
"position": [
-64,
768
],
"id": "dd5b28da-2d1b-4cb3-8139-e3cb4a5dfab9",
"name": "Get Sovelluspaketointi",
"alwaysOutputData": true,
"credentials": {
"zendeskApi": {
"id": "WcQEnkRlDXFidIPt",
"name": "Zendesk account"
}
}
},
{
"parameters": {
"operation": "getAll",
"options": {
"group": 8394066360476,
"query": "created>1day status<solved"
}
},
"type": "n8n-nodes-base.zendesk",
"typeVersion": 1,
"position": [
-64,
576
],
"id": "d26b01fb-5afb-4e1a-9da3-9f989f206594",
"name": "Get Turvatekniikka",
"alwaysOutputData": true,
"credentials": {
"zendeskApi": {
"id": "WcQEnkRlDXFidIPt",
"name": "Zendesk account"
}
}
},
{
"parameters": {
"numberInputs": 6
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
160,
416
],
"id": "244a7b15-29da-4471-978b-b15b822f60fa",
"name": "Merge results"
},
{
"parameters": {
"resource": "page",
"operation": "get",
"id": "46"
},
"type": "n8n-nodes-bookstack.bookstack",
"typeVersion": 1,
"position": [
1408,
576
],
"id": "d306722e-00d3-44aa-98fc-53931b314530",
"name": "Get current content",
"credentials": {
"bookstackApi": {
"id": "YoX3UhoH7hJj8hqH",
"name": "BookStack account"
}
}
},
{
"parameters": {
"resource": "page",
"operation": "update",
"id": "46",
"html": "={{ $('Code - Merge Results').item.json.html }}"
},
"type": "n8n-nodes-bookstack.bookstack",
"typeVersion": 1,
"position": [
2304,
576
],
"id": "809ac83f-9d88-4d3b-8e2b-e0f1c2267a3e",
"name": "Update page contents",
"credentials": {
"bookstackApi": {
"id": "YoX3UhoH7hJj8hqH",
"name": "BookStack account"
}
}
},
{
"parameters": {
"jsCode": "const output = $('AI Agent').item.json.output?.trim() || '';\n\nif (!output || output === 'CR report - No new change requests found') {\n return [];\n}\n\nconst normalized = output.replace(/^CR report -\\s*/i, '').trim();\n\nconst matches = [...normalized.matchAll(\n /Ticket ID:\\s*(\\d+)\\s*\\nTicket URL:\\s*(https?:\\/\\/\\S+)\\s*\\nSubject:\\s*(.+?)\\s*\\nShort reason[^:]*:\\s*([\\s\\S]*?)(?=\\nTicket ID:|$)/gi\n)];\n\nconst tickets = matches.map(match => ({\n ticketId: match[1],\n ticketUrl: match[2]\n .trim()\n .replace('/api/v2/tickets/', '/agent/tickets/')\n .replace('.json', ''),\n subject: match[3].trim(),\n shortReason: match[4].trim(),\n}));\n\nreturn tickets.map(t => ({ json: t }));"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
960,
576
],
"id": "9886b8a0-2002-4f0d-952c-9223bb279ee6",
"name": "Code - Parse Tickets"
},
{
"parameters": {
"jsCode": "const existingHtml = ($json.html || '').replace(/<br id=\"bkmrk[^>]*>/g, '<br>'); // from GET node\nconst newEntries = $('Code - Aggregate Entries').item.json.newEntriesHtml;\n\nconst mergedHtml = newEntries + existingHtml;\n\nreturn [{\n json: {\n html: mergedHtml\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1632,
576
],
"id": "949371b2-7c82-447c-aeba-9a4fcbf27e3b",
"name": "Code - Merge Results"
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nconst entries = items.map(item => {\n const t = item.json;\n const timestamp = new Date().toISOString();\n\n return `\n<h4>${timestamp} — Ticket #${t.ticketId}</h4>\n<p><strong>Subject:</strong> ${t.subject}</p>\n<p><strong>URL:</strong> <a href=\"${t.ticketUrl}\">${t.ticketUrl}</a></p>\n<p><strong>Reason:</strong> ${t.shortReason || 'N/A'}</p>\n<hr>\n`;\n});\n\nreturn [{\n json: {\n newEntriesHtml: entries.join('')\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1184,
576
],
"id": "415e2faf-44f9-4e52-a2bb-ca023f3523ad",
"name": "Code - Aggregate Entries"
},
{
"parameters": {
"operation": "sendAndWait",
"chatId": "8632524096",
"message": "=About to update BookStack with the following content:\n{{ $('Code - Parse Tickets').item.json.subject }}<br>\n{{ $('Code - Parse Tickets').item.json.shortReason }}",
"approvalOptions": {
"values": {
"approvalType": "double"
}
},
"options": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
1856,
576
],
"id": "09f0b8a1-ea26-4955-81f7-fbdb0996bd48",
"name": "Send a text message",
"webhookId": "804d8245-c1ec-426b-a7ae-7150c7e75d1b",
"credentials": {
"telegramApi": {
"id": "luur85dfnEY8JM2b",
"name": "Telegram account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "8ec2c9df-805d-407c-be57-cb5579f127ad",
"leftValue": "={{ $json.data.approved }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
2080,
576
],
"id": "ceee605d-8543-4678-9cff-1d6c08e398ca",
"name": "Update Approved?"
},
{
"parameters": {
"model": {
"__rl": true,
"value": "claude-haiku-4-5-20251001",
"mode": "list",
"cachedResultName": "Claude Haiku 4.5"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
"typeVersion": 1.3,
"position": [
680,
704
],
"id": "77205775-538c-422a-86cb-8f34f6fa0169",
"name": "Anthropic Chat Model",
"credentials": {
"anthropicApi": {
"id": "HvmPHef39dQg70rt",
"name": "Anthropic account"
}
}
}
],
"pinData": {},
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "Get IT-Tukipalvelut",
"type": "main",
"index": 0
},
{
"node": "Get IT-Change Control",
"type": "main",
"index": 0
},
{
"node": "Get IT-Hankinnat",
"type": "main",
"index": 0
},
{
"node": "Get Järjestelmähankintaryhmä",
"type": "main",
"index": 0
},
{
"node": "Get Sovelluspaketointi",
"type": "main",
"index": 0
},
{
"node": "Get Turvatekniikka",
"type": "main",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Report potential change requests",
"type": "main",
"index": 0
},
{
"node": "Code - Parse Tickets",
"type": "main",
"index": 0
}
]
]
},
"Aggregate the results": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Azure OpenAI": {
"ai_languageModel": [
[]
]
},
"Get IT-Tukipalvelut": {
"main": [
[
{
"node": "Merge results",
"type": "main",
"index": 0
}
]
]
},
"Get IT-Change Control": {
"main": [
[
{
"node": "Merge results",
"type": "main",
"index": 1
}
]
]
},
"Get IT-Hankinnat": {
"main": [
[
{
"node": "Merge results",
"type": "main",
"index": 5
}
]
]
},
"Get Järjestelmähankintaryhmä": {
"main": [
[
{
"node": "Merge results",
"type": "main",
"index": 2
}
]
]
},
"Get Turvatekniikka": {
"main": [
[
{
"node": "Merge results",
"type": "main",
"index": 3
}
]
]
},
"Get Sovelluspaketointi": {
"main": [
[
{
"node": "Merge results",
"type": "main",
"index": 4
}
]
]
},
"Merge results": {
"main": [
[
{
"node": "Aggregate the results",
"type": "main",
"index": 0
}
]
]
},
"Get current content": {
"main": [
[
{
"node": "Code - Merge Results",
"type": "main",
"index": 0
}
]
]
},
"Update page contents": {
"main": [
[]
]
},
"Code - Parse Tickets": {
"main": [
[
{
"node": "Code - Aggregate Entries",
"type": "main",
"index": 0
}
]
]
},
"Code - Merge Results": {
"main": [
[
{
"node": "Send a text message",
"type": "main",
"index": 0
}
]
]
},
"Code - Aggregate Entries": {
"main": [
[
{
"node": "Get current content",
"type": "main",
"index": 0
}
]
]
},
"Report potential change requests": {
"main": [
[]
]
},
"Send a text message": {
"main": [
[
{
"node": "Update Approved?",
"type": "main",
"index": 0
}
]
]
},
"Update Approved?": {
"main": [
[
{
"node": "Update page contents",
"type": "main",
"index": 0
}
]
]
},
"Anthropic Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate",
"availableInMCP": false,
"timeSavedMode": "fixed",
"timezone": "Europe/Helsinki",
"callerPolicy": "workflowsFromSameOwner",
"timeSavedPerExecution": 30
},
"versionId": "80dbb3f0-c0d8-4839-94ba-a8696c1e7b37",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "8252f310d83bbce15f2eeef49b08efc1d1f50f69569f5e4cd407eb868f5aa0f0"
},
"id": "DB6X0Bzz796l4Vhq",
"tags": []
}
+34
View File
@@ -0,0 +1,34 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Repo overview
This repo stores n8n workflow definitions as exported JSON files. There is no build system, test runner, or CI pipeline.
## File conventions
- Each file is a self-contained n8n workflow export (JSON).
- Naming pattern: `NNN - <Integration> - <Category> - <Description>.json`
- Do not rename files without updating the `"name"` field inside the JSON to match.
## Editing workflow JSON
- The `"nodes"` array holds all logic. Each node has a `"type"`, `"parameters"`, and a unique `"id"` (UUID).
- The `"connections"` object wires nodes together by **name** (not id). If you rename a node, update every reference to it in `"connections"` as well.
- `"position"` values are canvas coordinates — adjust them to avoid overlapping nodes when adding new ones.
- `"credentials"` blocks inside nodes reference credential IDs/names that exist in the target n8n instance. Do not invent or change credential IDs.
## Current workflows
| File | Trigger | Purpose |
|------|---------|---------|
| `001 - Zendesk - Evaluation - Change Requests.json` | Cron `0 8,14 * * *` (Europe/Helsinki) | Scans 6 Zendesk groups for unsolved tickets created in the last day, uses GPT-4o (Azure OpenAI) to identify ITIL 4 change requests, sends a Telegram report, and logs findings to BookStack page #46. |
## Key integration details (001 workflow)
- **Zendesk groups polled:** IT-Tukipalvelut, IT-Change Control, Järjestelmähankintaryhmä, Turvatekniikka, Sovelluspaketointi, IT-Hankinnat.
- **AI model:** `gpt-4o` via Azure OpenAI. The agent prompt is ITIL 4-aligned and handles both Finnish and English tickets.
- **Telegram chat ID:** `8632524096`.
- **BookStack page ID:** `46` (prepends new HTML entries on each run; existing content is preserved).
- The AI output format expected by `Code - Parse Tickets` is structured text with `Ticket ID:`, `Ticket URL:`, `Subject:`, and `Short reason:` fields — changes to the AI system prompt must preserve this format or the parse regex will break.
+80
View File
@@ -0,0 +1,80 @@
# n8n Workflows
Exported n8n workflow definitions stored as version-controlled JSON files.
## Workflows
### 001 · Zendesk — Evaluation — Change Requests
Runs twice daily at 08:00 and 14:00 (Europe/Helsinki). Scans six Zendesk support groups for unsolved tickets created in the past 24 hours, uses an AI agent to identify ITIL 4 change requests, notifies via Telegram, and logs confirmed findings to a BookStack wiki page.
#### Flow
```
Schedule Trigger (cron 0 8,14 * * *)
├─ Get IT-Tukipalvelut ─┐
├─ Get IT-Change Control │
├─ Get Järjestelmähankintaryhmä ├─ Merge results (6-input)
├─ Get Turvatekniikka │
├─ Get Sovelluspaketointi │
└─ Get IT-Hankinnat ─┘
Aggregate the results (formats tickets as JSON)
AI Agent (Claude Haiku 4.5 via Anthropic)
│ │
│ Report potential change requests → Telegram (immediate notification)
Code - Parse Tickets (regex-parses structured AI output)
Code - Aggregate Entries (builds HTML blocks)
Get current content (BookStack page #46)
Code - Merge Results (prepends new entries to existing HTML)
Send a text message (Telegram approval request — double confirmation)
Update Approved? (if approved == true)
Update page contents (BookStack page #46)
```
#### Required credentials
| Credential name | Type | Used by |
|---|---|---|
| `Zendesk account` | Zendesk API | All six Zendesk fetch nodes |
| `Anthropic account` | Anthropic API | AI Agent (Claude Haiku 4.5) |
| `Telegram account` | Telegram Bot API | Immediate report + approval request |
| `BookStack account` | BookStack API | Get and update page #46 |
> The `Azure OpenAI` node exists in the canvas but is not connected to the AI Agent and is not part of the active execution path.
#### AI output contract
The AI agent must begin every response with `CR report -` and use exactly these field labels for each finding:
```
Ticket ID: <id>
Ticket URL: <url>
Subject: <text>
Short reason: <text>
```
The `Code - Parse Tickets` node uses a regex that matches this exact format. Changes to the system prompt must preserve these labels and order.
## Repository conventions
- Naming: `NNN - <Integration> - <Category> - <Description>.json`
- Keep the `"name"` field inside the JSON in sync with the filename.
- `"connections"` references node names, not IDs — update both when renaming a node.
- Do not modify `"credentials"` blocks; credential IDs and names must match the target n8n instance.
## Importing a workflow
1. Open your n8n instance.
2. Go to **Workflows → Import from file**.
3. Select the JSON file.
4. Re-link any credentials that differ from the source instance before activating.