[Zendesk+Slack] Slack→Zendesk チケット作成:Lambda Event モードの非同期構成
概要
以前の記事で、Slack のスラッシュコマンドから Zendesk チケットを作成する際に Lambda + SQS による非同期処理構成 を紹介しました。この構成は信頼性が高く、処理の失敗にも強い一方で、構築コストやコンポーネント数の面では少々重たく感じる場面もあります。
そこで今回は、「とにかく簡単に、軽量に Slack コマンドを連携したい」という方向けに、Lambda のみで完結する構成を紹介します。この構成のカギとなるのが、 Lambda の「Event モード(非同期呼び出し)」 です。
Slack × Lambda で 3 秒制限を回避するには
Slack のスラッシュコマンドには、リクエスト送信後 3 秒以内に成功を返す必要があるという制約があります。そのため、Zendesk など外部 API を呼び出す処理を直接 Lambda 内で行うと、3 秒を超えてタイムアウトエラーになるリスクがあります。従来はこの問題を避けるために、Lambda → SQS → Lambda という構成で非同期処理を実現していました。
Event モードなら SQS なしでも非同期にできる
Lambda には InvocationType が存在し、デフォルトでは RequestResponse(同期)として実行されます。しかし、Lambda の InvocationType を Event に変更することで、Lambda を非同期に呼び出すことが可能です。
この仕組みを活用すれば、Slack に対しては即時に応答でき、Lambda 関数は Zendesk API の呼び出しなど時間のかかる処理を裏で完了させるという形が実現できます。
この記事のゴール
本記事では、Slack のスラッシュコマンド /zendesk-create-ticket を実行した際に、Zendesk API を通じてチケットを作成する処理を Lambda のみで非同期実行する構成を紹介します。Lambda の InvocationType を Event に設定することで、Slack の 3 秒制限を回避しつつ、サーバーレス構成をよりシンプルに保つ方法を解説します。
対象読者
- Slack コマンドから手軽に API を呼び出す仕組みを試したい方
- Event モードを利用した AWS Lambda による非同期処理を試してみたい方
- Zendesk API(チケット作成)を使った Slack 連携を短時間で構築したい方
想定ユースケース
- 技術支援チームが Slack から問い合わせを Zendesk に一括登録
- Slack 内で発生したインシデントをログとして Zendesk に登録
- Zendesk のフォームを使う前の仮保存用メモ書き
参考
- https://docs.aws.amazon.com/ja_jp/lambda/latest/api/API_Invoke.html
- https://docs.aws.amazon.com/ja_jp/whitepapers/latest/security-overview-aws-lambda/lambda-invoke-modes.html
- https://repost.aws/ja/knowledge-center/api-gateway-invoke-lambda
全体構成図

この構成では、Slack スラッシュコマンドをトリガーに、Lamba(proxy) を介して本体の Lambda 関数が Event モード で非同期実行されます。非同期実行された Lambda(本体) は Zendesk API を呼び出し、チケットを作成します。Slack 側には即時に 200 OK が返されるため、3 秒ルールをクリアしつつ、バックグラウンドで安全にチケット作成を行うことができます。
ポイントは以下のとおりです:
- Lambda(proxy) は Lambda(本体) を非同期(InvocationType:
Event)で呼び出す - Lambda(本体) は Slack の署名検証と Zendesk API 呼び出しを行う
- SQS やステートフルなバッファは不要
これにより、構成の単純化と開発コストの削減が両立できます。
初期設定:Zendesk コンソールでの操作
Slack コマンドから Zendesk にチケットを起票するには、Zendesk 側で API トークンを取得しておく必要があります。本記事では、トークン認証を使用した構成とします。
1. 管理者アカウントでログイン
まず、Zendesk に管理者アカウントでログインします。
2. API トークンを有効化する
- 右上のアイコンから「管理センター」を開きます
- 「アプリおよびインテグレーション」 > 「Zendesk API」 を選択
- 「トークンアクセスを有効にする」を ON にします
- 「API トークンを追加」をクリックして新しいトークンを発行します
トークンは一度しか表示されないため、忘れずにコピーして安全な場所に保存しておきます。

これは後ほど Lambda 関数側で認証ヘッダーとして使用します。
3. 検証用のテストチケットを 1 件作成しておく(任意)
動作確認の際に差分を把握しやすくするため、Zendesk 側に手動で 1 件チケットを作っておくと便利です。
Lambda(本体) 実装
非同期的に、Zendesk の API を使ってチケットを作成する Lambda 関数を実装します。
主な役割
- Slack からのリクエストに含まれる
textパラメータをパース text全体を Zendesk チケットの本文として利用(件名は固定)- 署名検証により不正な呼び出しをブロック
- Zendesk API を使ってチケットを作成
- 処理結果を
console.log()などで CloudWatch Logs に記録
事前準備(環境変数)
| 環境変数名 | 内容 |
|---|---|
SLACK_SIGNING_SECRET |
Slack アプリの Signing Secret |
ZENDESK_EMAIL |
Zendesk ログインメールアドレス |
ZENDESK_API_TOKEN |
発行済みの Zendesk API トークン |
ZENDESK_SUBDOMAIN |
Zendesk サブドメイン(例: yourcompany) |
実装例:index.js
const crypto = require("crypto");
const querystring = require("querystring");
const fetch = require("node-fetch");
exports.handler = async (event) => {
const headers = Object.fromEntries(
Object.entries(event.headers || {}).map(([k, v]) => [k.toLowerCase(), v])
);
const timestamp = headers["x-slack-request-timestamp"];
const signature = headers["x-slack-signature"];
if (!timestamp || !signature) {
console.log("❌ 必要な署名ヘッダーがありません");
return;
}
const now = Math.floor(Date.now() / 1000);
// リプレイ攻撃対策(5分以内)
if (Math.abs(now - timestamp) > 60 * 5) {
console.log("❌ リクエストが古すぎます");
return;
}
const rawBody = event.isBase64Encoded
? Buffer.from(event.body, "base64").toString("utf8")
: event.body;
const baseString = `v0:${timestamp}:${rawBody}`;
const expectedSignature = "v0=" + crypto
.createHmac("sha256", process.env.SLACK_SIGNING_SECRET)
.update(baseString)
.digest("hex");
// 署名検証
if (!crypto.timingSafeEqual(
Buffer.from(signature, "utf8"),
Buffer.from(expectedSignature, "utf8")
)) {
console.log("❌ 署名検証に失敗しました");
return;
}
const parsed = querystring.parse(rawBody);
const message = parsed.text || "(本文なし)";
const auth = Buffer.from(
`${process.env.ZENDESK_EMAIL}/token:${process.env.ZENDESK_API_TOKEN}`
).toString("base64");
const ticketPayload = {
ticket: {
subject: "Slackからの問い合わせ",
comment: {
body: message,
},
},
};
try {
const res = await fetch(`https://${process.env.ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/tickets.json`, {
method: "POST",
headers: {
Authorization: `Basic ${auth}`,
"Content-Type": "application/json",
},
body: JSON.stringify(ticketPayload),
});
const result = await res.json();
if (!res.ok) {
console.error("❌ チケット作成失敗:", result);
} else {
console.log("✅ チケット作成成功:", result.ticket?.id || "ID不明");
}
} catch (err) {
console.error("❌ Zendesk API 呼び出しエラー:", err);
}
};
テストイベントの例
{
"headers": {
"X-Slack-Request-Timestamp": "1234567890",
"X-Slack-Signature": "v0=fake"
},
"body": "text=Slack%E3%81%8B%E3%82%89%E3%81%AE%E5%95%8F%E3%81%84%E5%90%88%E3%82%8F%E3%81%9B",
"isBase64Encoded": false
}
Lambda(proxy) 実装
Slack スラッシュコマンド /zendesk-create-ticket から送られたリクエストは、まずこの proxy 関数で受け取ります。この関数は以下のような役割を担います:
- Slack からのリクエスト(署名ヘッダー付き HTTP POST)を受け付ける
- 署名検証や Zendesk API 呼び出しは行わず、そのまま本体 Lambda に非同期で転送
- Slack 側には即座に
200 OKを返すことで、3 秒タイムアウト制限を回避
環境変数の設定
この proxy 関数には、以下の環境変数を設定してください:
| 環境変数名 | 内容 |
|---|---|
ZENDESK_HANDLER_FUNCTION_NAME |
呼び出し先 Lambda 関数の名前(または ARN) |
IAM ロール設定(lambda → lambda の実行)
Lambda(proxy) が本体の Lambda(handler) を呼び出すには、proxy 側の実行ロールに対して lambda:InvokeFunction 権限を付与する必要があります。
Lambda(proxy) に割り当てられている実行ロールに、以下のインラインポリシーまたはマネージドポリシーを追加してください。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:zendesk-ticket-handler"
}
]
}
実装例:index.js
const { LambdaClient, InvokeCommand } = require("@aws-sdk/client-lambda");
const client = new LambdaClient({ region: "ap-northeast-1" });
exports.handler = async (event) => {
const command = new InvokeCommand({
FunctionName: process.env.ZENDESK_HANDLER_FUNCTION_NAME,
InvocationType: "Event", // 非同期で起動
Payload: Buffer.from(JSON.stringify(event)), // Slack のリクエストをそのまま渡す
});
try {
const response = await client.send(command);
console.log("✅ Zendesk handler invoked:", response.StatusCode);
} catch (err) {
console.error("❌ Failed to invoke Zendesk handler:", err);
}
return {
statusCode: 200,
body: "リクエストを受け付けました",
};
};
Lambda(proxy) に API Gateway トリガーを設定する
Slack のスラッシュコマンドからのリクエストを受け取るために、Lambda(proxy) に API Gateway をトリガーとして追加します。ここでは、Lambda コンソールから直接 HTTP API を作成する方法を紹介します。 トリガーの追加 ボタンをクリックします。
トリガータイプとしてAPI Gatewayを選択します。API タイプはHTTP APIを選択します。API の作成方法は新しい APIを選択します。セキュリティはオープン(認証なし) を選択します。
トリガーが追加されると、 API Gateway のエンドポイント URL が表示されます。この URL をコピーして控えておきます。後ほど Slack のスラッシュコマンドの設定で使用します。
API Gateway の設定を開き、左メニュー Integrations > 作成した API の ANY > 統合を管理 を選択します。 ペイロード形式のバージョン を 1.0 に設定します。
Slack アプリの設定
Slack からのスラッシュコマンド入力を受け取るには、Slack アプリの作成と設定が必要です。このセクションでは、署名検証に使う Signing Secret の取得と、スラッシュコマンドの登録方法を解説します。
Signing Secret の取得
Slack から送信されるリクエストの正当性を検証するために、Signing Secret(署名用シークレット) を Lambda に設定します。
- Slack API にアクセス
Create New App→From scratch

- 名前とワークスペースを指定してアプリ作成
- 左メニュー
Basic Information→App CredentialsからSigning Secretをコピー

スラッシュコマンドの作成
Slack アプリ左メニュー Slash Commands → Create New Command を選択し、以下のように設定:
| 項目 | 設定値の例 |
|---|---|
| Command | /zendesk-create-ticket |
| Request URL | Lambda(proxy) に紐づけた API Gateway のエンドポイント URL(例: https://abc123.execute-api.ap-northeast-1.amazonaws.com/zendesk) |
| Short Description | Zendesk チケットを作成します |
| Usage Hint | 問い合わせ内容を入力してください(例: Slack アプリの通知が届かない) |
アプリのインストール
左メニュー Install App → Install to Workspace をクリックし、権限を許可してインストールします。
動作確認
Slack 上で次のようにコマンドを入力します:
/zendesk-create-ticket 今すぐ対応が必要な問い合わせです!
正しく動作していれば、下記のようにチケットが起票されます。

まとめと今後の展望
本記事では、Slack スラッシュコマンドから Zendesk API を安全かつシンプルに呼び出す構成として、Lambda(proxy) + Lambda(handler) による 非同期実行パターン(Event モード) を紹介しました。
従来のように SQS や Step Functions を用いず、Lambda の Event モードを活用することで、Slack の 3 秒制限をクリアしながら構成を大幅に簡素化できるのが最大のメリットです。
署名検証などのセキュリティ対策も盛り込みつつ、運用に乗せられる実用性の高い構成として整えました。
この構成は、以下のような用途にも応用可能です:
- Slack からの問い合わせやアクションを他システムへ中継する「共通リクエスト受付基盤」
- Webhook や REST API 経由でのバックエンド処理トリガー
- 複数の処理系を切り替えて呼び出す「軽量な実行ハブ」
小規模な PoC はもちろん、実業務への組み込みにも十分対応できる構成です。
コストを抑えつつ安全・確実な連携を実現したい方は、ぜひ本記事の内容を参考に導入を検討してみてください。








