> ## Documentation Index
> Fetch the complete documentation index at: https://docs.visitoai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Administrar webhooks

> Crea un endpoint, verifica eventos firmados, prueba con ngrok y revisa los intentos de entrega desde Desarrolladores → Webhooks.

Los webhooks notifican a tu aplicación cuando ocurren eventos compatibles en tu espacio de trabajo. Abre **Desarrolladores → Webhooks** con una cuenta de administrador. Usas tu sesión del dashboard: **no necesitas una clave de API** para crear o administrar endpoints aquí.

Esta guía cubre suscripciones M2M. El webhook de una acción **Capturar datos** se configura por separado en Acciones.

<Note>
  Las capturas muestran la interfaz en inglés y una prueba local controlada con identificadores sintéticos. Sustituye el dominio de ngrok por el tuyo. No se creó ni reprodujo una conversación de cliente para estas pruebas.
</Note>

## Antes de empezar

Necesitas acceso de administrador, un receptor bajo tu control y una dirección HTTPS pública en el puerto 443. No se admiten `localhost:3000`, HTTP, direcciones privadas ni destinos que solo tengan IPv6. Para desarrollo local, usa un túnel HTTPS.

Si aparecen avisos de configuración de firma, trabajador no disponible o entregas no habilitadas para el espacio, contacta con tu administrador de Visito. **Habilitado** describe la configuración del endpoint; no demuestra que las entregas funcionen. Crear un endpoint requiere un trabajador compatible y configuración de firma; enviar pruebas también requiere que el espacio esté habilitado para entregas.

## 1. Iniciar un receptor local

El [ejemplo completo en Python](https://github.com/visito-ai/visito-docs/tree/main/examples/webhook-receiver) verifica la firma sobre los bytes exactos, comprueba la fecha, guarda los eventos aceptados en SQLite y elimina duplicados por ID de evento. Requiere Python 3.10 o posterior y solo usa la biblioteca estándar.

Desde el directorio del ejemplo:

```bash theme={null}
python3 -m venv .venv
source .venv/bin/activate
WEBHOOK_SECRET_FILE=./webhook-secret.txt \
WEBHOOK_DB=./webhook-events.sqlite3 \
WEBHOOK_FAIL_FIRST=1 python3 server.py
```

Déjalo en ejecución. Hasta guardar el secreto de firma, responde `503`. `WEBHOOK_FAIL_FIRST=1` devuelve intencionalmente `503` a la primera solicitud correctamente firmada de cada evento y acepta el reintento. Omítelo para aceptar normalmente.

En otra terminal, inicia tu cliente ngrok ya configurado:

```bash theme={null}
ngrok http 8765
```

Copia la dirección pública **HTTPS**. Si tienes un dominio reservado, usa `ngrok http 8765 --url=TU-DOMINIO`. El destino del webhook será:

```text theme={null}
https://TU-DOMINIO/webhooks/visito
```

Mantén ambos procesos activos durante la prueba. Tu computadora, receptor y túnel deben estar disponibles. Este receptor de un solo proceso es un ejemplo de desarrollo; en producción necesitas alojamiento administrado, procesamiento acotado, monitoreo y trabajo posterior persistente.

## 2. Crear el webhook

1. En el espacio correcto, abre **Desarrolladores → Webhooks** y selecciona **Crear webhook** (**Create webhook** en las capturas).
2. Introduce un **Nombre** reconocible y el **Destino HTTPS** completo, incluido `/webhooks/visito` para este ejemplo.
3. Selecciona los eventos necesarios. Para esta prueba, elige solo `message.created`.
4. Selecciona **Guardar webhook**.
5. Copia y guarda el secreto inmediatamente. Solo se muestra una vez. No hay selector de claves de API.

<img src="https://mintcdn.com/muhammadtest/QlQR9y4q6jBU1DlD/images/webhooks/create.jpg?fit=max&auto=format&n=QlQR9y4q6jBU1DlD&q=85&s=610f26556ce0f5c747787eea65ea1b33" alt="Crear un webhook con nombre, destino HTTPS público y evento seleccionado" width="1512" height="828" data-path="images/webhooks/create.jpg" />

Guarda el secreto sin incluirlo en el historial de comandos:

```bash theme={null}
python3 - <<'PY'
import getpass, os
from pathlib import Path
secret = getpass.getpass("Pega el secreto de firma: ").strip()
if not secret:
    raise SystemExit("No se introdujo un secreto")
path = Path("webhook-secret.txt")
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
with os.fdopen(fd, "w") as out:
    out.write(secret)
os.chmod(path, 0o600)
print("Secreto guardado")
PY
```

Ejecuta el comando desde el directorio del ejemplo. El receptor lee el archivo en cada solicitud; no necesita reiniciarse. Nunca incluyas secretos en Git, capturas, tickets, URLs ni registros. Este secreto verifica entregas; no es un token de acceso a la API de Visito.

### Eventos disponibles

| Evento | Qué representa |
| - | - |
| `message.created` | Se creó un mensaje compatible. Comprueba `data.direction` antes de actuar sobre mensajes entrantes. |
| `message.delivery.updated` | Cambió el estado de entrega de un mensaje. |
| `conversation.handoff.updated` | Cambió una derivación de conversación. |
| `crm.opportunity.created` | Se creó una oportunidad de CRM. |
| `crm.opportunity.updated` | Cambió una oportunidad de CRM. |

Los eventos reales contienen metadatos, como IDs y cambios de estado, no cuerpos de conversaciones. Consulta contenido con la [API y sus permisos](/es/api-docs/webhooks). Se excluyen turnos internos de sistema/herramientas y mensajes de Playground.

## 3. Enviar un evento de prueba

Abre el endpoint guardado, selecciona **Enviar evento de prueba** (**Send test event**), elige un evento suscrito y revisa el contenido y el destino. Selecciona **Confirmar** una vez.

<img src="https://mintcdn.com/muhammadtest/QlQR9y4q6jBU1DlD/images/webhooks/test-preview.jpg?fit=max&auto=format&n=QlQR9y4q6jBU1DlD&q=85&s=ac4eb37df9fa454f2b01ea3671264cd7" alt="Confirmar el evento sintético y su destino" width="1512" height="828" data-path="images/webhooks/test-preview.jpg" />

Las pruebas usan la firma, cola persistente, tiempo límite y reintentos normales. Conservan `schemaVersion: "1.0"` y añaden `test: true`. Los IDs de recursos son sintéticos; no se crea ningún mensaje, conversación ni registro de CRM. Tu receptor sí recibe una solicitud HTTPS real: evita que los eventos de prueba ejecuten acciones de negocio.

Se requiere un endpoint activo y autorizado, y un trabajador listo. El límite es **una prueba por endpoint por minuto**. El dashboard utiliza una clave de idempotencia para evitar duplicar la creación de la misma solicitud. Espera el resultado en lugar de confirmar repetidamente.

## 4. Revisar entregas e intentos

En **Entregas** (**Deliveries**), selecciona la prueba. El detalle muestra ID de evento, ID de entrega, JSON, estado, siguiente reintento cuando corresponde e intentos individuales. Filtra por estado, evento, Prueba/Real e historial. El rango predeterminado es de siete días, con opciones hasta 30 días.

En este ejemplo, el intento 1 responde `503` intencionalmente. La entrega permanece **Pendiente** y Visito programa otro intento aproximadamente un minuto después. Cuando recibe `204`, pasa a **Entregado**. Ambos intentos comparten el mismo ID de evento.

<img src="https://mintcdn.com/muhammadtest/QlQR9y4q6jBU1DlD/images/webhooks/delivered.jpg?fit=max&auto=format&n=QlQR9y4q6jBU1DlD&q=85&s=56fd1d7d17ebf4a12366529689b8dae5" alt="Prueba entregada con un intento fallido seguido de éxito" width="1512" height="828" data-path="images/webhooks/delivered.jpg" />

Abre `http://127.0.0.1:8765` localmente para ver las verificaciones del receptor. Esta página de diagnóstico no se sirve a través del dominio público de ngrok.

<img src="https://mintcdn.com/muhammadtest/QlQR9y4q6jBU1DlD/images/webhooks/receiver.jpg?fit=max&auto=format&n=QlQR9y4q6jBU1DlD&q=85&s=b1b747a07ea28e2935d0d59c43e7fc53" alt="Receptor local con firmas válidas y respuestas 503 y 204" width="1512" height="772" data-path="images/webhooks/receiver.jpg" />

| Estado | Significado y siguiente paso |
| - | - |
| **Pendiente / Pending** | Espera el primer envío o un reintento. Revisa **Siguiente reintento**. |
| **Enviando / Sending** | Un trabajador tomó la entrega; puede haber una solicitud en curso. |
| **Entregado / Delivered** | El receptor respondió 2xx. Confirma aceptación, no la finalización de una acción posterior. |
| **Fallido / Failed** | Se agotaron los intentos automáticos. Corrige el receptor y usa **Reintentar entrega** si corresponde. |
| **Cancelado / Canceled** | La entrega se detuvo, por ejemplo por una suscripción o credencial inactiva. Reactivarla no reproduce este evento. |
| **Resultado desconocido / Outcome unknown** en un intento | La ejecución terminó sin confirmar el resultado. El receptor pudo aceptarlo; deduplica los intentos posteriores. |

Usa **Actualizar** para renovar la lista. Una entrega pendiente abierta se actualiza como máximo cada diez segundos; se detiene al finalizar, ocultar la página o cerrar el panel. Puedes copiar la URL para compartir el endpoint o la entrega con otro administrador autorizado.

Cada intento muestra inicio, duración, código HTTP cuando existe y motivo de fallo seguro. No se almacenan cuerpos de respuesta, encabezados arbitrarios, firmas ni secretos. Los registros antiguos pueden indicar que no se guardó el historial de intentos. Los intentos y la actividad se conservan 30 días; los metadatos de entregas terminadas, 30 días después de su finalización.

### Reintentos y duplicados

Hay un intento inicial y **seis reintentos**, con esperas de **1 minuto, 5 minutos, 30 minutos, 2 horas, 8 horas y 24 horas** después de fallos sucesivos. Cada intento tiene un **límite de diez segundos**. Las colas y la recuperación pueden añadir demora. No se siguen redirecciones.

La entrega es al menos una vez y no garantiza orden. Verifica cada solicitud y deduplica de forma persistente por `eventId`. **Reintentar entrega** solo está disponible para fallos terminales: inicia otro ciclo conservando el ID de evento y el historial previo. No evita la deduplicación del receptor.

El servicio limita los envíos simultáneos a diez en total y dos por espacio. Responde 2xx después de aceptar el evento de forma persistente; procesa el trabajo costoso por separado.

## Verificar firmas en tu aplicación

Lee estos encabezados:

```text theme={null}
X-Visito-Event-Id: <ID de evento>
X-Visito-Signature: t=<segundos Unix>,v1=<HMAC hexadecimal>
```

Calcula HMAC-SHA256 con el secreto del endpoint sobre `timestamp + "." + rawBody`. Compara en tiempo constante y rechaza diferencias de más de cinco minutos. Verifica antes de interpretar JSON, conserva los bytes exactos y sincroniza el reloj. Comprueba que el ID del encabezado coincida con el evento y deduplica de forma persistente.

No uses un parser que transforme el cuerpo antes de verificarlo. No autentiques solo por la URL de destino. La [referencia en inglés](/api-docs/webhooks#verify-signatures) incluye un verificador JavaScript; el ejemplo Python combina verificación y aceptación persistente.

## Administrar configuración y autorización

En **Configuración** (**Settings**) puedes cambiar nombre, destino, eventos y estado habilitado. **Actividad** (**Activity**) muestra el historial de cambios sin secretos.

<img src="https://mintcdn.com/muhammadtest/QlQR9y4q6jBU1DlD/images/webhooks/settings.jpg?fit=max&auto=format&n=QlQR9y4q6jBU1DlD&q=85&s=79b574cb60470c07e96439ac4b02c9d7" alt="Configuración del webhook y controles de administración" width="1512" height="828" data-path="images/webhooks/settings.jpg" />

| Acción | Consecuencia |
| - | - |
| Editar | Cambia el comportamiento futuro conservando el modo de autorización. Revisa el receptor al cambiar destino o eventos. |
| Deshabilitar | Detiene futuras entregas y cancela las que estén en cola. No puede retirar una solicitud en curso. |
| Habilitar de nuevo | Permite futuras entregas; no reproduce eventos cancelados. |
| Rotar secreto de firma | Muestra un secreto nuevo una vez. El anterior deja de usarse para futuros intentos; coordina la actualización del receptor. |
| Eliminar | Detiene futuras entregas. Una solicitud en curso puede terminar. Guarda antes los diagnósticos que necesites. |
| Transferir a administración del dashboard | Elimina explícitamente la dependencia de la credencial de API para futuras entregas; no reproduce cancelaciones. |

Las suscripciones **administradas desde el dashboard** pertenecen al espacio. La salida del administrador creador o la expiración de su sesión no detienen las entregas. Las administran los administradores actuales del espacio.

Las suscripciones **vinculadas a credenciales**, creadas mediante API, dependen de los permisos, vencimiento y revocación de su credencial. Los registros anteriores conservan este comportamiento. Los administradores pueden revisar y editar ambos tipos; una edición ordinaria desde el dashboard no cambia el modo.

**Transferir a administración del dashboard** solicita confirmar que una futura revocación de la credencial ya no detendrá las entregas. Conserva destino, secreto, IDs de evento e historial, y registra actor y fecha. No existe transferencia inversa en esta versión.

Los clientes de API siguen necesitando permisos de webhooks y de lectura de los eventos afectados para modificar suscripciones administradas desde el dashboard. No las vinculan silenciosamente a su credencial. Consulta el [contrato de API](/es/api-docs/webhooks).

## Solución de problemas

| Síntoma | Qué revisar |
| - | - |
| No puedes crear un endpoint | Rol de administrador, destino HTTPS público, eventos, configuración de firma y trabajador listo. |
| Prueba no disponible | Endpoint y autorización activos, espacio habilitado y trabajador listo. Respeta el límite de un minuto. |
| `receiver_http_error` | Respuesta distinta de 2xx. Revisa código HTTP, registros seguros, ruta y archivo del secreto. |
| Firma inválida | Secreto correcto, bytes originales, tolerancia y reloj. Actualiza el receptor después de una rotación. |
| Tiempo agotado o fallo de transporte | Receptor y túnel activos, DNS público, TLS válido, ruta correcta, sin redirecciones y respuesta en diez segundos. |
| `unsafe_destination` | Se requiere IPv4 pública. Usa el túnel HTTPS público sin eludir la protección de direcciones privadas. |
| Las pruebas funcionan, pero no hay eventos reales | Las pruebas no demuestran captura de productores. Revisa la activación y actividad compatible; Playground está excluido. |
| Evento duplicado | Es posible con entrega al menos una vez. Usa una restricción única por ID y procesamiento idempotente. |

## Terminar la prueba local

Deshabilita el webhook y confirma su estado **antes** de detener el receptor o ngrok. Así no queda un endpoint habilitado apuntando a una computadora desconectada. Conserva el historial para revisarlo. Detén ambos procesos con Ctrl+C y elimina el secreto local cuando ya no lo necesites.

<img src="https://mintcdn.com/muhammadtest/QlQR9y4q6jBU1DlD/images/webhooks/disabled.jpg?fit=max&auto=format&n=QlQR9y4q6jBU1DlD&q=85&s=f83b9c00ec7fd299d8557e153d67b4d1" alt="Endpoint de prueba deshabilitado con su entrega conservada" width="1512" height="828" data-path="images/webhooks/disabled.jpg" />

Esta prueba verificó un evento sintético firmado, un `503` controlado y recuperación automática a `204` con el mismo ID. Por sí sola no verifica captura de eventos reales, todos los casos de error ni rendimiento de producción del receptor.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.