Notificaciones
Resúmenes por correo y webhooks para las conversaciones que necesitan atención, con verificación de firma.
Hearl puede avisar a tu equipo en cuanto una conversación necesita atención. Configúralo en la página Notificaciones. Solo los propietarios y administradores pueden cambiar esta configuración.
Cuándo se envían las notificaciones
Hearl comprueba cada 15 minutos. Una conversación se incluye cuando:
- su estado de plataforma es Sin respuesta o El usuario respondió de nuevo
- su progreso sigue en Sin empezar
- la IA la juzgó como relacionada con tu producto, sin respuestas inciertas
- su prioridad es igual o superior al mínimo que definiste: todas, Media o superior, o solo Alta
Cada conversación se notifica una vez por estado. Si se notificó como Sin respuesta y después el usuario vuelve a escribir, se notifica de nuevo como El usuario respondió de nuevo.
Correo
Activa Enviar correo y añade destinatarios. Deja la lista vacía para enviar el correo a todos los miembros del espacio de trabajo. Sirve cualquier dirección, como una bandeja de soporte compartida, hasta 20 destinatarios.
Cada comprobación envía un solo resumen con hasta 10 conversaciones, con un enlace al resto en la bandeja de entrada. Elige inglés o chino en Idioma.
Webhook
Escribe un Endpoint. Debe ser una URL https pública. Hearl reconoce los bots de chat más comunes por su dirección y envía el formato de mensaje que esperan:
| Dirección | Se envía como |
|---|---|
hooks.slack.com |
Mensaje de Slack |
feishu.cn, larksuite.com |
Mensaje de texto de Feishu |
qyapi.weixin.qq.com |
Mensaje Markdown de WeCom |
oapi.dingtalk.com |
Mensaje Markdown de DingTalk |
discord.com, discordapp.com |
Mensaje de Discord |
| Cualquier otra URL | JSON genérico (abajo) |
La página muestra qué tipo detectó. Enviar prueba envía un ejemplo, y la página muestra el resultado del último envío. Una solicitud que tarda más de 8 segundos cuenta como tiempo de espera agotado.
JSON genérico
{
"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"
}
| Campo | Descripción |
|---|---|
event |
conversations.needs_attention, o test para Enviar prueba. |
workspace |
El id y el name del espacio de trabajo. |
text |
Un resumen listo para publicar. Slack lee solo este campo. |
conversations |
Todas las conversaciones de este envío. |
sent_at |
Cuándo se envió, en ISO 8601. |
platform es x, reddit o hn. status, priority y category son códigos estables en chino, para que tu código pueda compararlos sea cual sea el idioma de tu espacio de trabajo:
| Campo | Código | Significado |
|---|---|---|
status |
待回复 |
Sin respuesta |
status |
用户有新回复 |
El usuario respondió de nuevo |
priority |
高 / 中 / 低 |
Alta / Media / Baja |
category |
故障 |
Error |
category |
求助 |
Pregunta |
category |
功能建议 |
Solicitud de función |
category |
价格 |
Precios |
category |
负面反馈 |
Crítica |
category |
正面反馈 |
Elogio |
category |
购买咨询 |
Compra |
category |
其他 |
Otro |
Verificar la firma
Cuando guardas un endpoint de webhook, Hearl crea para él un Secreto de firma. Puedes mostrarlo o rotarlo en la página Notificaciones. La solicitud incluye este encabezado:
X-Hearloop-Signature: sha256=<hex>
<hex> es el HMAC-SHA256 del cuerpo sin procesar de la solicitud, con tu secreto de firma como clave, en hexadecimal en minúsculas. Calcúlalo sobre los bytes exactos que recibiste, antes de analizar el JSON, y compara en tiempo constante.
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 "", 200Devuelve cualquier estado 2xx para confirmar el envío.