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など)は別途料金が発生する場合があります。

ワークフロー情報
難易度
上級
ノード数20
カテゴリー2
ノードタイプ6
難易度説明

上級者向け、16ノード以上の複雑なワークフロー

外部リンク
n8n.ioで表示

このワークフローを共有

カテゴリー

カテゴリー: 34