---
title: Notificaciones
description: 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 [#when]

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 [#email]

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 [#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 [#json]

```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"
}
```

| 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 [#signature]

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:

```txt
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.

```js Node.js
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);
```

```python Python
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
```

Devuelve cualquier estado `2xx` para confirmar el envío.
