通知
対応が必要な会話のメールダイジェストと Webhook。署名の検証についても説明します。
Hearl は、会話に対応が必要になった時点でチームに知らせることができます。設定は通知ページで行います。この設定を変更できるのは、オーナーと管理者のみです。
通知が送られるタイミング
Hearl は15分ごとにチェックします。会話は、次の条件をすべて満たすと対象になります。
- プラットフォームのステータスが未返信またはユーザーが再返信である
- 進捗が未着手のままである
- Hearl の AI が製品に関するものと判定していて、不確かな回答がない
- 優先度が、設定した最低ラインと同じかそれ以上である。選べるのは、すべて、中以上、高のみのいずれかです
通知はステータスごとに、会話1件につき1回送られます。未返信として通知された会話で、その後ユーザーが再返信した場合は、ユーザーが再返信として改めて通知されます。
メール
メールを送信をオンにして、受信者を追加します。リストを空にすると、ワークスペースの全メンバーにメールが届きます。共有のサポート用アドレスなど、どのアドレスでも指定でき、最大20件までです。
チェックのたびに、最大10件の会話を載せたダイジェストを1通送ります。残りの会話は、受信トレイへのリンクから確認できます。言語で、英語か中国語を選べます。
Webhook
エンドポイントを入力します。公開された https の URL である必要があります。Hearl は、アドレスから一般的なチャットボットを判別し、それぞれが求める形式でメッセージを送ります。
| アドレス | 送信形式 |
|---|---|
hooks.slack.com |
Slack メッセージ |
feishu.cn, larksuite.com |
Feishu テキストメッセージ |
qyapi.weixin.qq.com |
WeCom Markdown メッセージ |
oapi.dingtalk.com |
DingTalk Markdown メッセージ |
discord.com, discordapp.com |
Discord メッセージ |
| その他の URL | 汎用 JSON(下記) |
ページには、検出された種類が表示されます。テスト送信でサンプルを送信でき、直近のプッシュの結果もページに表示されます。リクエストに8秒を超える時間がかかると、タイムアウトとして扱われます。
汎用 JSON
{
"event": "conversations.needs_attention",
"workspace": { "id": "org_123", "name": "Acme" },
"text": "*2 conversations need attention* · Acme\n• [reddit] Sync stopped working after 2.3 — No reply yet <https://…>",
"conversations": [
{
"id": "c_8f2a",
"platform": "reddit",
"product": "Acme",
"title": "Sync stopped working after 2.3",
"author": "some_user",
"url": "https://www.reddit.com/r/…",
"status": "待回复",
"priority": "高",
"category": "故障",
"inbox_url": "https://hearl.org/app?c=c_8f2a"
}
],
"sent_at": "2026-11-08T09:15:00.000Z"
}
| フィールド | 説明 |
|---|---|
event |
conversations.needs_attention。テスト送信の場合は test。 |
workspace |
ワークスペースの id と name。 |
text |
そのまま投稿できる要約。Slack はこのフィールドだけを読み取ります。 |
conversations |
このプッシュに含まれるすべての会話。 |
sent_at |
プッシュを送信した日時(ISO 8601 形式)。 |
platform は x、reddit、hn のいずれかです。status、priority、category は中国語の固定コードです。ワークスペースの言語にかかわらず、コード側でこの値を使って判別できます。
| フィールド | コード | 意味 |
|---|---|---|
status |
待回复 |
未返信 |
status |
用户有新回复 |
ユーザーが再返信 |
priority |
高 / 中 / 低 |
高 / 中 / 低 |
category |
故障 |
不具合 |
category |
求助 |
質問 |
category |
功能建议 |
機能要望 |
category |
价格 |
料金 |
category |
负面反馈 |
批判 |
category |
正面反馈 |
称賛 |
category |
购买咨询 |
購入検討 |
category |
其他 |
その他 |
署名を検証する
Webhook のエンドポイントを保存すると、Hearl はそのエンドポイント用の署名シークレットを作成します。通知ページで、表示またはローテーションできます。リクエストには、次のヘッダーが付きます。
X-Hearloop-Signature: sha256=<hex>
<hex> は、リクエストボディ(生データ)の HMAC-SHA256 を、署名シークレットをキーとして計算し、小文字の16進数で表したものです。受信したバイト列をそのまま使い、JSON を解析する前に計算してください。比較は定数時間で行います。
import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";
const app = express();
const secret = process.env.HEARL_WEBHOOK_SECRET;
app.post("/hearl", express.raw({ type: "application/json" }), (req, res) => {
const expected = "sha256=" + createHmac("sha256", secret).update(req.body).digest("hex");
const received = req.get("x-hearloop-signature") ?? "";
const ok =
received.length === expected.length &&
timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!ok) return res.status(401).end();
const payload = JSON.parse(req.body.toString("utf8"));
console.log(payload.event, payload.conversations.length);
res.status(200).end();
});
app.listen(3000);import hashlib
import hmac
import json
import os
from flask import Flask, abort, request
app = Flask(__name__)
SECRET = os.environ["HEARL_WEBHOOK_SECRET"].encode()
@app.post("/hearl")
def hearl():
body = request.get_data()
expected = "sha256=" + hmac.new(SECRET, body, hashlib.sha256).hexdigest()
received = request.headers.get("X-Hearloop-Signature", "")
if not hmac.compare_digest(received, expected):
abort(401)
payload = json.loads(body)
print(payload["event"], len(payload["conversations"]))
return "", 200プッシュを受け取ったことを知らせるには、任意の 2xx ステータスを返してください。