HaloPSA:新しいチケット → AI 要約(テンプレート)
上級
これはAI Summarization, Multimodal AI分野の自動化ワークフローで、20個のノードを含みます。主にCode, Webhook, HttpRequest, Agent, LmChatGoogleGeminiなどのノードを使用。 Gemini AI要約によるHaloPSAチケット分類の自動化
前提条件
- •HTTP Webhookエンドポイント(n8nが自動生成)
- •ターゲットAPIの認証情報が必要な場合あり
- •Google Gemini API Key
ワークフロープレビュー
ノード接続関係を可視化、ズームとパンをサポート
ワークフローをエクスポート
以下のJSON設定をn8nにインポートして、このワークフローを使用できます
{
"name": "HaloPSA: New Ticket → AI Summary (Template)",
"nodes": [
{
"name": "Note: Webhook トリガー",
"type": "n8n-nodes-base.stickyNote",
"position": [
-24,
48
],
"parameters": {
"content": "### 🔔 Webhook (HaloPSA → n8n)\nUse **POST**. Replace `WEBHOOK_PATH` below, then paste the full **Production URL** back into HaloPSA webhook.\n- Path: something unique, e.g. `halopsa-new-ticket-ai`\n- HaloPSA payload must include `ticket`, `team` or `team_id` if you use the guard."
},
"typeVersion": 1,
"id": "Note-Webhook--0"
},
{
"name": "Webhook トリガー",
"type": "n8n-nodes-base.webhook",
"position": [
0,
128
],
"parameters": {
"path": "WEBHOOK_PATH",
"options": {},
"httpMethod": "POST"
},
"typeVersion": 2.1,
"id": "Webhook--1"
},
{
"name": "ノート: Guard",
"type": "n8n-nodes-base.stickyNote",
"position": [
200,
48
],
"parameters": {
"content": "### 🚧 Guard (optional)\nSkips tickets for a team you don’t want to process.\n- Change `teamName` or `teamId` checks below (e.g. `sales` or `6`).\n- Remove this node if you don’t need filtering."
},
"typeVersion": 1,
"id": "-Guard-2"
},
{
"name": "Guard",
"type": "n8n-nodes-base.code",
"position": [
224,
128
],
"parameters": {
"jsCode": "// Guard - Stop workflow if the ticket belongs to a filtered team\nconst body = $json.body || {};\nconst teamName = String(body.team ?? body.ticket?.team ?? '').toLowerCase();\nconst teamId = Number(body.team_id ?? body.ticket?.team_id ?? NaN);\nconst isFiltered = teamName === 'sales' || teamId === 6; // <- change these\nif (isFiltered) return []; // stop\nreturn $input.all();"
},
"typeVersion": 2,
"id": "Guard-3"
},
{
"name": "ノート: Extract",
"type": "n8n-nodes-base.stickyNote",
"position": [
424,
48
],
"parameters": {
"content": "### 📦 Extract Ticket\nPulls `id`, `summary`, `details` from webhook body.\nNo keys to change unless your webhook payload uses different field names."
},
"typeVersion": 1,
"id": "-Extract-4"
},
{
"name": "Extract Ticket",
"type": "n8n-nodes-base.code",
"position": [
448,
128
],
"parameters": {
"jsCode": "const ticket = $input.item.json.body?.ticket;\nif (!ticket) throw new Error('No ticket object found in webhook payload.');\nconst summaryCard = `🆕 New ticket created\\n\\n🧾 Ticket ID: ${ticket.id}\\n🧑💻 Summary: ${ticket.summary}\\n📝 Description: ${ticket.details || 'No details provided.'}\\n`;\nreturn [{ json: { ticket_id: ticket.id, summary_card: summaryCard, details: ticket.details || '', subject: ticket.summary } }];"
},
"typeVersion": 2,
"id": "Extract-Ticket-5"
},
{
"name": "ノート: Prompt",
"type": "n8n-nodes-base.stickyNote",
"position": [
648,
48
],
"parameters": {
"content": "### 🧠 Build AI Prompt (Gemini / any LLM)\nEdit wording and the **tools/systems** your MSP uses.\nNo secrets here.\nOutputs a single `prompt` string."
},
"typeVersion": 1,
"id": "-Prompt-6"
},
{
"name": "Build AI Prompt",
"type": "n8n-nodes-base.code",
"position": [
672,
128
],
"parameters": {
"jsCode": "const { subject, details, ticket_id } = $input.item.json;\nconst prompt = `You are a senior MSP engineer. Analyze the support ticket and return **strict JSON** with: summary, next_step, troubleshooting_suggestions (HTML list), system_actions (optional HTML list).\\n\\n### Systems we use\\n1. NinjaOne (RMM)\\n2. Microsoft 365 (tenant management)\\n3. CIPP (multi-tenant admin)\\n\\n### Ticket\\n- ID: ${ticket_id}\\n- Subject: ${subject}\\n- Details:\\n${details}\\n`;\nreturn [{ json: { prompt, ticket_id } }];"
},
"typeVersion": 2,
"id": "Build-AI-Prompt-7"
},
{
"name": "Note: AI エージェント",
"type": "n8n-nodes-base.stickyNote",
"position": [
872,
48
],
"parameters": {
"content": "### 🤖 AI Agent (LangChain)\nConnect your **LLM node** here.\n- If using Gemini: add a *Google Gemini Chat Model* node and connect as the **Language Model** input.\n- Or swap for OpenAI/other LLM nodes.\n- **Set credentials in the model node, not here.**"
},
"typeVersion": 1,
"id": "Note-AI--8"
},
{
"name": "AI エージェント",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
896,
128
],
"parameters": {
"text": "={{ $json.prompt }}",
"options": {},
"promptType": "define"
},
"typeVersion": 2.2,
"id": "AI--9"
},
{
"name": "ノート: LLM Model",
"type": "n8n-nodes-base.stickyNote",
"position": [
952,
272
],
"parameters": {
"content": "### 🧩 Gemini / LLM Model Node\n**Set your API credentials** here.\n- Example: Google Gemini Chat Model\n- No other changes required."
},
"typeVersion": 1,
"id": "-LLM-Model-10"
},
{
"name": "Google Gemini チャットモデル",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
976,
352
],
"parameters": {
"options": {}
},
"typeVersion": 1,
"id": "Google-Gemini--11"
},
{
"name": "ノート: Parse",
"type": "n8n-nodes-base.stickyNote",
"position": [
1224,
48
],
"parameters": {
"content": "### 📑 Parse LLM Output (JSON)\nStrips ```json fences if present and parses to fields:\n- `summary`, `next_step`, `troubleshooting` (HTML), `ticket_id`."
},
"typeVersion": 1,
"id": "-Parse-12"
},
{
"name": "Parse AI JSON",
"type": "n8n-nodes-base.code",
"position": [
1248,
128
],
"parameters": {
"jsCode": "const raw = $json.output ?? '';\nlet clean = raw.trim();\nif (clean.startsWith('```')) {\n clean = clean.replace(/^```json\\s*/i, '').replace(/^```\\s*/i, '').replace(/```$/,'').trim();\n}\nlet parsed; try { parsed = JSON.parse(clean); } catch (e) { throw new Error('Failed to parse LLM JSON: ' + e.message); }\nreturn [{ json: { summary: parsed.summary, next_step: parsed.next_step, troubleshooting: parsed.troubleshooting_suggestions, ticket_id: $json.ticket_id } }];"
},
"typeVersion": 2,
"id": "Parse-AI-JSON-13"
},
{
"name": "Note: HTML Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
1448,
48
],
"parameters": {
"content": "### 🧱 Build HTML Note (branding)\nChange **logo URL**, colours, footer text.\nOutput: `note_html` + `ticket_id`."
},
"typeVersion": 1,
"id": "Note-HTML-Note-14"
},
{
"name": "Build AI HTML Note",
"type": "n8n-nodes-base.code",
"position": [
1472,
128
],
"parameters": {
"jsCode": "const ticket_id = $items('Extract Ticket', 0, 0)[0]?.json?.ticket_id; if (!ticket_id) throw new Error('ticket_id missing');\nconst summary = String($json.summary||'').trim();\nconst next_step = String($json.next_step||'').trim();\nconst troubleshooting_html = String($json.troubleshooting||'').trim();\nconst LOGO_URL = 'https://YOUR_LOGO_URL/logo.png'; // <- change\nconst BRAND = 'Your MSP Brand';\nconst note_html = `\n<div style=\"font-family: Arial, sans-serif; font-size: 14px; color: #333; background: #fafafa; padding: 16px; border-radius: 8px; border: 1px solid #ddd; line-height: 1.6;\">\n <div style=\"text-align: center; margin-bottom: 20px;\"><img src=\"${LOGO_URL}\" alt=\"${BRAND}\" style=\"max-width: 180px; height: auto;\" /></div>\n <h2 style=\"color: #0055a5; margin-top: 0; border-bottom: 1px solid #ccc; padding-bottom: 6px;\">🧠 AI Ticket Summary</h2>\n <p><strong>Ticket ID:</strong> ${ticket_id}</p>\n <h3 style=\"margin-top: 24px;\">Summary</h3><div>${summary || 'n/a'}</div>\n <h3 style=\"margin-top: 24px;\">Next Step</h3><div>${next_step || 'n/a'}</div>\n <h3 style=\"margin-top: 24px;\">Troubleshooting Suggestions</h3><div>${troubleshooting_html || '<em>None</em>'}</div>\n <hr style=\"margin: 30px 0; border: none; border-top: 1px solid #ccc;\" />\n <p style=\"font-size: 12px; color: #666;\">This note was generated automatically by <strong>${BRAND} – AI Assistant</strong>.</p>\n</div>`;\nreturn [{ json: { ticket_id, note_html } }];"
},
"typeVersion": 2,
"id": "Build-AI-HTML-Note-15"
},
{
"name": "ノート: Wrap",
"type": "n8n-nodes-base.stickyNote",
"position": [
1668,
48
],
"parameters": {
"content": "### 📦 Wrap for HaloPSA\nPrepares the **Actions API** payload for a Private Note.\n- Change `is_visible_to_user` / `outcome` if needed."
},
"typeVersion": 1,
"id": "-Wrap-16"
},
{
"name": "Wrap for Halo",
"type": "n8n-nodes-base.code",
"position": [
1696,
128
],
"parameters": {
"jsCode": "return [{ json: { payload: [{ ticket_id: $json.ticket_id, note_html: $json.note_html, is_visible_to_user: false, outcome: 'Private Note' }] } }];"
},
"typeVersion": 2,
"id": "Wrap-for-Halo-17"
},
{
"name": "ノート: Halo HTTP",
"type": "n8n-nodes-base.stickyNote",
"position": [
1892,
48
],
"parameters": {
"content": "### 🌐 HTTP → HaloPSA\nPOST to your HaloPSA **Actions** endpoint.\n- Base URL: `https://YOUR_HALO_DOMAIN/api/actions`\n- Auth Header: set your API token or Basic auth.\n**Replace placeholders below** in URL & Headers."
},
"typeVersion": 1,
"id": "-Halo-HTTP-18"
},
{
"name": "HaloPSA: Create ノート",
"type": "n8n-nodes-base.httpRequest",
"position": [
1920,
128
],
"parameters": {
"url": "https://YOUR_HALO_DOMAIN/api/actions",
"method": "POST",
"options": {
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_HALO_API_TOKEN"
}
},
"jsonBody": "={{ $json.payload }}",
"sendBody": true,
"specifyBody": "json"
},
"typeVersion": 4.2,
"id": "HaloPSA-Create--19"
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"connections": {
"Guard-3": {
"main": [
[
{
"node": "Extract-Ticket-5",
"type": "main",
"index": 0
}
]
]
},
"Webhook": {
"main": [
[
{
"node": "Guard-3",
"type": "main",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Parse-AI-JSON-13",
"type": "main",
"index": 0
}
]
]
},
"Parse-AI-JSON-13": {
"main": [
[
{
"node": "Build-AI-HTML-Note-15",
"type": "main",
"index": 0
}
]
]
},
"Wrap-for-Halo-17": {
"main": [
[
{
"node": "HaloPSA: Create Note",
"type": "main",
"index": 0
}
]
]
},
"Extract-Ticket-5": {
"main": [
[
{
"node": "Build-AI-Prompt-7",
"type": "main",
"index": 0
}
]
]
},
"Build-AI-Prompt-7": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Build-AI-HTML-Note-15": {
"main": [
[
{
"node": "Wrap-for-Halo-17",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
}
}
}よくある質問
このワークフローの使い方は?
上記のJSON設定コードをコピーし、n8nインスタンスで新しいワークフローを作成して「JSONからインポート」を選択、設定を貼り付けて認証情報を必要に応じて変更してください。
このワークフローはどんな場面に適していますか?
上級 - AI要約, マルチモーダルAI
有料ですか?
このワークフローは完全無料です。ただし、ワークフローで使用するサードパーティサービス(OpenAI APIなど)は別途料金が発生する場合があります。
関連ワークフロー
競合他社コンテンツギャップ分析ツール:構題マッピングの自動化
Gemini AI、Apify、Google Sheetsを使用して競合企業のコンテンツギャップを分析
If
Set
Code
+
If
Set
Code
30 ノードMychel Garzon
その他
AI履歴書最適化ツール
Gemini分析とメールレポートで履歴書を職位記述に一致させる
Set
Code
Gmail
+
Set
Code
Gmail
18 ノードMychel Garzon
AI要約
GitLab コードレビューテンプレート
Gemini AIとJIRAコンテキストを使用したGitLabマージンリクエストコードレビューの自動化
If
Set
Code
+
If
Set
Code
41 ノードEvgeny Agronsky
AI要約
会議記録の通知
Gemini AIとSlack通知を使った会議ノート要約の自動化
Set
Code
Slack
+
Set
Code
Slack
16 ノードSayone Technologies
AI要約
会議サマリジェネレーター
Google Drive、Gemini AI、Google Docsを使って会議の要約を自動化する
Code
Google Drive
Http Request
+
Code
Google Drive
Http Request
12 ノードParth Pansuriya
AI要約
Groq AIとGhostGeniusを使ってLinkedInプロフィールと求人情報を比較
Groq AI と GhostGenius を使って LinkedIn プロフィールと職位説明のマッチ度を比較する
If
Set
Code
+
If
Set
Code
17 ノードStephan Koning
その他