Twilio × Zendesk 連携 - SMS でチケットのやり取りを可能にする

Twilio × Zendesk 連携 - SMS でチケットのやり取りを可能にする

Twilio の SMS 機能と Zendesk のチケット管理システムを連携させ、 SMS を通じてカスタマーサポートを提供する方法を解説します。顧客は SMS でサポートリクエストを送信でき、エージェントは Zendesk 上でチケット管理を行いながら SMS で返信することが可能になります。

はじめに

本記事では、 Twilio の SMS 送受信と Zendesk のチケット管理を連携させる方法について解説します。

実装イメージ

Twilio とは

Twilio は、開発者が音声通話、 SMS 、ビデオ通話などの通信機能をアプリに組み込むためのクラウド通信プラットフォームです。 API を通じて簡単に SMS の送受信機能を実装できます。

Zendesk とは

Zendesk は、カスタマーサービスとサポートチケット管理のためのクラウドプラットフォームです。チケットの作成、追跡、解決までの一連の流れを効率的に管理できます。

対象読者

  • Twilio と Zendesk の基本的な操作に慣れている開発者
  • SMS を活用したカスタマーサポートシステムの構築を検討している方
  • API 連携の実装経験がある方

参考

全体構成

SMS 受信

SMS 受信時の Webhook で Twilio Function を呼び出し、 Twilio Function から Zendesk チケットの作成/更新 API を呼び出します。

SMS 受信の構成

SMS 送信

チケット更新をトリガーに Webhook で Twilio Function を呼び出し、 Twilio Function から SMS 送信の API を呼び出します。

SMS 送信の構成

SMS 受信をトリガーにチケットを作成/更新

SMS を受信した際に、既存のオープンチケットがあれば更新し、なければ新規作成する機能を実装します。

Twilio Webhook に設定するエンドポイントを作成

Twilio Functions を使用して、 SMS 受信時の処理を行うエンドポイントを作成します。

Twilio コンソール にアクセスし、 Functions and Assets > Services で Service を作成します。

Create Service

Function を作成します。今回は例として update-ticket という名前をつけます。

update-ticket function を作成

update-ticket の内容は下記のとおりです。今回の実装では、 オープン 状態であるチケットを検索し、すでに自分の電話番号チケットがあればそのチケットを更新します。

/**
 * Twilio SMS 受信 → Zendesk チケット作成/更新
 * SMS を受信した際に、既存のオープンチケットがあれば更新し、なければ新規作成する
 */
exports.handler = function(context, event, callback) {
    const twilio = require('twilio');
    const axios = require('axios');

    // SMS 受信データの取得
    const fromNumber = event.From;
    const messageBody = event.Body;

    // Zendesk API 設定
    const zendeskDomain = context.ZENDESK_DOMAIN;
    const zendeskEmail = context.ZENDESK_EMAIL;
    const zendeskToken = context.ZENDESK_TOKEN;
    const zendeskAuth = Buffer.from(`${zendeskEmail}/token:${zendeskToken}`).toString('base64');

    /**
     * SMS メッセージを処理してチケットを作成または更新する
     */
    async function processMessage() {
        try {
            // 送信元番号を含むオープンチケットを検索
            const searchUrl = `https://${zendeskDomain}/api/v2/search.json?query=type:ticket status:open "${fromNumber}"`;

            const searchResponse = await axios.get(searchUrl, {
                headers: {
                    'Authorization': `Basic ${zendeskAuth}`,
                    'Content-Type': 'application/json'
                }
            });

            console.log(`Found ${searchResponse.data.results.length} open tickets containing ${fromNumber}`);

            if (searchResponse.data.results.length > 0) {
                // 既存チケットにコメントを追加
                const ticketId = searchResponse.data.results[0].id;
                const commentUrl = `https://${zendeskDomain}/api/v2/tickets/${ticketId}.json`;

                await axios.put(commentUrl, {
                    ticket: {
                        comment: {
                            body: `SMS from ${fromNumber}: ${messageBody}`,
                            public: false
                        }
                    }
                }, {
                    headers: {
                        'Authorization': `Basic ${zendeskAuth}`,
                        'Content-Type': 'application/json'
                    }
                });

                console.log(`Updated existing ticket: ${ticketId}`);
            } else {
                // 新規チケットを作成
                const createUrl = `https://${zendeskDomain}/api/v2/tickets.json`;

                await axios.post(createUrl, {
                    ticket: {
                        subject: `SMS from ${fromNumber}`,
                        description: `SMS from ${fromNumber}: ${messageBody}`,
                        requester: {
                            email: zendeskEmail
                        },
                        priority: 'normal',
                        type: 'question'
                    }
                }, {
                    headers: {
                        'Authorization': `Basic ${zendeskAuth}`,
                        'Content-Type': 'application/json'
                    }
                });

                console.log('Created new ticket');
            }

        } catch (error) {
            console.error('Error processing message:', error);
            throw error;
        }
    }

    // メッセージ処理を実行し、Twilio に応答を返す
    processMessage()
        .then(() => {
            callback(null, new twilio.twiml.MessagingResponse());
        })
        .catch(error => {
            console.error(error);
            callback(error);
        });
};

Environment Variables に以下を設定します。

  • ZENDESK_DOMAIN: your-domain.zendesk.com
  • ZENDESK_EMAIL: サポート担当者のメールアドレス
  • ZENDESK_TOKEN: Zendesk API トークン

環境変数

Dependencies に axios を追加します。 Version 欄は空のまま Add して構いません。

Dependencies 設定

エンドポイントのメニューの Copy URL から、エンドポイント URL を取得しておきます。

エンドポイント URL の取得

Twilio Webhook の設定

SMS を受信したときに、エンドポイント URL にリクエストが送信されるよう設定します。

  1. Phone Numbers > Manage > Active Numbers にアクセス
  2. 今回使用する電話番号を選択
  3. Messaging Configuration セクションの A message comes in の Webhook URL に作成した Functions の URL を設定
  4. HTTP を HTTP POST に設定
  5. Save configuration で保存

Twilio Webhook 設定

チケットへのコメント追加をトリガーに SMS を送信

エージェントが Zendesk でコメントを追加した際に、自動的に SMS で顧客に通知する機能を実装します。

Zendesk Webhook に設定するエンドポイントを作成

Twilio Functions を使用して、チケット内容を受け取り SMS 送信するエンドポイントを作成します。今回は例として send-sms という名前をつけます。

send-sms エンドポイント

send-sms の実装は以下の通りです。

/**
 * Zendesk チケット更新 → Twilio SMS 送信
 * エージェントがコメントを追加した際に、SMS で通知を送信する
 */
exports.handler = function(context, event, callback) {
    const twilio = require('twilio');

    // Twilio クライアントの初期化
    const client = context.getTwilioClient();
    const twilioPhoneNumber = context.TWILIO_PHONE_NUMBER;

    /**
     * Zendesk からの Webhook データを処理して SMS を送信する
     */
    async function processTrigger() {
        try {
            // Zendesk からの Webhook データをログ出力
            console.log('Received Zendesk webhook:', JSON.stringify(event, null, 2));

            // チケットとコメントの情報を取得
            const ticketSubject = event.ticket_subject || event.subject;
            const ticketId = event.ticket_id || event.id;
            const commentBody = event.comment_body || event.latest_comment;

            if (!ticketSubject || !commentBody) {
                console.log('Missing required data - Subject or Comment');
                return callback(null, { 
                    success: false, 
                    error: 'Missing ticket subject or comment body' 
                });
            }

            // チケット件名から送信先電話番号を抽出
            const phoneMatch = ticketSubject.match(/SMS from (\+\d+)/);

            if (!phoneMatch) {
                console.log('No phone number found in ticket subject:', ticketSubject);
                return callback(null, { 
                    success: false, 
                    error: 'No phone number found in ticket subject' 
                });
            }

            const toPhoneNumber = phoneMatch[1];
            console.log(`Sending SMS to: ${toPhoneNumber}`);

            // SMS メッセージの作成
            const smsMessage = `Zendesk Ticket #${ticketId}: ${commentBody}`;

            // SMS を送信
            const message = await client.messages.create({
                body: smsMessage,
                from: twilioPhoneNumber,
                to: toPhoneNumber
            });

            console.log(`SMS sent successfully. Message SID: ${message.sid}`);

            callback(null, {
                success: true,
                messageSid: message.sid,
                sentTo: toPhoneNumber
            });

        } catch (error) {
            console.error('Error processing Zendesk trigger:', error);
            callback(null, {
                success: false,
                error: error.message
            });
        }
    }

    // Webhook 処理を実行
    processTrigger();
};

Environment Variables に以下を設定します。

  • TWILIO_PHONE_NUMBER: SMS 送信時、 From として使用する Twilio の電話番号

今回は検証のためエンドポイントの設定を Public に設定します。

Public に設定

Copy URL でエンドポイント URL を取得します。

Zendesk Webhook の設定

Zendesk 管理センター > アプリおよびインテグレーション > Webhook で Webhook を作成します。

  • 接続方法を選択する: トリガまたは自動化
    接続方法
  • 名前: 任意 (例: send-sms-webhook)
  • エンドポイント URL: Twilio Function の エンドポイント URL (例: https://zendesk-tickets-9999.twil.io/send-sms)
  • リクエスト方法: POST
  • リクエスト形式: JSON
  • 認証: なし
    Webhook 設定

Zendesk Trigger の設定

Zendesk 管理センター > オブジェクトとルール > ビジネスルール > トリガ でトリガを作成します。

  • トリガ名: 任意 (例: send-sms-trigger)
  • 条件:
    • 以下の条件をすべて満たす:
      • チケット > コメント , = , パブリック
      • チケット > 件名のテキスト , 次の文字列を含む , SMS from
        条件の設定
  • アクション:
    • 通知方法 > アクティブなWebhook , send-sms-webhook
    • Body
      {
        "ticket_id": "{{ticket.id}}",
        "ticket_subject": "{{ticket.title}}",
        "comment_body": "{{ticket.latest_comment.value}}",
        "requester_name": "{{ticket.requester.name}}",
        "agent_name": "{{current_user.name}}"
      }
      
    アクションの設定

動作確認

以下の動作確認を行いました。

  1. Twilio の電話番号に SMS を送信
  2. Zendesk で新しいチケットが作成されることを確認
  3. Zendesk でチケットを オープン にし、パブリックコメントを追加
  4. SMS が送信されることを確認
  5. 同じ番号から再度 SMS を送信
  6. 既存チケットにコメントが追加されることを確認

Zendesk 側の表示

スマートフォン側の表示

まとめ

本記事では、 Twilio と Zendesk を連携させて SMS ベースのカスタマーサポートシステムを構築する方法について解説しました。この実装により、顧客が SMS という身近な手段でサポートを受けられる環境が用意できます。本記事の実装をベースに、セキュリティの強化やさらなる機能拡張を行うこともご検討ください。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.